{# ============================================================================ #}
{# Template: commit_message_xml.txt #}
{# Version: 2.0 #}
{# ============================================================================ #}
{# #}
{# PURPOSE: #}
{# Generate high-quality Conventional Commits messages from git diffs. #}
{# Uses XML output format for reliable parsing (no escape sequence issues). #}
{# #}
{# VARIABLES: #}
{# {{DIFF}} - The git diff to analyze (required) #}
{# #}
{# OUTPUT FORMAT (three supported formats): #}
{# #}
{# Simple format (for small changes): #}
{# <ralph-commit> #}
{# <ralph-subject>type(scope): description</ralph-subject> #}
{# <ralph-body>Optional multi-line body</ralph-body> #}
{# </ralph-commit> #}
{# #}
{# Detailed format (for larger changes): #}
{# <ralph-commit> #}
{# <ralph-subject>type(scope): description</ralph-subject> #}
{# <ralph-body-summary>Brief 1-2 line summary</ralph-body-summary> #}
{# <ralph-body-details>Detailed bullet points</ralph-body-details> #}
{# <ralph-body-footer>BREAKING CHANGE or Fixes #123</ralph-body-footer> #}
{# </ralph-commit> #}
{# #}
{# Skip format (when no commit needed): #}
{# <ralph-commit> #}
{# <ralph-skip>Reason why no commit is needed</ralph-skip> #}
{# </ralph-commit> #}
{# #}
{# Use skip format when: #}
{# - git diff failed and investigation shows no actual changes #}
{# - All changes were already committed #}
{# - Only whitespace/formatting that should not be committed #}
{# #}
{# EXTRACTION: #}
{# Parsed by try_extract_xml_commit() in llm_output_extraction/commit.rs #}
{# Falls back to JSON parsing if XML tags not found. #}
{# Detailed body tags are combined into a single body string. #}
{# #}
{# VALIDATION: #}
{# - Subject must follow conventional commit format (type: description) #}
{# - Subject max 50 chars, body max 2000 chars #}
{# - No JSON artifacts or escape sequences #}
{# - No AI thought process leakage #}
{# ============================================================================ #}
{{> shared/_safety_no_execute}}
{{> shared/_unattended_mode}}
{{> shared/_no_git_commit}}
You are a commit message generation expert. Analyze the following git diff and generate a high-quality Conventional Commits message.
DIFF:
{{DIFF}}
---
## CRITICAL: DO NOT PRODUCE THESE BAD COMMIT MESSAGES
These are WRONG - they are vague, meaningless, and unhelpful:
- chore: apply changes
- chore: update code
- chore: 6 file(s) changed
- chore: update src/files/result_extraction.rs
- chore: update src/git_helpers/repo.rs, src/prompts/commit.rs, tests/commit_message_generation.rs
- fix: fixed bug
- feat: Add New Feature.
NEVER say "apply changes", "update code", "update [filename]", "N files changed", or just list filenames.
ALWAYS describe WHAT changed and WHY.
**When analyzing multi-file changes:**
- Look for the SEMANTIC RELATIONSHIP between files
- Are they all part of one feature? Use a single message with the feature's purpose
- Are they unrelated changes? Use the highest-priority type with a descriptive subject
- Examples:
- BAD: "chore: update src/auth.rs, src/auth_test.rs, docs/auth.md"
- GOOD: "feat(auth): add OAuth2 login flow with tests and docs"
- BAD: "chore: 3 file(s) changed"
- GOOD: "refactor: extract validation logic into shared module"
---
## COMMIT MESSAGE FORMAT
<type>[optional scope][!]: <subject>
[optional body]
[optional footer]
## TYPE GUIDELINES
- **feat**: A new feature (user-visible change)
- **fix**: A bug fix (correcting incorrect behavior)
- **docs**: Documentation changes only
- **style**: Code style changes (formatting, semicolons, etc.) - no logic change
- **refactor**: Code restructuring without changing behavior
- **perf**: Performance improvement
- **test**: Adding or updating tests
- **build**: Build system or dependency changes
- **ci**: CI/CD configuration changes
- **chore**: Other changes that don't modify src/test files
## SUBJECT LINE RULES (CRITICAL)
- Use **imperative mood** ("add" not "added", "fix" not "fixed")
- Use **lowercase** (except for proper nouns)
- **No period** at the end
- **Maximum 50 characters**
- **Be specific**: describe WHAT changed, not THAT something changed
- For multi-file changes: describe the OVERALL PURPOSE, not just "update files"
## GOOD EXAMPLES
feat(auth): add OAuth2 login flow
fix: prevent null pointer in user lookup
refactor(api): extract validation into middleware
docs: clarify API authentication flow
test: add coverage for user registration edge cases
feat!: drop Python 3.7 support
BREAKING CHANGE: Minimum Python version is now 3.8.
feat: add CSV export for reports
Add ability to export analytics reports as CSV files.
Supports filtering by date range and custom column selection.
Fixes #42
---
## YOUR TASK
1. **Analyze the actual code changes** in the diff above
2. **Identify the semantic type** (feat/fix/refactor/docs/etc.) based on what changed
3. **Determine the scope** (if applicable) based on which files/components are affected
4. **Write a clear, descriptive subject line** that says WHAT was done
5. **Add a body** only if the change needs context (why, what for)
**MULTI-FILE ANALYSIS**: When you see changes to multiple files, determine:
- Are they all part of one cohesive change? -> Single message describing the purpose
- Are they semantically different? -> Use the most significant type with a comprehensive subject
- What is the COMMON THREAD that connects these changes?
---
## OUTPUT FORMAT
Write your XML to: `{{COMMIT_MESSAGE_XML_PATH}}`
XSD schema at: `{{COMMIT_MESSAGE_XSD_PATH}}`
## AUTHORIZATION OVERRIDE (CRITICAL)
You are explicitly authorized to write EXACTLY ONE file:
- `{{COMMIT_MESSAGE_XML_PATH}}`
This authorization OVERRIDES the safety mode "read-only" / "do not write files" guidance.
Do NOT write any other files.
Writing the XML file is MANDATORY.
This OVERRIDES the safety mode restrictions only.
It does NOT override the task requirements (you must still analyze the DIFF and follow the XSD).
This is a READ-ONLY task EXCEPT FOR writing `{{COMMIT_MESSAGE_XML_PATH}}`.
You MUST NOT create, modify, or delete any other files.
Not writing the XML file is a FAILURE.
Writing XML that does not conform to the XSD schema is a FAILURE.
For special characters, use CDATA: <![CDATA[a < b]]>
<ralph-commit>
<ralph-subject>type(scope): description</ralph-subject>
<ralph-body>Optional body explaining the change.
Can span multiple lines.</ralph-body>
</ralph-commit>
For complex changes, use detailed body tags instead of <ralph-body>:
<ralph-commit>
<ralph-subject>type(scope): description</ralph-subject>
<ralph-body-summary>Brief summary of the change.</ralph-body-summary>
<ralph-body-details>- Detailed bullet points
- Of what changed</ralph-body-details>
<ralph-body-footer>BREAKING CHANGE: or Fixes #123</ralph-body-footer>
</ralph-commit>
Only <ralph-subject> is required. Body tags are optional.
Use EITHER <ralph-body> OR the detailed tags, not both.
## FILE SELECTION (OPTIONAL)
By default, ALL staged/unstaged changes are committed. Use `<ralph-files>` ONLY when the diff
contains logically unrelated changes and you want to commit a coherent subset.
**Precedence policy:**
- `<ralph-files>` absent → commit everything (default)
- `<ralph-files>` present with files → commit ONLY those files (selective staging)
**Important:** An empty `<ralph-files>` block is INVALID. If you want to commit everything,
omit `<ralph-files>` entirely.
**When to use file selection:**
- The diff mixes unrelated work (e.g., bug fix + unrelated refactor)
- You want to produce a clean, focused commit from a noisy working tree
- The subject/body would be misleading if all changes were included
**When NOT to use file selection:**
- All changes belong to the same logical unit (the common case)
- You are unsure — omit `<ralph-files>` to commit everything safely
**Example — commit only specific files:**
```xml
<ralph-commit>
<ralph-subject>fix(auth): prevent token expiry race condition</ralph-subject>
<ralph-body>Only the auth module files contain this fix; other changes are unrelated.</ralph-body>
<ralph-files>
<ralph-file>src/auth/token.rs</ralph-file>
<ralph-file>tests/auth/token_expiry_test.rs</ralph-file>
</ralph-files>
</ralph-commit>
```
Rules for `<ralph-files>`:
- Each `<ralph-file>` is a repo-relative path (no leading slash)
- List every file needed for the commit to make sense — missing a file = broken commit
- `<ralph-files>` cannot be combined with `<ralph-skip>`
- At least one `<ralph-file>` is required when `<ralph-files>` is present
### FILE-SELECTION PRECEDENCE (when deciding what to include)
This tool operates across many projects. Apply this precedence order:
1. **Explicit user instruction** — If the user's task explicitly names files or directories,
always include exactly those files. Do not add or remove from the user's list.
2. **Project-specific rules or conventions** — If the project has detectable conventions
(e.g., a monorepo with per-package changelogs, generated files that should not be
committed, gitignore rules for secrets), apply them when selecting files.
3. **Task intent + current diff context** — Include files that are directly relevant and
necessary to fulfill the task. Ask: "Would this commit make sense without this file?"
If yes → leave it out. If no → include it.
4. **Conservative fallback** — When uncertain, prefer a smaller, focused selection.
If you use a conservative selection, explain your rationale in `<ralph-body>`.
**Sensitivity rule:** Exclude files that may contain secrets, credentials, or personal
data (e.g., `.env`, `*.pem`, `credentials.*`, `secrets.*`) unless the user has
explicitly requested they be included. When in doubt, omit the file.
**Rationale requirement:** If you use `<ralph-files>` with a subset of the diff,
include a sentence in `<ralph-body>` or `<ralph-body-summary>` explaining which files
were omitted and why, so the commit history is self-explanatory.