mirror of
https://github.com/github/spec-kit.git
synced 2026-08-03 06:26:30 +08:00
* feat(git-extension): add configurable Conventional Commit support Adds a commit_style option (fixed | conventional) to the git extension's auto-commit config. When set to conventional, the speckit.git.commit hook instructs the agent to generate a Conventional Commit message from the diff and pass it to auto-commit.sh / auto-commit.ps1 as an explicit argument. If no message is supplied in conventional mode, the scripts fail loudly (stderr + exit 1) instead of silently falling back to the fixed message, but still short-circuit cleanly when there are no changes to commit. - extensions/git/config-template.yml, git-config.yml: new commit_style: fixed (default) / conventional option. - extensions/git/scripts/bash/auto-commit.sh: optional [generated_message] arg, commit_style parsing, conventional-mode enforcement. - extensions/git/scripts/powershell/auto-commit.ps1: mirrored PowerShell implementation. - extensions/git/commands/speckit.git.commit.md: documents commit message styles and updated execution/config guidance. - extensions/git/README.md: documents the new option. - tests/extensions/git/test_git_extension.py: regression tests for fixed default, conventional success, conventional missing-message failure, and no-changes short-circuit (bash + PowerShell). Fixes #3390 Assisted-by: GitHub Copilot (model: claude-sonnet-5, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(tests): check combined stdout+stderr for conventional commit_style failure test Write-Warning output stream placement is not deterministic across pwsh versions/platforms (observed failing on macOS CI). Match the existing pattern used elsewhere in this file (e.g. test_not_a_repo_still_detected_with_autocrlf) by asserting against the combined stdout+stderr instead of stderr alone. Assisted-by: GitHub Copilot (model: claude-sonnet-5, autonomous) * fix(git-extension): strip YAML inline comments from commit_style value Copilot review feedback on PR #3413 identified that commit_style parsing didn't strip trailing YAML inline comments (e.g. "commit_style: conventional # team standard"), causing the value to retain a trailing comment fragment and silently skip conventional-mode enforcement. - bash: fix the inline-comment strip regex to use a proper {1,} interval so multiple spaces before '#' are consumed together with the comment, preventing a stray trailing quote character from surviving quote-strip when the value is quoted (e.g. commit_style: "conventional" # x). - powershell: already handled this correctly via \s+#.*$ + Trim(); no behavior change needed there. - tests: add regression coverage for commit_style values with trailing inline comments (bash + pwsh), and a pwsh regression test for the no-changes short-circuit ordering, per additional Copilot suggestion. Assisted-by: GitHub Copilot (model: claude-sonnet-5, autonomous) * fix(git-extension): validate commit_style and reword abort message per review Address Copilot review feedback on PR #3413: - Validate commit_style against the documented fixed/conventional values; an unrecognized value now warns and falls back to fixed instead of silently mis-parsing. - Reword the conventional-mode-without-generated-message message from 'skipped auto-commit' to 'aborting auto-commit' since the script exits 1 (a failure, not a skip), and include the actionable remediation. - Add regression tests (bash + pwsh) covering the unknown commit_style fallback. Assisted-by: GitHub Copilot (model: claude-sonnet-5, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(git-extension): limit commit_style parsing to first match in config Address Copilot review feedback on PR #3413: grep '^commit_style:' without -m1 could concatenate values if a config file accidentally contains multiple commit_style lines (e.g. from a bad merge/manual edit), causing an unexpected fallback to 'fixed'. Limit to the first match and add a regression test covering duplicate commit_style lines. Assisted-by: GitHub Copilot (model: claude-sonnet-5, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(git-extension): avoid shell interpolation of generated commit messages - Remove r -d '[:space:]' from commit_style parsing in auto-commit.sh: it stripped ALL whitespace (not just leading/trailing), so commit_style: con ventional was silently normalized to conventional instead of being rejected as unknown (PowerShell version already rejected it correctly). - Add a file-based message-passing channel to both auto-commit scripts: --message-file <path> (bash) / -MessageFile <path> (PowerShell). Agent-generated commit messages may contain quotes, $(...), or backticks; passing them as a shell argument risked command injection if ever inlined into a shell command string. The new flag reads the message from a file instead, so untrusted content never touches a shell command line. The raw positional-argument form is kept for backward compatibility. - Update speckit.git.commit.md to instruct the agent to write the generated message to a temp file (via its file-editing tool) and pass the file path, explicitly warning against inlining the message into a shell command string. - Add test coverage: explicit commit_style: fixed (previously only the absent-key default was tested), --message-file/-MessageFile success path (including injection-shaped content), and missing-file error path, for both bash and PowerShell suites. Assisted-by: GitHub Copilot (model: claude-sonnet-5, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(git-extension): exclude --message-file transport file from staging The temp file passed via --message-file / -MessageFile was read but left in the worktree. If written inside the project (as an agent's file-editing tool would naturally do), git add . staged it into the commit, and its mere presence as an untracked file could also defeat the no-changes short-circuit, causing a spurious commit containing only that file. Remove the file immediately after its content is captured, before the change-detection check and before staging. Add bash + pwsh regression tests covering both scenarios. Assisted-by: GitHub Copilot (model: claude-sonnet-5, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
64 lines
3.6 KiB
Markdown
64 lines
3.6 KiB
Markdown
---
|
|
description: "Auto-commit changes after a Spec Kit command completes"
|
|
---
|
|
|
|
# Auto-Commit Changes
|
|
|
|
Automatically stage and commit all changes after a Spec Kit command completes.
|
|
|
|
## Behavior
|
|
|
|
This command is invoked as a hook after (or before) core commands. It:
|
|
|
|
1. Determines the event name from the hook context (e.g., if invoked as an `after_specify` hook, the event is `after_specify`; if `before_plan`, the event is `before_plan`)
|
|
2. Checks `.specify/extensions/git/git-config.yml` for the `auto_commit` section
|
|
3. Looks up the specific event key to see if auto-commit is enabled
|
|
4. Falls back to `auto_commit.default` if no event-specific key exists
|
|
5. Determines the commit message based on `commit_style` (see below)
|
|
6. If enabled and there are uncommitted changes, runs `git add .` + `git commit`
|
|
|
|
## Commit Message Styles
|
|
|
|
Controlled by the `commit_style` key in `.specify/extensions/git/git-config.yml`:
|
|
|
|
- **`fixed`** (default): use the per-command `message` if configured, otherwise a generic `[Spec Kit] Auto-commit <phase> <command>` message.
|
|
- **`conventional`**: inspect the actual changes (`git diff` / `git status`) since the last commit and generate a single-line [Conventional Commit](https://www.conventionalcommits.org/) message (`type(scope): subject`, e.g. `feat: add OAuth specification` or `docs: update implementation plan`) that accurately summarizes the change. Write this message to a temporary file and pass the file's path to the script (see Execution below). The configured `message` values are ignored in this mode.
|
|
|
|
## Execution
|
|
|
|
Determine the event name from the hook that triggered this command, then run the script:
|
|
|
|
- **Bash**: `.specify/extensions/git/scripts/bash/auto-commit.sh <event_name> [--message-file <path>]`
|
|
- **PowerShell**: `.specify/extensions/git/scripts/powershell/auto-commit.ps1 <event_name> [-MessageFile <path>]`
|
|
|
|
Replace `<event_name>` with the actual hook event (e.g., `after_specify`, `before_plan`, `after_implement`). Only pass a generated message when `commit_style: conventional` is configured — first check `.specify/extensions/git/git-config.yml` for the value of `commit_style`:
|
|
|
|
- If `conventional`: inspect the diff and generate a Conventional Commit message. **Do not interpolate the generated message directly into a shell command string** — its content is derived from repository changes and may contain characters (quotes, `$(...)`, backticks) that a shell would execute or that would break command quoting. Instead, write the message to a temporary file using your file-editing tool (not a shell `echo`/`printf`), then pass that file's path via `--message-file <path>` (Bash) or `-MessageFile <path>` (PowerShell).
|
|
- If `fixed` or absent: run the script with just `<event_name>`; it uses the configured/static message.
|
|
|
|
## Configuration
|
|
|
|
In `.specify/extensions/git/git-config.yml`:
|
|
|
|
```yaml
|
|
# "fixed" (default) uses the messages below; "conventional" asks the agent
|
|
# to generate a Conventional Commit message from the diff instead.
|
|
commit_style: fixed
|
|
|
|
auto_commit:
|
|
default: false # Global toggle — set true to enable for all commands
|
|
after_specify:
|
|
enabled: true # Override per-command
|
|
message: "[Spec Kit] Add specification"
|
|
after_plan:
|
|
enabled: false
|
|
message: "[Spec Kit] Add implementation plan"
|
|
```
|
|
|
|
## Graceful Degradation
|
|
|
|
- If Git is not available or the current directory is not a repository: skips with a warning
|
|
- If no config file exists: skips (disabled by default)
|
|
- If no changes to commit: skips with a message
|
|
- If `commit_style: conventional` is set and no generated message was supplied: fails with a clear error instead of silently falling back to the fixed message format
|