* fix: suppress CRLF warnings in auto-commit.ps1 (#2253)
Replace 2> with 2>&1 redirection and assignment to properly
suppress stderr output including CRLF warnings on Windows. Exit code
logic preserved for change detection.
Fixes#2253
* fix: use SilentlyContinue for CRLF stderr handling, add tests
The 2>&1 approach still raises terminating errors under
$ErrorActionPreference='Stop'. Instead, temporarily set
SilentlyContinue around all native git calls that may emit
CRLF warnings to stderr (rev-parse, diff, ls-files, add, commit).
Adds 5 pytest tests (TestAutoCommitPowerShellCRLF) that set
core.autocrlf=true with LF-ending files. On Windows runners
this triggers actual CRLF warnings; on other platforms the tests
pass trivially.
Fixes#2253
* refactor: address Copilot review feedback
- Use 'Continue' instead of 'SilentlyContinue' so error output is
still captured in $out for diagnostics on real git failures.
- Wrap all three EAP save/restore blocks in try/finally to guarantee
restoration even on unexpected exceptions.
- Fix CRLF test to commit a tracked LF file first, then modify it,
so git diff --quiet HEAD actually inspects the tracked change and
triggers the CRLF warning on Windows.
* test: assert CRLF warning fires on Windows
On Windows, probe git diff stderr before running the script to verify
the test setup actually produces the expected CRLF warning. This
makes the regression test deterministic on the Windows runner. On
non-Windows the probe is skipped (warnings don't fire there).
---------
Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com>
* fix: allow Claude to chain skills for hook execution (#2178)
- Set disable-model-invocation to false so Claude can invoke extension
skills (e.g. speckit-git-feature) from within workflow skills
- Inject dot-to-hyphen normalization note into Claude SKILL.md hook
sections so the model maps extension.yml command names to skill names
- Replace Unicode checkmark with ASCII [OK] in auto-commit scripts to
fix PowerShell encoding errors on Windows
- Move Claude-specific frontmatter injection to ClaudeIntegration via
post_process_skill_content() hook on SkillsIntegration, wired through
presets and extensions managers
- Add positive and negative tests for all changes
Fixes#2178
* refactor: address PR review feedback
- Preserve line-ending style (CRLF/LF) in _inject_hook_command_note
instead of always inserting \n, matching the convention used by other
injection helpers in the same module.
- Extract duplicated _post_process_skill() from extensions.py and
presets.py into a shared post_process_skill() function in agents.py.
Both modules now import and call the shared helper.
* fix: match full hook instruction line in regex
The regex in _inject_hook_command_note only matched lines ending
immediately after 'output the following', but the actual template
lines continue with 'based on its `optional` flag:'. Use [^\r\n]*
to capture the rest of the line before the EOL.
* refactor: use integration object directly for post_process_skill_content
Instead of a free function in agents.py that re-resolves the
integration by key, callers in extensions.py and presets.py now
resolve the integration once via get_integration() and call
integration.post_process_skill_content() directly. The base
identity method lives on SkillsIntegration.
* feat: add git extension with hooks on all core commands
- Create extensions/git/ with 5 commands: initialize, feature,
validate, remote, commit
- 18 hooks covering before/after for all 9 core commands
- Scripts: create-new-feature, initialize-repo, auto-commit,
git-common (bash + powershell)
- Configurable: branch_numbering, init_commit_message,
per-command auto-commit with custom messages
- Add hooks to analyze, checklist, clarify, constitution,
taskstoissues command templates
- Allow hooks-only extensions (no commands required)
- Bundle extension in wheel via pyproject.toml force-include
- Resolve bundled extensions locally before catalog lookup
- Remove planned-but-unimplemented before/after_commit hook refs
- Update extension docs (API ref, dev guide, user guide)
- 37 new tests covering manifest, install, all scripts (bash+pwsh),
config reading, graceful degradation
Stage 1: opt-in via 'specify extension add git'. No auto-install,
no changes to specify.md or core git init code.
Refs: #841, #1382, #1066, #1791, #1191
* fix: set git identity env vars in extension tests for CI runners
* fix: address PR review comments
- Fix commands property KeyError for hooks-only extensions
- Fix has_git() operator precedence in git-common.sh
- Align default commit message to '[Spec Kit] Initial commit' across
config-template, extension.yml defaults, and both init scripts
- Update README to reflect all 5 commands and 18 hooks
* fix: address second round of PR review comments
- Add type validation for provides.commands (must be list) and hooks
(must be dict) in manifest _validate()
- Tighten malformed timestamp detection in git-common.sh to catch
7-digit dates without trailing slug (e.g. 2026031-143022)
- Pass REPO_ROOT to has_git/Test-HasGit in create-new-feature scripts
- Fix initialize command docs: surface errors on git failures, only
skip when git is not installed
- Fix commit command docs: 'skips with a warning' not 'silently'
- Add tests for commands:null and hooks:list rejection
* fix: address third round of PR review comments
- Remove scripts frontmatter from command files (CommandRegistrar
rewrites ../../scripts/ to .specify/scripts/ which points at core
scripts, not extension scripts)
- Update speckit.git.commit command to derive event name from hook
context rather than using a static example
- Clarify that hook argument passthrough works via AI agent context
(the agent carries conversation state including user's original
feature description)
* fix: address fourth round of PR review comments
- Validate extension_id against ^[a-z0-9-]+$ in _locate_bundled_extension
to prevent path traversal (security fix)
- Move defaults under config.defaults in extension.yml to match
ConfigManager._get_extension_defaults() schema
- Ship git-config.yml in extension directory so it's copied during
install (provides.config template isn't materialized by ExtensionManager)
- Condition handling in hook templates: intentionally matches existing
pattern from specify/plan/tasks/implement templates (not a new issue)
* fix: add --allow-empty to git commit in initialize-repo scripts
Ensures git init succeeds even on empty repos where nothing has been
staged yet.
* fix: resolve display names to bundled extensions before catalog download
When 'specify extension add "Git Branching Workflow"' is used with a
display name instead of the ID, the catalog resolver now runs first to
map the name to an ID, then checks bundled extensions again with the
resolved ID before falling back to network download.
Also noted: EXECUTE_COMMAND_INVOCATION and condition handling match the
existing pattern in specify/plan/tasks/implement templates (pre-existing,
not introduced by this PR).
* fix: handle before_/after_ prefixes in auto-commit message derivation
- Strip both before_ and after_ prefixes when deriving command name
(fixes misleading 'Auto-commit after before_plan' messages)
- Include phase (before/after) in default commit messages
- Clarify README config example is an override, not default behavior
* fix: use portable grep -qw for word boundary in create-new-feature.sh
BSD grep (macOS) doesn't support \b as a word boundary. Replace with
grep -qw which is POSIX-portable.
* fix: validate hook values, numeric --number, and PS warning routing
- Validate each hook value is a dict with a 'command' field during
manifest _validate() (prevents crash at install time)
- Validate --number is a non-negative integer in bash create-new-feature
(clear error instead of cryptic shell arithmetic failure)
- Route PowerShell no-git warning to stderr in JSON mode so stdout
stays valid JSON
---------
Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com>