mirror of
https://github.com/github/spec-kit.git
synced 2026-07-03 20:36:23 +08:00
14da893e4f967ffc55a643b5eeed1acd03466fba
148 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
14da893e4f |
fix(copilot): resolve active spec template (#2765)
Co-authored-by: root <kinsonnee@gmail.com> |
||
|
|
39925ac084 |
fix: add missing agent-context extension entries to Cline _expected_files (#2797)
TestClineIntegration._expected_files() overrides the base-class version but was not updated when the bundled agent-context extension files were added to test_integration_base_markdown.py, causing test_complete_file_inventory_sh and test_complete_file_inventory_ps to fail. Fixes #2796 |
||
|
|
44aac9f6e4 |
feat: add native Cline integration (#2508)
* test: strip ansi to make asserts work * feat: add native Cline integration |
||
|
|
089feca75f |
fix: move URL install confirmation prompt before spinner (#2783) (#2784)
* fix: move URL install confirmation prompt before spinner (#2783) The typer.confirm() prompt inside console.status() was overwritten by Rich's spinner animation, making extension add --from <url> appear hung. Move URL validation and the default-deny confirmation prompt before the spinner block so the user can see and respond to the [y/N] prompt. * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * fix: guard prompt with not dev, escape from_url in Rich markup Address PR review feedback: - Gate URL confirmation prompt on 'not dev' so --dev + --from does not show a confusing prompt for a URL path that will be ignored. - Escape from_url with rich.markup.escape() in both the warning panel and the download message to prevent markup injection via crafted URLs. * fix: remove unused import, reuse safe_url, add regression tests Address second round of PR review: - Remove unused urllib.request import from URL install path - Remove redundant re-import of rich.markup.escape; reuse safe_url computed before the spinner for download and error messages - Add test_add_from_url_prompts_before_spinner: asserts typer.confirm fires before console.status spinner to prevent #2783 regression - Add test_add_from_url_cancel_exits_cleanly: asserts declining the prompt exits with code 0 and prints Cancelled --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
50da3a0f77 |
Extract agent context updates into bundled agent-context extension (#2546)
* Initial plan * Extract agent context updates into bundled agent-context extension * Potential fix for pull request finding 'Unused import' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * Potential fix for pull request finding 'Unused import' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * fix: address review comments on agent-context extension - bash: parse init-options.json with a single python3 invocation instead of three separate read_json_field calls, for parity with the PowerShell ConvertFrom-Json approach and to avoid divergent error semantics - bash: use parameter expansion to strip PROJECT_ROOT prefix from plan path instead of sed interpolation, avoiding special-character fragility - powershell: limit Get-ChildItem to -Depth 1 so plan.md discovery matches the bash glob specs/*/plan.md (one level deep) — fixes cross-platform inconsistency with nested plan.md files - powershell: replace Substring+Length relative-path with [System.IO.Path]::GetRelativePath for robustness across case/PSDrive differences - __init__.py: move agent-context extension install to after save_init_options so init-options.json is present when hooks run - __init__.py: seed context_markers in init-options only when context_file is truthy; avoids noise for integrations without a context file - integrations/base.py: narrow blanket except Exception in _resolve_context_markers to ImportError / (OSError, ValueError) so unexpected bugs surface instead of being silently swallowed * fix: gate context_markers in _update_init_options_for_integration on context_file Apply the same gating logic used during `specify init`: only write context_markers to init-options.json when the integration actually has a context_file set. When switching to an integration without a context file the stale markers are removed, keeping the two init paths consistent. * fix: move context_file/context_markers from init-options.json to agent-context extension config * Potential fix for pull request finding 'Unused global variable' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * fix: clarify local import comment in agents.py * Fix remaining agent-context review findings * Fix follow-up agent-context review issues * Address review feedback: narrow except, improve PyYAML messaging, surface config-written note * Fix double-space in PyYAML install hint message * Potential fix for pull request finding 'Empty except' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * Potential fix for pull request finding 'Empty except' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * Address latest agent-context review feedback * Harden bash config parse output handling * Clarify ImportError-only fallback comment * Apply review feedback: drop dead try/except, guard ext-config creation, explicit ConvertFrom-Yaml check * Remove redundant $Options = $null in PS1 catch block * Add constitution directives, deprecation warning, agent-context auto-install, and init flow fix - Add constitution-loading directive to specify, clarify, tasks, checklist, taskstoissues commands - Add deprecation warning (v0.12.0) in upsert_context_section() - Auto-install agent-context extension during specify init - Move context_file from init-options.json to agent-context extension config - Add tests: deprecation warning, corrupt config, constitution directives - Update file inventories across all integration tests * Address review: fix init ordering, test coverage, and hermes inventory - Move agent-context extension install after init-options.json is saved so skill registration can read ai_skills + integration key - Write extension config after install (avoids template overwriting context_file) - Fix test_defaults_when_markers_field_missing to truly test missing markers key - Update hermes tests to allow extension-installed agent-context skill * Address review: chmod ordering, preserve markers, PS1 Python check, YAML key order - Move ensure_executable_scripts after agent-context extension install so extension scripts get execute bits set - Use preserve_markers=True on reinit to keep user-customized markers - Add Python 3 version check in PowerShell fallback (matching bash behavior) - Add sort_keys=False to yaml.safe_dump for stable config output * Address review: path traversal guards and docstring fix - Reject absolute paths and '..' segments in context_file in both bash and PowerShell scripts to prevent writes outside the project root - Fix docstring in _update_init_options_for_integration to accurately describe marker preservation behavior * Address review: strict enabled check, docstring, segment-level path traversal - Use 'is not False' for enabled check so only literal False disables - Update upsert_context_section docstring to mention disabled-extension return - Fix path traversal guards to check actual path segments, not substrings (allows filenames like 'notes..md' while rejecting '../' traversal) * Address review: UnicodeError handling, missing extension warning - Add UnicodeError to exception tuples in _load_agent_context_config and _resolve_context_markers so garbled UTF-8 config files fall back to defaults - Emit error (with reinstall command) instead of silent skip when bundled agent-context extension is not found during init * Address review: bash backslash traversal guard, wheel packaging - Reject backslash separators and Windows drive-letter paths in bash context_file validation (prevents traversal on Git-Bash/Windows) - Add extensions/agent-context to pyproject.toml force-include so the bundled extension is included in wheel builds * Address review: write extension config before init-options.json - Reorder writes in _update_init_options_for_integration so the agent-context extension config is updated first; if it fails, init-options.json remains consistent with the previous state --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> |
||
|
|
cc3d828227 |
Add support for SPECKIT_WORKFLOW_RUN_ID override (#2742)
* Initial plan * feat: support SPECKIT_WORKFLOW_RUN_ID override * docs: clarify run_id env var precedence wording --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> |
||
|
|
b4e5a1c3be |
feat: support SPECKIT_INTEGRATION_<KEY>_EXECUTABLE env var (#2743)
* Initial plan * feat: support SPECKIT_INTEGRATION_<KEY>_EXECUTABLE env var override Adds `IntegrationBase._resolve_executable()` which reads `SPECKIT_INTEGRATION_<KEY>_EXECUTABLE` (hyphens→underscores, uppercased) and falls back to `self.key` when unset or whitespace-only. All concrete `build_exec_args()` implementations now call `self._resolve_executable()` instead of hard-coding `self.key` or `"agy"` as the first argv token: - MarkdownIntegration, TomlIntegration, SkillsIntegration (base classes) - CodexIntegration, DevinIntegration, OpencodeIntegration, HermesIntegration, AgyIntegration - CopilotIntegration (overrides `_resolve_executable()` to fall back to the platform-specific `_copilot_executable()` default; also updates `dispatch_command()` to use `_resolve_executable()`) Tests added to tests/integrations/test_extra_args.py covering: - default (unset) falls back to key - env var replaces first argv token - whitespace-only env var is a no-op - key hyphen→underscore normalisation - cross-integration scoping (wrong key ignored) - all override integrations (Codex, Devin, Opencode, Copilot) - Copilot dispatch_command path - EXECUTABLE and EXTRA_ARGS can be set simultaneously See issue #2596." * fix: complete docstring sentence in _resolve_executable * test: generalize extra-args test comments for override coverage * Fix stale Codex executable comment --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> |
||
|
|
f50839a928 |
feat(integrations): support SPECIFY_<KEY>_EXTRA_ARGS env var for agent subprocess flags (#2596)
* feat(integrations): support SPECIFY_<KEY>_EXTRA_ARGS env var for agent subprocess flags Read a per-integration env var (SPECIFY_<KEY>_EXTRA_ARGS) inside `SkillsIntegration.build_exec_args`, `MarkdownIntegration.build_exec_args`, and `TomlIntegration.build_exec_args` and append the parsed flags to the spawned agent's argv, gated per integration key. Operators can now opt into extra CLI flags (e.g. `SPECIFY_CLAUDE_EXTRA_ARGS=--dangerously-skip-permissions`) without modifying any SKILL or workflow YAML. Useful in CI / non-interactive contexts where the spawned `<agent> -p ...` would otherwise hang on an internal permission or input prompt invisible to the parent `specify workflow run` process. Key normalization: `kiro-cli` → `SPECIFY_KIRO_CLI_EXTRA_ARGS` (hyphen replaced with underscore, then uppercased). Default (env var unset or whitespace-only) is byte-identical to previous behaviour. Extra args are inserted between `-p prompt` and the model / output-format flags so they cannot clobber canonical Spec Kit args. Implementation: a single helper `IntegrationBase._apply_extra_args_env_var` encapsulates the env-var read + shlex parsing; each of the three concrete `build_exec_args` implementations calls it. Closes #2595 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(integrations): wire SPECIFY_<KEY>_EXTRA_ARGS into Codex/Devin/Opencode/Copilot Four integrations override `build_exec_args` and were silently ignoring the env-var hook introduced in the previous commit: - CodexIntegration (`codex exec ...`) - DevinIntegration (`devin -p ...`) - OpencodeIntegration (`opencode run ...`) - CopilotIntegration (`copilot -p ...`) Each now calls `self._apply_extra_args_env_var(args)` between the base argv and the canonical Spec Kit flags (matching the placement in `MarkdownIntegration`, `TomlIntegration`, and `SkillsIntegration`), so operator-injected flags cannot clobber `--model` / `--output-format` / `--json`. Adds 4 parameterized override-integration tests locking the wiring per agent. Also cleans up an inline `__import__("os").environ` in the fixture to a top-of-file `import os`. Drive-by typing fix: guard `self.registrar_config.get(...)` in `CopilotIntegration.add_commands` against the `None` case, matching the pattern already used in `base.py` for the same access. Addresses Copilot review on #2596. * fix(integrations): apply Opencode extra-args before prompt-derived --command When the Opencode prompt starts with `/`, `build_exec_args` injects `--command <X>` derived from the prompt. The previous placement of `self._apply_extra_args_env_var(args)` appended operator-injected args AFTER that `--command`, so a user setting `SPECIFY_OPENCODE_EXTRA_ARGS="--command override"` could redirect the command under typical last-wins repeated-flag CLI semantics. Move the hook to immediately after `args = [self.key, "run"]`, before the prompt-parsing block. The operator's `--command override` (if any) now precedes the Spec Kit-derived `--command speckit`, so the canonical choice wins. Adds `test_opencode_extra_args_cannot_clobber_prompt_derived_command` locking the ordering. Also corrects the module docstring to describe the hook as living in `IntegrationBase` (not `SkillsIntegration`) and to acknowledge that this file covers Codex/Devin/Opencode/Copilot in addition to SkillsIntegration stubs. Addresses Copilot review on #2596. * fix(integrations): honour SPECIFY_COPILOT_EXTRA_ARGS in dispatch_command `CopilotIntegration` is the only integration that overrides `dispatch_command` — it builds `cli_args` inline rather than going through `build_exec_args`. The previous commit wired `_apply_extra_args_env_var` into `build_exec_args` but workflow execution calls `dispatch_command`, so `SPECIFY_COPILOT_EXTRA_ARGS` was silently ignored at runtime. Add the hook in `dispatch_command` immediately after `cli_args = ["copilot", "-p", prompt]`, mirroring the placement in `build_exec_args` (between `-p prompt` and the canonical `--agent` / `--yolo` / `--model` / `--output-format` flags). `IntegrationBase.dispatch_command` already delegates to `build_exec_args`, so Codex, Devin, and Opencode continue to honour their respective env vars through inheritance — no further changes needed for them. Adds two end-to-end tests that monkeypatch `subprocess.run` and assert the env-var args reach the executed argv: - `test_copilot_dispatch_command_includes_extra_args` locks the bypass fix on the overridden path. - `test_codex_dispatch_command_includes_extra_args` locks the inherited-base-dispatch path for the other three integrations. Addresses Copilot review on #2596. * refactor(integrations): rename env var to SPECIFY_INTEGRATION_<KEY>_EXTRA_ARGS Per maintainer request: scope the operator-injected env var to the integration subsystem by prepending `INTEGRATION_` to the key segment, so it does not collide with other Spec Kit env-var namespaces. Renames everywhere it appears: - Helper `IntegrationBase._apply_extra_args_env_var` env_name format and docstring (`base.py`). - Inline comment in `CopilotIntegration.dispatch_command`. - All `monkeypatch.setenv(...)` calls, docstrings, and the autouse fixture's scope filter in `tests/integrations/test_extra_args.py`. No behaviour change beyond the variable name. Default (env var unset) still byte-identical to before this PR. Addresses review on #2596. * fix(integrations): raise actionable error on malformed EXTRA_ARGS quoting Wrap `shlex.split` in `_apply_extra_args_env_var` so an unmatched quote in `SPECIFY_INTEGRATION_<KEY>_EXTRA_ARGS` surfaces a clear `ValueError` naming the offending env var and showing the invalid value, instead of crashing workflow dispatch with a bare shlex traceback. Adds a new test locking the actionable error path. Addresses Copilot review feedback on #2596. * test(integrations): use `_copilot_executable()` in Copilot extra-args test `test_copilot_integration_honours_extra_args` hardcoded `"copilot"` in the expected argv, but `CopilotIntegration.build_exec_args` calls `_copilot_executable()` which returns `"copilot.cmd"` on Windows (`os.name == "nt"`). The test passed on Linux/macOS and failed on all three Windows-latest matrix entries. Resolve by importing `_copilot_executable` alongside `CopilotIntegration` and using it as the first expected argv token. The companion `test_copilot_dispatch_command_includes_extra_args` already uses `index()` lookups rather than full-argv equality so it was unaffected. * fix(integrations): couple Codex executable to self.key + cover base classes Two Copilot findings on the latest pass: 1. `CodexIntegration.build_exec_args` hardcoded the executable name as the literal `"codex"` while the env-var lookup derives from `self.key`. The two should stay coupled (matching Devin/Opencode, which both use `self.key` already). Replace the literal with `self.key` so the argv and env-var scoping cannot drift. 2. `tests/integrations/test_extra_args.py` covered the `SkillsIntegration` mechanism (via stubs near the top) and the four `build_exec_args` overrides (Codex/Devin/Opencode/Copilot) end-to-end, but did not exercise the `MarkdownIntegration` or `TomlIntegration` base implementations directly. Add bare `_MarkdownAgentStub` and `_TomlAgentStub` test stubs and a test each — the most common integration pattern (Amp, Auggie, Generic, Gemini, Tabnine, …) inherits without overriding, so the base wiring is now locked. Full suite: 3011 passed (was 3009), 40 skipped, no regressions. Addresses Copilot review feedback on #2596. * fix(integrations): rename env var to SPECKIT_INTEGRATION_<KEY>_EXTRA_ARGS Renames the env-var hook prefix from `SPECIFY_INTEGRATION_*` to `SPECKIT_INTEGRATION_*` to match the established codebase convention for integration-subsystem env vars (`SPECKIT_INTEGRATION_CATALOG_URL` in `integrations/catalog.py`, `SPECKIT_COPILOT_ALLOW_ALL_TOOLS` in `integrations/copilot/__init__.py`). The `SPECIFY_*` prefix is reserved for user-facing feature-resolution variables (`SPECIFY_FEATURE`, `SPECIFY_FEATURE_DIRECTORY`); reusing it for integration-subsystem scoping would introduce a second integration namespace under a different prefix, confusing operators who already set `SPECKIT_INTEGRATION_CATALOG_URL`. Also reverts the unrelated defensive `arg_placeholder` / `registrar_config is None` guard in `CopilotIntegration.setup_skills_mode` — it was a drive-by pyright cleanup mixed into this PR. Every concrete integration sets `registrar_config` so the guard never fires in practice; the typing issue belongs in a focused follow-up rather than this env-var-hook PR. Updates everywhere the old prefix appeared: - `IntegrationBase._apply_extra_args_env_var` helper + docstring - `CopilotIntegration.dispatch_command` inline comment - All `monkeypatch.setenv(...)` calls in `tests/integrations/test_extra_args.py` - The autouse fixture scope filter - Test module docstring Full suite: 3011 passed, 40 skipped, no regressions. Addresses Copilot review feedback on #2596. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
ad62357015 |
docs: consolidate Community sections in README (#2736)
* docs: consolidate Community sections in README Replace four separate Community sections (Extensions, Presets, Walkthroughs, Friends) with a single consolidated section containing a bullet list, one shared disclaimer, and both publishing guide links. * fix: broken community anchor links and missing Hermes hook note injection - Update README.md and extensions/README.md to point community extension links to the docs site instead of removed section anchor - Add post_process_skill_content() call in Hermes setup() so hook command notes are injected into generated skills - Add Hermes test override for test_hook_sections_explain_dotted_command_conversion with Path.home() monkeypatch |
||
|
|
57a518a583 |
Fix shared script command hints for integration separators (#2627)
* fix shared script command refs for integration separators * Fix integration use shared infra refresh hint * Clarify shared infrastructure force wording --------- Co-authored-by: root <1647273252@qq.com> Co-authored-by: root <kinsonnee@gmail.com> |
||
|
|
6d25d869b3 |
feat(agy): enhance Google Antigravity CLI integration (#2689)
* feat(agy): enhance Google Antigravity CLI integration - Set requires_cli=True and install_url for CLI tool detection - Implement build_exec_args() for non-interactive execution via agy --print - Add dot-to-hyphen hook command note injection in generated SKILL.md files * fix(agy): add --ignore-agent-tools to TestAgyAutoPromote tests Tests verify file layout and setup warnings, not CLI presence. agy requires_cli=True causes CI failures when agy is not installed. |
||
|
|
9307093d8a |
Fix --dev extension agent symlinks (#2554)
* Fix dev extension agent symlinks * Address dev symlink review feedback * fix: handle dev symlink relpath failures * fix: fall back when dev cache writes fail * test: cover dev symlink fallback without privileges |
||
|
|
5a678c552e |
Share skills hook note post-processing (#2679)
* fix(integrations): share skills hook note post-processing * fix(integrations): tighten skill post-processing Apply skill content post-processing before the initial write, use an exact hook-note sentinel for idempotence, and route Copilot skill post-processing through the shared helper before adding mode frontmatter. * Make hook note injection per instruction * Deduplicate Codex hook note processing --------- Co-authored-by: Puneet Dixit <236133619+puneetdixit200@users.noreply.github.com> Co-authored-by: Puneet Dixit <puneetdixit200@users.noreply.github.com> |
||
|
|
5a50b75adb |
feat: add Hermes Agent integration (with review fixes) (#2651)
* feat: add Hermes Agent integration * feat: add Hermes Agent integration * feat: add Hermes Agent integration * feat: add Hermes Agent integration (with review fixes) - Full SkillsIntegration subclass with dual install strategy (project-local .hermes/skills/ + global ~/.hermes/skills/) - CLI fix: integration_uninstall now calls integration.teardown() instead of manifest.uninstall() directly, allowing custom cleanup - Fix Copilot review issues: - Docstring now reflects both -Q (quiet) and -q (query) flags - Empty command guard prevents passing empty skill names - Add catalog entry for hermes in integrations/catalog.json Co-authored-by: Zhaoxiaoguang001 <3357983213@qq.com> * feat: write Hermes skills directly to global ~/.hermes/skills/ Hermes loads skills from the global ~/.hermes/skills/ directory, not from project-local paths. The old dual-install strategy copied SKILL.md files to both locations — project-local (for manifest tracking) and global (for Hermes discovery). This change removes the project-local copies entirely: - setup() writes directly to ~/.hermes/skills/speckit-*/SKILL.md - An empty .hermes/skills/ marker directory is created in the project so extension commands (e.g. git) can detect Hermes as an active integration via register_commands_for_all_agents() - teardown() cleans both the global speckit-* dirs and the local marker - import yaml moved to local import inside setup() Tests updated: Hermes-specific tests now assert global skill location, and shared SkillsIntegrationTests that assumed project-local files are overridden with Hermes-appropriate assertions. Co-authored-by: Zhaoxiaoguang001 <3357983213@qq.com> * fix: address Copilot review feedback on Hermes integration Addresses all 6 review comments from copilot-pull-request-reviewer: 1. Hard-fail on missing integration key → fall back to manifest.uninstall() with a warning instead of raising an error. Allows users to always remove stale integration files even when the integration class is missing from the registry. 2. HOME isolation in tests → every test that calls setup() or CliRunner now monkeypatches Path.home() to a temp directory, keeping the test suite hermetic and non-destructive. 3. HermesIntegration.teardown() now delegates to manifest.uninstall() for project-local tracked files (scripts, manifest), merging results with global cleanup. 4. Global skills cleanup gated behind force=True to avoid destroying speckit-* skills shared across multiple Spec Kit projects when running 'specify integration uninstall hermes' without --force. 5. Line 160 isolation (CLI test test_complete_file_inventory_sh). 6. Line 258 isolation (Path.home assertion in test_ai_hermes_without_ai_skills_auto_promotes). * fix: address second Copilot review round — 6 remaining observations - Move to module scope (was inside per-template loop) - Add safety checks in setup() matching standard - Fix docstrings: global skills always removed on uninstall (standard) - Fix removal tracking: only report after successful rmtree - Override shared test_modified_file_survives_uninstall with Hermes-appropriate behaviour (global skills always removed, no hash tracking) - Update PR description to match implementation (global-only skills + marker) * fix: add first-class global/home-based agent dir support in CommandRegistrar Resolves Copilot HIGH concern (discussion_r3312194525): HermesIntegration.registrar_config.dir was '.hermes/skills' (project- relative), but skills live in ~/.hermes/skills/ (global). Extensions and presets registering commands for the 'hermes' agent via CommandRegistrar would write to the project-local marker directory instead of the real global skills directory, making those commands invisible to Hermes. Fix consists of three parts: 1. CommandRegistrar._resolve_agent_dir now supports '~/'-prefixed and absolute paths in agent_config['dir']. Relative paths still resolve against project_root as before — zero change for existing agents (Claude, Codex, Gemini, etc.). 2. HermesIntegration.registrar_config.dir changed from '.hermes/skills' to '~/.hermes/skills', so extensions/presets write directly to the global directory Hermes searches at runtime. 3. Two inline project_root / agent_config['dir'] calls in the extension update backup/restore paths (src/specify_cli/__init__.py) now delegate to _resolve_agent_dir, giving them the same global-dir support plus the legacy_dir fallback they were missing (improvement for all agents). Test side-effect: test_update_failure_rolls_back_registry_hooks_and_commands was constructing verification paths with project_dir / '~/.hermes/skills' (literal tilde) — fixed to use _resolve_agent_dir and monkeypatch Path.home() so Hermes' global dir doesn't leak into the real filesystem. * fix: address remaining 3 Copilot review observations (round 3) - teardown docstring: clarify marker removal is conditional (if empty) - test_pre_existing_skills_not_removed: now actually calls teardown() to verify foreign skills survive uninstall (was only running setup) - integration_switch Phase 1: replaced old_manifest.uninstall() + remove_context_section() with current_integration.teardown(), matching the pattern already used in integration_uninstall. This ensures custom teardown logic (e.g. Hermes global skills cleanup) runs during switches. * fix: address Copilot round 4 — home-relative dir resolution + project-local detection 1. _resolve_agent_dir(): expand ~/... via Path.home() + slice instead of expanduser(), so tests that monkeypatch Path.home() properly isolate the home directory (Copilot r3312731595, r3312731729) 2. Add detect_dir field to registrar_config: Hermes declares detect_dir='.hermes/skills' (project-local marker). CommandRegistrar checks detect_dir before resolving the output dir, preventing global dirs like ~/.hermes/skills from causing false detection in every project (Copilot r3312731682) 3. test_update_failure_rolls_back: no additional changes needed — the _resolve_agent_dir fix makes the existing Path.home() monkeypatch effective, so ~/.hermes/skills is not found in the fake home and Hermes is properly skipped. Tests: 2236 passed (2009 integration + 195 extension + 32 hermes) --------- Co-authored-by: Zhaoxiaoguang001 <3357983213@qq.com> Co-authored-by: majordave <majordave@users.noreply.github.com> |
||
|
|
c6afe4cde1 |
feat(workflows): expose {{ context.run_id }} template variable (#2664)
* feat(workflows): expose `{{ context.run_id }}` template variable
Closes #2590.
Surfaces the engine-assigned run id (the same 8-character hex
string Spec Kit prints as `Run ID:` at the end of
`workflow run`) as a workflow template variable so YAML
authors can reference it from shell `run:`, command
`input.args:`, switch `expression:`, and any other field that
already evaluates `{{ ... }}` templates.
### Why
The run id is the natural join key between a Spec Kit workflow
run and downstream artifacts, telemetry, or per-run scratch
state. Today the operator sees it in stdout but workflows
themselves cannot reference it — there was no way to stamp a
log line, name a scratch directory, or tag an artifact with
the same id Spec Kit assigned.
The three motivating use cases from the issue:
1. Telemetry / observability — stamp logs and events with the
run id so external systems can join workflow runs to
downstream artifacts.
2. Per-run scratch / isolation — interactive operator commands
that need their own state directory under
`/tmp/run-<id>/`.
3. Run-id in artifact metadata — stable join key from artifact
back to the producing run.
### Implementation
`StepContext.run_id` is already populated by `WorkflowEngine`
in both `execute()` and `resume()`. The only gap was the
template namespace builder.
`_build_namespace` (in `workflows/expressions.py`) now adds a
`context` key alongside the existing `inputs`, `steps`,
`item`, and `fan_in` namespaces:
```python
ns["context"] = {"run_id": run_id}
```
The value is always present (even outside a run) and falls
back to an empty string when no run is active. Workflows
referencing `{{ context.run_id }}` therefore never error — a
hard requirement from the issue's acceptance criteria for
dry-run, validation, and ad-hoc evaluator usage.
### Default behaviour preserved
Workflows that do not reference `{{ context.run_id }}` are
byte-equivalent to before this change. The `context`
namespace is added unconditionally to keep template
resolution branch-free, but its presence has no observable
effect when nothing references it.
### Tests
`TestExpressions` (unit-level) gains three tests:
- `test_context_run_id_resolves` — direct lookup against a
`StepContext(run_id=...)`.
- `test_context_run_id_defaults_to_empty_when_unset` —
graceful default outside a run context.
- `test_context_run_id_string_interpolation` — mixed
template (e.g. `"RUN_ID={{ context.run_id }}"`).
`TestContextRunId` (end-to-end) covers the three step types
the acceptance criteria called out:
- `test_shell_run_resolves_run_id` — `run:` field
substitution, verified via captured stdout.
- `test_command_input_args_resolves_run_id` — `input.args:`
resolution, captured in step output even when CLI dispatch
is unavailable (the artifact-metadata use case).
- `test_switch_expression_matches_on_run_id` — switch
matches against the resolved value, proving the run id is a
first-class value in the expression engine, not just an
interpolation token.
- `test_workflow_without_context_reference_unchanged` —
locks the byte-equivalent default required by the issue.
### Docs
`workflows/README.md` gains a "Runtime Context" subsection
under "Expressions" documenting the new namespace and the
three canonical use patterns (telemetry, per-run scratch,
artifact metadata).
* test(workflows): drop inline double-quotes in run_id shell tests
`test_shell_run_resolves_run_id` and
`test_switch_expression_matches_on_run_id` used
`run: 'echo "RUN_ID={{ context.run_id }}"'` with inner double-quotes
around the echo argument. Bash/sh strips those quotes before invoking
echo, but cmd.exe (used on Windows when `shell=True`) treats them
as literal characters and emits `"RUN_ID=abc12345"` — failing the
exact-match assertion. Linux passed; all three Windows-latest matrix
entries failed with `assert '"RUN_ID=abc12345"' == 'RUN_ID=abc12345'`.
Resolve by dropping the inner double-quotes (the value has no spaces
or shell metacharacters) and wrapping the YAML scalar in plain
double-quotes the same way other shell-step tests in this file do
(e.g. `run: "echo b-saw-..."`). Behaviour-equivalent on POSIX,
portable to cmd.exe.
|
||
|
|
66884db85b |
fix: resolve __SPECKIT_COMMAND_*__ refs in preset skill rendering (#2717) (#2718)
* fix: resolve __SPECKIT_COMMAND_*__ refs in preset skill rendering (#2717)
The preset skill layer mirrors command templates into SKILL.md files but
only ran resolve_skill_placeholders(), leaving command cross-references as
raw __SPECKIT_COMMAND_<NAME>__ placeholders instead of rendering them as
/speckit-<cmd> the way CommandRegistrar.register_commands() does. As a
result, presets that override core commands under the agent skill layer
(e.g. Claude --ai-skills) leaked the raw tokens into SKILL.md.
Add a shared PresetManager._resolve_skill_command_refs() helper that maps
the agent's invoke separator to IntegrationBase.resolve_command_refs(), and
call it right after resolve_skill_placeholders() in every preset
skill-rendering path: _register_skills() (install), the _reconcile_skills()
override-restoration block, and both _unregister_skills() restore paths.
This mirrors register_commands() and addresses the path divergence flagged
in #1976.
Add regression tests covering the install and restore paths.
AI assistance: authored with Claude Code (Anthropic) — analysis, patch, and
tests. Verified via the existing pytest suite plus a manual CLI install and
remove cycle on a Claude --ai-skills project.
* test: cover reconcile-override and extension restore command-ref paths (#2718 review)
Copilot review flagged that the install and core-template restore paths
gained regression tests, but the reconcile project-override branch and the
extension-backed restore branch were uncovered. Add focused tests for both:
- test_reconcile_override_skill_resolves_command_refs: a project override
wins after preset removal; _reconcile_skills must render command refs.
- test_extension_restore_resolves_command_refs: a skill restored from an
extension command body must also render command refs.
Both fail on main and pass with the fix in
|
||
|
|
3227b9660e |
fix: paths-only skips branch validation, setup-plan preserves existing plan (#2672)
* fix: paths-only skips branch validation, setup-plan preserves existing plan (#2653) - check-prerequisites.sh/ps1: move branch validation after --paths-only early exit so --paths-only returns paths without requiring a spec branch - setup-plan.sh/ps1: skip template copy when plan.md already exists to prevent overwriting user-authored plans on reruns - setup-plan.sh: send status messages to stderr in --json mode so stdout remains parseable JSON - Add tests for both fixes (bash + PowerShell) * fix: remove trailing whitespace in PowerShell scripts * fix: route PS skip message to stderr in -Json mode, add PS JSON assertions Address review: setup-plan.ps1 Write-Output polluted stdout in -Json mode when plan.md already existed. Use [Console]::Error.WriteLine() when -Json is set. Add json.loads + stderr assertions to the PS rerun test to catch regressions. * fix: use Test-Path -PathType Leaf for plan existence check Bare Test-Path matches directories too, which would silently skip plan creation if a directory existed at the plan.md path. |
||
|
|
e54653efcc |
fix: create skills directory on demand during extension/preset install (#2711)
* fix: create skills directory on demand during extension/preset install _get_skills_dir() in both extensions.py and presets.py returned None when the skills directory did not yet exist on disk, even though skills were enabled in init-options. This caused extension skill registration to silently produce an empty registered_skills list and skip writing SKILL.md files. Replace the is_dir() bail-out with mkdir(parents=True, exist_ok=True) so the directory is created on demand when ai_skills is enabled. Update the existing test expectation and add a parametrized regression test (claude + codex) that installs an extension before the skills directory exists and asserts SKILL.md files and registry entries are created. Fixes #2682 * test: assert skills dir is NOT created when skills are disabled Strengthen negative tests to verify _get_skills_dir does not create the directory on disk when ai_skills is false or init-options.json is absent. * fix: add symlink/containment check and preserve Kimi existence gate Address PR review feedback: - Use _ensure_safe_shared_directory() instead of raw mkdir() to prevent symlink-following writes outside the project root. - Restore the is_dir() existence gate for the Kimi native-skills fallback (ai_skills=false): only create the directory on demand when ai_skills is explicitly enabled. - Update docstrings to reflect the on-demand vs existence-gate behavior. - Reuse resolve_skills_dir helper in tests instead of manually reconstructing paths from AGENT_CONFIG. * refactor: extract resolve_active_skills_dir shared helper Deduplicate the _get_skills_dir logic that was nearly identical in ExtensionManager and PresetManager into a single module-level resolve_active_skills_dir() function in __init__.py. The shared helper wraps _ensure_safe_shared_directory errors with skills-specific messages so users see 'agent skills directory' instead of 'shared infrastructure directory' in error output. Both class methods now delegate to the shared helper. * fix: preserve original error reason in skills dir safety check Include the original exception message from _ensure_safe_shared_directory in the re-raised ValueError so the user sees the specific reason (symlink, not-a-directory, path escape, etc.) instead of a generic message. * fix: handle skills dir safety errors gracefully during install Catch ValueError/OSError from _get_skills_dir() inside _register_extension_skills() so a symlink or permission error logs a warning and returns [] instead of aborting mid-install and leaving a partially-installed extension without a registry entry. Also document OSError in resolve_active_skills_dir() docstring. * fix: catch errors in _get_skills_dir and use _print_cli_warning Move the ValueError/OSError catch from _register_extension_skills into _get_skills_dir itself so all callers (install, uninstall, reconcile) are protected from unsafe-path exceptions. Replace logging.getLogger().warning with _print_cli_warning for consistent Rich-formatted user output. * fix: use context-aware error messages for skills directory safety Add a 'context' parameter to _ensure_safe_shared_directory (defaults to 'shared infrastructure directory' for backward compat). The skills dir caller passes context='agent skills directory' so error messages say e.g. 'Refusing to use symlinked agent skills directory' instead of 'Refusing to use symlinked shared infrastructure directory'. Simplify resolve_active_skills_dir by removing the now-unnecessary try/except wrapper. * fix: validate Kimi native-skills directory for symlink/containment The Kimi fallback path (ai_skills=false) used is_dir() which follows symlinks, so a symlinked .kimi/skills could cause writes outside the project root. Now validates with _ensure_safe_shared_directory(create= False) before returning the directory. |
||
|
|
c7e0cacaff |
fix: PS 5.1 compat — replace non-ASCII chars in shipped PowerShell scripts (#2709)
* Initial plan * fix: replace non-ASCII chars in PS1 files, add encoding regression tests, fix ANSI stripping in tests, update docs --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> |
||
|
|
0ae451f697 |
Add util for windows sub-process (#2598)
* Add util for windows sub-process * Use platform-aware Copilot executable in subprocess calls * Update test_workflows.py |
||
|
|
7f33dca87c |
refactor: create commands/ package and move init handler (PR-4/8) (#2615)
* refactor: create commands/ package and move init handler (PR-4/8) - Extract agent configuration constants (AGENT_CONFIG, AI_ASSISTANT_HELP, SCRIPT_TYPE_CHOICES, etc.) to _agent_config.py to avoid circular imports - Create commands/ package skeleton with stub modules for each command group - Move init command handler (~670 lines) from __init__.py to commands/init.py using the register(app) pattern; lazy imports inside the handler body prevent circular dependencies with __init__.py - Re-export AGENT_CONFIG, AI_ASSISTANT_HELP, SCRIPT_TYPE_CHOICES from __init__.py for backward compatibility - Add tests/test_commands_package.py to verify package structure * fix(tests): update patch targets after moving init handler to commands/init.py _stdin_is_interactive and select_with_arrows are now bound in specify_cli.commands.init, not specify_cli directly. * fix(lint): remove unused imports and mark re-exports in __init__.py - Remove shutil, shlex top-level imports (used lazily inside functions) - Remove rich.live.Live import (moved to commands/init.py) - Mark select_with_arrows and _locate_bundled_workflow as explicit re-exports to satisfy ruff F401 * chore: add from __future__ import annotations to new modules Aligns with the project convention established in _console.py, _assets.py, _utils.py, and other modules. * docs(cli): align init help with bundled scaffolding Potential fix for pull request finding Update command package documentation and init help text to reflect the current implementation: init uses bundled assets and integration setup, while placeholder command modules are import anchors until extracted. Remove the unused tracker-active flag assignment that had no reader in the codebase. Constraint: --offline is hidden/no-op and init no longer downloads templates from GitHub releases Rejected: Add no-op register functions to placeholder modules | would imply extracted command groups are implemented there Confidence: high Scope-risk: narrow Directive: Keep CLI help text aligned with the actual init scaffolding path Tested: uv run specify init --help; uv run pytest tests/test_commands_package.py tests/test_agent_config_consistency.py -q; uv run pytest tests/test_commands_package.py tests/test_console_imports.py tests/integrations/test_cli.py -q Not-tested: full test suite * fix(init): align preset failure reporting with _print_cli_warning helper Use the _print_cli_warning helper (introduced in main) for preset install failures so that output matches the expected format: "Failed to install preset '<name>': ..." "Continuing without the optional preset." * fix(init): remove unused lazy imports The init command imported CLI error formatting helpers through its circular-dependency-safe lazy import block, but the module does not use them. Remove those imports so ruff does not report F401. Constraint: uvx ruff check src/ must pass. Rejected: Wire the helpers into init error handling | Existing preset warnings already use _print_cli_warning, and changing behavior is unnecessary for this lint fix. Confidence: high Scope-risk: narrow Directive: Keep lazy import blocks limited to names consumed in the importing module. Tested: uvx ruff check src/ Not-tested: Full pytest suite Co-authored-by: OmX <omx@oh-my-codex.dev> --------- Co-authored-by: OmX <omx@oh-my-codex.dev> |
||
|
|
dca81b90de | fix init-options speckit version refresh (#2647) | ||
|
|
616eba6a57 |
fix: while/do-while loop condition reads stale iteration-0 step output (#2662)
* fix: while/do-while loop condition reads stale iteration-0 step output
After executing namespaced loop body steps, copy each iteration's
results back to the original unprefixed step key so that
evaluate_condition() sees the latest values instead of stale
iteration-0 data.
Fixes #2592
* address review: cross-platform tests, preserve iteration-0 history
- Rewrite shell scripts in tests to use Python via script files
instead of POSIX syntax, so they pass on Windows CI.
- Snapshot iteration-0 nested-step results under a namespaced key
(parent:child:0) before the first copy-back overwrite, preserving
complete per-iteration history for debugging.
* address review: skip copy-back on paused/failed iterations
Move the status check before the copy-back so that partial results
from paused or failed nested steps (e.g., a gate awaiting input)
do not overwrite the unprefixed key. This preserves correct resume
behavior.
* address review: quote paths in test shell commands
Quote both the Python executable and script file paths in the
run: commands to handle spaces in paths on Windows.
* address review: execute loop body with original IDs
Instead of namespacing step IDs for execution and copying results
back, execute the loop body with original (unprefixed) step IDs so
results naturally land at the right keys. Snapshot previous
iteration results to namespaced keys (parent:child:N) for history
only.
This fixes multi-step loop bodies where step B references step A's
output within the same iteration — previously step B would see
stale data until the copy-back ran after the entire iteration.
* address review: namespaced execution with per-step copy-back
Revert to namespaced step IDs for execution (preserving unique
log entries and state keys per iteration) but copy each step's
result back to the unprefixed key immediately after it completes.
This preserves backward compatibility (same namespaced key format,
same log IDs) while fixing both the condition evaluation bug and
inter-step references within multi-step loop bodies.
* address review: alias after status check, add multi-step body test
- Move per-step aliasing below the PAUSED/FAILED/ABORTED status
check so partial results from incomplete steps are not aliased
back to the unprefixed key.
- Add test_while_loop_multi_step_body_inter_step_refs to exercise
a multi-step loop body where step B reads step A's output within
the same iteration, verifying per-step aliasing works correctly.
Addresses feedback from @doquanghuy (items 2 & 4) and Copilot
review on commit
|
||
|
|
0dee2faf11 |
fix(catalogs): reject boolean priority in extension and preset catalog readers (#2589)
`bool` is a subclass of `int` in Python, so `int(True)` silently returns
`1`. The extension- and preset-catalog config readers coerced priority
with a bare `int(item.get("priority", idx + 1))`, which meant a YAML
config like:
catalogs:
- name: mine
url: https://example.com/catalog.json
priority: yes # parses to True
was silently accepted as a valid priority of 1, quietly reordering the
catalog stack instead of raising the same `Invalid priority` error a
typo of `priority: not-a-number` already raises.
The sibling integration-catalog reader in `src/specify_cli/catalogs.py`
already guards this case (see `catalogs.py:137`). This change mirrors
that pattern in `extensions.py` and `presets.py` so the three catalog
validators stay consistent, and adds regression tests for both readers
matching the existing `test_load_catalog_config_rejects_boolean_priority`
template in `tests/integrations/test_integration_catalog.py`.
|
||
|
|
b4b83be51b |
feat: add self-check tip to check output (#2574)
* feat: add self-check tip to check output * style: drop trailing period from self-check tip Aligns the new tip with the other `Tip:` lines in `specify check`, which don't end in a period. Per Copilot review feedback on #2574. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
3d50f85875 |
fix(cli): clarify exception diagnostics (#2602)
Consolidate the CLI diagnostic plan, implementation, and test hardening into one reviewable change. The CLI now reports phase and target context for broad failure paths while preserving existing fail-fast behavior for real setup failures and warning-only behavior for optional best-effort work. The workflow unit tests also avoid discovering real local agent CLIs, so developer machines with tools such as gemini installed do not hang pytest during metadata-only assertions. Constraint: CLI setup failures must remain fail-fast, while optional preset and cleanup paths should continue with clear warnings. Rejected: Replace broad handlers across the whole codebase in one pass | too broad for a targeted CLI diagnostic fix Rejected: Add runtime timeouts to workflow agent dispatch | dispatch may legitimately be long-running and the observed hang was test isolation Confidence: high Scope-risk: moderate Directive: Keep future best-effort CLI warnings tied to the failed phase and target so users can diagnose setup state. Tested: uvx ruff check src/; uv run pytest tests/integrations/test_cli.py -v; uv run pytest tests/test_workflows.py::TestCommandStep::test_step_override_integration tests/test_workflows.py::TestPromptStep::test_execute_with_step_integration tests/test_workflows.py::TestPromptStep::test_execute_with_model -vv; uv run pytest Not-tested: Real Nacos/PG/Redis-style external service failure injection; real interactive workflow dispatch against installed gemini CLI |
||
|
|
9735145289 |
fix(codex): inject dot-to-hyphen hook command note in Codex skills (#2503)
* fix(codex): inject dot-to-hyphen hook command note in Codex skills Hook commands in `.specify/extensions.yml` use dotted ids like `speckit.git.commit`, but Codex skills are named with hyphens (`speckit-git-commit`). The Claude integration handles this via an explicit instruction injected into each generated SKILL.md by `ClaudeIntegration.post_process_skill_content`, but the Codex integration had no such override, so Codex would emit `/speckit.git.commit` (which does not resolve) instead of `/speckit-git-commit`. This adds the same `_inject_hook_command_note` helper and a `post_process_skill_content` override to `CodexIntegration`, plus a small `setup()` override that applies the post-process to each generated SKILL.md (mirroring the pattern in `ClaudeIntegration`). Also widens the existing `test_non_claude_post_process_is_identity` test to use `agy` (another `SkillsIntegration` with no override), since asserting identity behavior on Codex would now incorrectly fail. Tests: - New `TestCodexHookCommandNote` class mirrors `TestClaudeHookCommandNote`: setup-level injection, no-op when no hook block is present, idempotency, and indentation preservation. - `pytest tests/` → 2866 passed, 34 skipped. Signed-off-by: Chao Zhang <1175468+picklebento@users.noreply.github.com> * fix(codex): handle empty eol when instruction is final line without newline The hook-note injection regex allowed end-of-string matches via ``$``, which left the captured ``eol`` empty. When the matched indent was also empty, the substitution concatenated the note onto the same line as the instruction. Default ``eol`` to ``\n`` when the capture is empty. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Signed-off-by: Chao Zhang <1175468+picklebento@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
51e6a140e2 |
refactor: migrate extension catalog stack parsing to shared base (#2576)
Co-authored-by: root <1647273252@qq.com> |
||
|
|
409ec59704 |
fix(workflow): support integration: auto to follow project's initialized AI (#2421)
* fix(workflow): support integration: auto to follow project's initialized AI Closes #2406 (squashed) * fix(workflow): combine JSONDecodeError and UnicodeDecodeError handling Address Copilot feedback: UnicodeDecodeError can be raised by both read_text() and json.loads(), so combining the handlers ensures both cases produce a consistent, clear error message. * fix(workflows): honor integration_state schema guard and modern state in 'integration: auto' Three Copilot follow-ups on PR #2421: 1. engine.py:799 — `_load_project_integration` was bypassing the same schema guard `_read_integration_json` enforces. It now reads the schema field directly, returns None on a future schema (so the workflow falls back to the literal 'auto' default rather than guessing), and routes through `normalize_integration_state` / `default_integration_key` so modern installs that record `default_integration` / `installed_integrations` (without the legacy top-level `integration` field) resolve correctly. 2. test_workflows.py — added two regression cases: - `integration: auto` resolves a modern normalized state file - `integration: auto` falls back when the state file declares a newer `integration_state_schema` than this CLI supports 3. test_cli.py — added a CLI-level regression for the `UnicodeDecodeError` branch in `_read_integration_json` to match the existing malformed-JSON coverage. * refactor(integration): extract shared try_read_integration_json helper Address Copilot review on PR #2421: Both `_read_integration_json` (CLI) and `_load_project_integration` (workflow engine) were parsing `.specify/integration.json` independently, duplicating the schema guard and risking drift between the two readers. Extract the parse + schema validation into a single low-level helper `try_read_integration_json` in `integration_state.py` that returns either the normalized state or a structured `IntegrationReadError`. Both callers now delegate to this helper: - CLI keeps its loud-fail UX: each error kind ("decode", "os", "not_object", "schema_too_new") is translated into the existing console message + typer.Exit(1). - Engine keeps its silent fallback: any error simply returns None so `integration: auto` falls back to the workflow's literal default. This eliminates the divergence Copilot flagged without changing observable behavior for either caller. * fix(integration): distinguish missing file from non-regular path Address Copilot review on PR #2421: `try_read_integration_json` was collapsing two distinct cases into a single `(None, None)` return: 1. `.specify/integration.json` truly missing — silent fallback is correct. 2. Path exists but is a directory, socket, or other non-regular file — this is a misconfiguration the CLI should surface loudly. Split the check: `exists()` falsey returns `(None, None)`; existing-but- not-a-regular-file returns `(None, IntegrationReadError(kind="os", ...))` so the CLI's loud-fail path produces an actionable error while the engine still treats it as a fallback to the workflow's literal default. * docs(workflow): clarify version pin, advisory integrations list, enum exemption - workflow.yml: fix comment that said 0.8.3 was first release with auto resolution; the pin is >=0.8.5 so the comment now matches the pin. - workflow.yml: clarify that requires.integrations.any is an advisory, non-exhaustive compatibility hint, not a closed set. - engine.py: clarify that the auto-sentinel exemption only skips enum membership; declared type is still enforced through _coerce_input. * fix(workflow): resolve auto sentinel for provided values; report stat errors Two Copilot findings fixed: 1. _resolve_inputs only resolved the ``integration: auto`` sentinel when it came from the input default. A caller explicitly providing ``{"integration": "auto"}`` (which the workflow prompt advertises as a valid value) bypassed _resolve_default and the literal "auto" leaked to dispatch. Provided values now go through the same resolution path as defaults, and the enum-membership exemption applies in both cases. Regression test added. 2. try_read_integration_json used Path.exists() / Path.is_file() as a pre-check. Both return False on some OSErrors (e.g. permission errors during stat), which silently treated an unreadable-but-present file as missing — the engine fell back without warning and the CLI failed to surface the loud error. The pre-check is gone: read_text() is attempted directly, FileNotFoundError means missing (silent fallback), IsADirectoryError and other OSErrors become loud IntegrationReadError. * fix(workflow): enforce declared type for string inputs, reject bool-as-number Two Copilot findings fixed: 1. _coerce_input previously coerced/validated only ``number`` and ``boolean`` types, so ``type: string`` silently accepted any Python value (numbers, lists, dicts). A YAML authoring mistake like ``type: string`` + ``default: 5`` slipped through. Strings are now required to actually be strings; non-strings raise ValueError, which surfaces as an ``invalid default`` error from validate_workflow. 2. ``type: number`` accepted ``default: true`` because ``bool`` is a subclass of ``int`` (``float(True) == 1.0``). Bools are now rejected explicitly in the number path so the YAML mistake fails fast. The boolean path is also tightened to reject non-bool / non-string values for symmetry. Comment on the auto-sentinel enum exemption updated to reflect the stronger guarantee. Regression tests added for both rejections. * fix(cli): drop unused normalize_integration_state import to satisfy ruff CI's `uvx ruff check src/` flagged this as F401: the symbol was imported under a private alias but never referenced. Tests stay green after removal. |
||
|
|
d947fda96f |
refactor: extract _version.py from __init__.py (PR-3/8) (#2550)
* refactor: extract _version.py from __init__.py (PR-3/8) Move version-checking helpers and `specify self` sub-commands into a focused `_version.py` module. Moved symbols: - GITHUB_API_LATEST — GitHub releases API endpoint constant - _get_installed_version — importlib.metadata-based version lookup - _normalize_tag — strip leading 'v' from release tag strings - _is_newer — PEP 440 version comparison - _fetch_latest_release_tag — single outbound call to GitHub API - self_app — Typer sub-app for `specify self` - self_check, self_upgrade — `specify self check/upgrade` commands Dependency rule: _version.py imports only stdlib + packaging + ._console. Backward compatibility: GITHUB_API_LATEST, self_check, self_upgrade remain importable from specify_cli via re-exports in __init__.py. Update test_upgrade.py to import helpers from specify_cli._version and patch at the correct module path (specify_cli._version.*). Add test_version_imports.py as regression guard. * fix(tests): update _fetch_latest_release_tag import path in test_authentication.py PR-3 moved _fetch_latest_release_tag from specify_cli into specify_cli._version. test_upgrade.py was updated at the time, but test_authentication.py::TestFetchLatestReleaseTagDelegation still imported from the old location, causing ImportError on all three delegation tests. Update all three inline imports to the correct module path. |
||
|
|
f684305e51 |
fix(powershell): ensure UTF-8 templates are written without BOM (#2280)
* fix(powershell): strip BOM from templates and ensure No-BOM output * fix: address review feedback on encoding and naming for all ps scripts * fix: address copilot feedback (encoding detection and variable naming) * fix: remove duplicate comments in setup-plan.ps1 * test: verify spec.md is written without UTF-8 BOM * test: also verify BOM-free output under Windows PowerShell 5.1 * fix * fix: resolve merge conflict with main, add TestDescriptionQuoting * fix: resolve TestDescriptionQuoting string quoting conflict with main * test: restore PowerShell prefix-stripping parity test in TestGetFeaturePathsSinglePrefix * fix: remove trailing whitespace from module docstring blank lines * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * test: seed ps_git_repo with BOM-prefixed template to exercise WriteAllText fix * fix: remove duplicate ps_git_repo fixture, restore ext_ps_git_repo * fix: remove unrelated TestDescriptionQuoting and restore original test_ps_specify_feature_prefixed_resolves_by_prefix --------- Co-authored-by: Nimraakram22 <nimra.akram123451@gmail.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
be382804c7 |
Fix preset skill description precedence (#2538)
* Fix preset skill description precedence * Fix skill description precedence during restore --------- Co-authored-by: root <1647273252@qq.com> |
||
|
|
c87081a50a | fix(integration): clarify multi-install guidance (#2549) | ||
|
|
e6afba9429 | feat: add version feature reporting (#2548) | ||
|
|
59fdca5997 |
fix(cli): harden extension registration and discovery workflows (#2499)
* chore: update community catalog with latest extension versions
- Update memory-md from 0.7.9 to 0.8.0
- Update architecture-guard from 1.6.7 to 1.8.0
* fix(cli): harden extension registration with project-level tracking in extensions.yml
* test(cli): add comprehensive unit tests for extension registration logic
* chore: remove out-of-scope catalog changes
* refactor: address PR feedback for extension registration hardening
* fix: harden extension registration defensive logic and add comprehensive unregister_hooks tests
- Add dict guard to register_hooks() to handle corrupted extensions.yml (non-dict root)
- Add 5 comprehensive tests for unregister_hooks() workflow:
* Full workflow with hooks + installed list removal
* Resilience when config has no 'hooks' key
* Corrupted YAML handling
* Multiple extension scenarios
* All 11 tests passing
* fix: sanitize installed to strings, guard unregister_hooks dict, handle null hook values
- register_extension(): filter non-string entries from installed before sort
- register_hooks(): normalize hooks to {} when missing or not a dict
- unregister_hooks(): add isinstance(config, dict) guard before key checks
- unregister_hooks(): coerce null/scalar hook lists to [] before iteration
- tests: add 3 regression tests for no-hooks manifest, mixed-type installed, null hook values
- All 14 tests passing
* fix(cli): persist sanitization results and harden hook registration
* Harden extension registration to always persist sanitization results
* Hardening extension registration: support mapping entries, improve persistence, and fix update rollback
* fix(cli): harden extension update and unregistration workflows
* fix(cli): move update sentinels outside try block to prevent NameError on rollback
* fix(cli): sanitize hook event lists in register_hooks to prevent crashes
* fix(cli): deduplicate hook entries and harden rollback hooks-restore guards
* test(cli): add regression tests for extension update and rollback hardening
* fix(cli): deduplicate installed list by id in register_extension
* fix(cli): consolidate and harden extension update rollback logic
* fix(cli): initialize backup_registry_entry before try block to prevent UnboundLocalError on rollback
* fix(tests): return Path from download_extension mock and add Path import
* fix(cli): normalize get_project_config() return to dict; deduplicate in unregister_extension()
* fix(cli): normalize hooks/installed/settings in get_project_config(); use tmp_path-scoped zip in tests
* fix(cli): set modified=True on hook coercion in rollback; sanitize hook event values in get_project_config(); harden test assertions
* fix(cli): filter non-dict hook entries in get_project_config(); remove dead MISSING sentinel
* fix(cli): gate extensions.yml rollback on backup_hooks is not None; update stale comment
* fix(cli): move _AgentReg import outside try block; assert result.exception is None in tests
* fix(extensions): consistent key order in default config; deep-copy backup_installed
* test: fix misleading comment; assert exit_code==1 in rollback test
* test: clean up duplicate imports in hardening tests
* refactor(extensions): extract _sanitize_installed_list helper; strengthen hook unregister assertion
* fix(extensions): validate extension IDs in _sanitize_installed_list; clarify test comment
|
||
|
|
2fb9d3bb4b |
refactor: extract _assets.py and _utils.py from __init__.py (PR-2/8) (#2543)
* refactor: extract _assets.py and _utils.py from __init__.py Move bundle path resolution and version lookup into _assets.py (stdlib only, zero internal imports), and system utilities (subprocess, tool detection, file operations) into _utils.py (imports only from ._console). Re-export all moved symbols from __init__.py for backward compatibility. Update test_check_tool.py to patch both specify_cli and specify_cli._utils namespaces since constants are now defined in _utils. * style: apply PR-1 review patterns to _assets.py and _utils.py - Add module docstring to _assets.py (stdlib-only, zero internal imports) - Add blank line after `from __future__ import annotations` in both files - Replace `Optional[X]` with `X | None` throughout _utils.py (PEP 604) - Remove unused `Optional` import from _utils.py - Use explicit re-export form (`X as X`) for public symbols in __init__.py - Remove unused `subprocess` and `tempfile` imports from __init__.py (moved to _utils.py) |
||
|
|
9732a4d092 |
fix(opencode): use commands/ directory (plural) to match OpenCode docs (#2453)
* fix(opencode): use commands/ directory (plural) to match OpenCode docs OpenCode documentation (https://opencode.ai/docs/commands/) uses .opencode/commands/ (plural) as the canonical command directory. The OpenCode runtime supports both .opencode/command/ and .opencode/commands/ via a {command,commands} glob, but the singular form was the original convention and is now outdated. Update the OpenCode integration to write to .opencode/commands/ instead of .opencode/command/, aligning with the documented standard and the OpenSpec fix (Fission-AI/OpenSpec#748). Signed-off-by: Marcus Burghardt <maburgha@redhat.com> Assisted-by: OpenCode (claude-opus-4-6) * feat(registrar): add legacy_dir fallback for backward-compatible directory migration Add _resolve_agent_dir() to CommandRegistrar that checks a legacy_dir fallback when the canonical directory does not exist. When legacy_dir is found, a deprecation warning directs users to run "specify integration upgrade" to migrate. The OpenCode integration declares legacy_dir: ".opencode/command" so that extension and preset registration, as well as command cleanup, continue working for projects that have not yet migrated to .opencode/commands/. The legacy_dir mechanism is opt-in: integrations that do not declare it get no fallback and no behavioral change. Add end-to-end test verifying that "specify integration upgrade opencode" migrates commands from legacy .opencode/command/ to canonical .opencode/commands/ and removes stale files. Signed-off-by: Marcus Burghardt <maburgha@redhat.com> Assisted-by: OpenCode (claude-opus-4-6) * fix(registrar): address PR review feedback on legacy_dir handling - Fix deprecation warning formatting: quote paths and remove trailing '/.' that produced confusing '.opencode/commands/.' output - Eliminate duplicate warnings: pass pre-resolved directory to register_commands() via _resolved_dir parameter so _resolve_agent_dir() is only called once per agent - Fix unregister_commands() to clean both canonical and legacy dirs when both exist, preventing orphaned command files after upgrade - Add test_unregister_cleans_legacy_when_both_dirs_exist regression test and tighten warning count assertion to exactly 1 Assisted-by: OpenCode (claude-opus-4-6) Signed-off-by: Marcus Burghardt <maburgha@redhat.com> --------- Signed-off-by: Marcus Burghardt <maburgha@redhat.com> |
||
|
|
4f51e066c3 |
refactor: extract _console.py from __init__.py (PR-1/8) (#2474)
* refactor: extract _console.py from __init__.py Move Rich UI primitives (BANNER, TAGLINE, StepTracker, get_key, select_with_arrows, console, BannerGroup, show_banner) into a new src/specify_cli/_console.py module. Re-export all symbols from __init__.py to preserve the public API. Add regression guard tests. * refactor(console): improve type annotations and add guard for empty options - Add module-level docstring documenting the console layer's purpose and the dependency-layering rule (no imports from other specify_cli modules) - Tighten select_with_arrows() signature: options typed as dict[str, str] and default_key as str | None to align with repo typing style - Add early ValueError guard when options is empty, preventing downstream ZeroDivisionError / IndexError inside the Live loop * refactor(console): improve type safety and code quality in _console.py - Add Callable import from collections.abc for precise callback typing - Annotate StepTracker._refresh_cb as Callable[[], None] | None - Add parameter/return types to attach_refresh() - Use explicit keyword form typer.Exit(code=1) across all error exits - Add blank line between StepTracker class and get_key() (PEP 8) - Add regression test for select_with_arrows() raising ValueError on empty options dict * style(cli): add __all__ declaration to fix Ruff F401 lint warnings - Add explicit __all__ for intentional re-exports (BANNER, TAGLINE, get_key) - Prevent F401 unused import errors in CI lint checks - Maintain backward compatibility for external imports * Preserve public console imports The CLI package intentionally re-exports console helpers for compatibility, so __all__ must track that public surface instead of narrowing star imports to a partial set. Constraint: Existing tests import console helpers directly from specify_cli Rejected: Remove __all__ entirely | keeping an explicit export list documents the intended compatibility surface Confidence: high Scope-risk: narrow Directive: Keep __all__ synchronized when adding or removing specify_cli public re-exports Tested: uv run pytest tests/test_console_imports.py -q * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * style(cli): use explicit re-export syntax to fix ruff F401 warnings Use `X as X` form for BANNER, TAGLINE, and get_key imports to mark them as intentional public re-exports and silence ruff F401 lint errors. --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
cba00ab9a5 |
fix(kiro-cli): replace literal $ARGUMENTS with prose fallback (#2482)
* fix(kiro-cli): replace literal $ARGUMENTS with prose fallback Kiro CLI file-based prompts do not natively substitute any argument placeholder (kirodotdev/Kiro#4141, kiro.dev/docs/cli manage-prompts), so the literal "$ARGUMENTS" set in KiroCliIntegration.registrar_config["args"] reached the model verbatim and broke the prompt — every parameterized SpecKit command under Kiro CLI was unusable. Replace the placeholder with a prose fallback that instructs the model to take its argument from the user's next message, mirroring the convention used by other integrations whose target CLI lacks native argument injection. Add two regression tests in TestKiroCliIntegration: - test_rendered_prompts_do_not_contain_raw_arguments - test_rendered_prompts_contain_kiro_arg_placeholder and override the inherited test_registrar_config so it does not require args == "$ARGUMENTS". Fixes #1926 * test(kiro-cli): tighten args regression guard + document quirk Address review feedback on PR #2482. Two changes that bracket the original bug fix from both sides — code AND documentation: 1. Test layer (Copilot finding at lines 27, 56) The previous test_registrar_config asserted only that args != "$ARGUMENTS" and that args is truthy. That would silently pass if a future change swapped $ARGUMENTS for $INPUT, {{userMessage}}, <args>, or any other unsubstituted placeholder syntax — defeating the regression guard for issue #1926. Replace with a dual-layer guard: - test_registrar_config_args_is_exact_prose_fallback pins args to the imported _KIRO_ARG_FALLBACK constant. Wording drift now requires a deliberate paired commit (production constant + test). - test_registrar_config_args_does_not_look_like_a_placeholder_token is an independent regression guard built on a 7-pattern regex set covering Bash ($X, ${X}, ${X:-default}), Mustache/Handlebars/Jinja ({{X}}, {{{X}}}), Liquid/Jinja control ({% %}), Python str.format / .NET ({0}, {var}), angle-bracket (<X>), and Windows (%X%). Patterns are anchored to the full string so legitimate prose mentioning a placeholder ("the {{magic}} of placeholders") is not flagged. Also fix the line-56 tautology by importing _KIRO_ARG_FALLBACK directly into test_rendered_prompts_contain_kiro_arg_placeholder, instead of reading the constant back from registrar_config["args"]. The test now verifies the FALLBACK STRING reaches the rendered output, independent of the integration's own config staying correct. 2. Docs layer (mnriem CHANGES_REQUESTED) The Kiro CLI row in docs/reference/integrations.md only documented its alias. Update the notes column to lead with the limitation — Kiro CLI does not substitute $ARGUMENTS in file-based prompts, so Spec Kit ships a prose fallback at render time — with inline links to upstream Kiro "Manage prompts" docs and issue #1926. Style follows the Pi row ("limitation first, alias preserved at end"). Refs #1926 |
||
|
|
bf47e89249 |
test(presets): silence expected UserWarnings in self-test composition… (#2373)
* test(presets): silence expected UserWarnings in self-test composition tests
The self-test preset that ships with the repo provides a wrap-strategy
command (speckit.wrap-test) intentionally without a corresponding core
base layer, exercising the 'no base layer' branch of
_reconcile_composed_commands().
Eighteen tests across TestSelfTestPreset and TestPresetSkills install
this preset and trigger an expected UserWarning. Running the suite with
-W error::UserWarning surfaces them as test noise that could obscure
unrelated warnings.
Add class-level pytest.mark.filterwarnings filters to acknowledge the
two known messages ('Cannot compose command speckit.wrap-test' and
'Post-install reconciliation failed for self-test') so other UserWarning
sources still propagate normally.
Fixes #2363
* test(presets): scope filterwarnings to UserWarning category
Address Copilot review on #2373: the previous filterwarnings entries
omitted the warning category, so any warning class with a matching
message would have been silenced. Append :UserWarning to the four
filters so only the deliberately-emitted UserWarnings from
_reconcile_composed_commands() are ignored.
* test(presets): narrow self-test warning filter to install helper only
Address Copilot feedback: the class-level @pytest.mark.filterwarnings on
TestPresetSkills was too broad. The 'Post-install reconciliation failed'
filter could mask real reconciliation regressions, since that warning is
only emitted when _reconcile_composed_commands/_reconcile_skills raises.
Tests in TestPresetSkills already call install_self_test_preset(), which
scopes a narrow filter to the expected wrap-strategy 'Cannot compose'
warning. The class-level filters are redundant for those calls and unsafe
elsewhere, so they are removed.
* test(presets): align TestSelfTestPreset docstring with helper-based filtering
Address Copilot feedback: docstring referred to 'filters above', but the
fix uses warnings.filterwarnings inside install_self_test_preset rather
than class-level decorators. Updated the docstring to describe the actual
mechanism.
* test(presets): remove extra blank line between helper and class (PEP 8)
Address Copilot feedback: PEP 8 expects two blank lines between top-level
definitions; reduce the three blank lines between install_self_test_preset
and TestSelfTestPreset to two.
|
||
|
|
947b4398c7 |
fix(integration): refresh shared infra on integration switch (#2375)
* fix(integration): refresh shared infra on integration switch * fix(integration): address Copilot review on switch shared-infra refresh - Clarify install_shared_infra docstring: force overwrites regular files but always preserves symlinks (safe-destination check refuses to follow). - Print refresh_hint only for preserved_user_files; skipped_files keeps the generic remediation. Avoids misleading guidance when files were merely skipped (not detected as customized). - Catch ValueError from the safe-destination check and bucket the path under a new symlinked_files warning instead of aborting the switch. - Restore templates/constitution-template.md to upstream (drop accidental leading blank lines). * fix(integration): narrow symlink bucketing to dedicated exception Address Copilot feedback on shared_infra.py:305 — _safe_dest_or_bucket caught any ValueError as 'symlinked', which masked genuine safety errors (path escape, parent-not-a-directory). - Introduce SymlinkedSharedPathError(ValueError) raised only by the symlink-specific branches in _ensure_safe_shared_*(). - _safe_dest_or_bucket() now catches only SymlinkedSharedPathError; other ValueErrors propagate so the operation aborts with the real cause instead of being silently bucketed. - Wrap top-level dest_scripts/dest_variant/dest_templates mkdir calls in the same bucket helper so a symlinked .specify/scripts or .specify/templates is preserved with a warning rather than aborting the switch (matches the documented 'preserve customizations' behavior). - Update tests to expect the new bucket+warn behavior for leaf-level symlinked destinations. * fix(integration): tailor shared-infra warnings and rename preflight test Address Copilot review on PR #2375: - skipped_files hint now uses refresh_hint when refresh_managed=True so integration switch suggests --refresh-shared-infra instead of the generic init/upgrade flags. - symlinked-files warning header says "path(s)" rather than "file(s)" since symlinked directories (e.g. .specify/scripts/bash) are also bucketed there. - Rename test_shared_infra_install_preflights_before_writing to test_shared_infra_install_buckets_unsafe_destinations_and_continues to match the new bucket-and-continue semantics. * test: rename symlink bucketing tests to reflect bucket-and-continue behavior The two file-bucketing tests at line 300/320 were named *_refuses_*, but the new behavior buckets symlinked file destinations with a warning while safe destinations in the same install still complete. Rename to *_buckets_* and update docstrings to match. The remaining *_refuses_* tests (line 342/362/381) genuinely raise on symlinked dirs/manifests and keep their names. --------- Co-authored-by: Quratulain-bilal <quratulain.bilal@users.noreply.github.com> |
||
|
|
f0998348be |
feat: Config-driven opt-in authentication registry with multi-platform support (#2393)
* Initial plan * feat: add authentication provider registry (GitHub + Azure DevOps) Agent-Logs-Url: https://github.com/github/spec-kit/sessions/da7ecfd0-e1c9-48dc-b692-27be0879e976 Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * feat: add try-each-provider HTTP helper and wire all catalog fetches through auth registry - Add authentication/http.py with open_url() that tries each configured provider in registry order, falling through on 401/403 to the next, and finally to unauthenticated - Add build_request() for one-shot request construction - Add configured_providers() to registry __init__ - Remove api_base_url() from AuthProvider ABC (unused) - Remove hosts attribute from providers (no host matching) - Replace _github_http.py usage in ExtensionCatalog and PresetCatalog - Wire IntegrationCatalog and WorkflowCatalog through open_url (were unauthenticated) - Wire _fetch_latest_release_tag() through open_url - Wire all inline --from-url downloads through open_url - Fix unused stub variable flagged by code-quality bot - 49 auth tests (positive + negative), 1805 total tests passing * fix: address review — fix stale docstrings, restore Accept header, add extra_headers to open_url - Fix _open_url() docstrings in extensions.py and presets.py that incorrectly claimed redirect stripping behavior - Add extra_headers parameter to open_url() so callers can pass additional headers (e.g. Accept) that persist across retries - Restore Accept: application/vnd.github+json header in _fetch_latest_release_tag() via extra_headers * feat: config-driven opt-in auth via ~/.specify/auth.json Security-first redesign: no credentials are sent unless the user explicitly creates ~/.specify/auth.json mapping hosts to providers. - Add authentication/config.py: loads and validates auth.json with host-to-provider mappings, supports token/token_env/azure-ad/azure-cli - Refactor AuthProvider ABC: auth_headers(token, scheme) + resolve_token(entry) - Refactor GitHubAuth: bearer scheme only, token from config entry - Refactor AzureDevOpsAuth: 4 schemes (basic-pat, bearer, azure-cli, azure-ad) with dynamic token acquisition for azure-cli and azure-ad - Rewrite authentication/http.py: host matching, redirect stripping, provider fallthrough on 401/403, unauthenticated fallback - Add docs/reference/authentication.md with full reference and template - 1823 tests passing (67 auth-specific) * fix: address review — unused imports, host normalization, provider+scheme validation, security hardening - Remove unused imports (os, field, Any) in config.py - Normalize hosts during load (strip + lowercase) - Validate token/token_env are non-empty strings during load - Validate provider+scheme compatibility during load - Fix extra_headers order: auth headers applied last, cannot be overridden - Remove unused 'tried' variable in http.py - Warn (once) on malformed auth.json instead of silent fallback - URL-encode OAuth2 client credentials body in azure_devops.py - Update 403 message to mention auth.json configuration - Fix registry leak in test_register_duplicate (try/finally) - Fix import style consistency in test_authentication.py - Add azure-cli and azure-ad token acquisition tests (mock subprocess/urlopen) - Add autouse fixture to isolate upgrade tests from real auth.json - 1829 tests passing * fix: reject unknown providers, validate azure-ad fields, strip Authorization from extra_headers - Reject unknown provider keys during auth.json load with clear error message - Validate azure-ad tenant_id/client_id/client_secret_env as non-empty strings - Strip Authorization from extra_headers in both build_request and open_url to prevent accidental or intentional bypass of provider-configured auth - Add tests for unknown provider and incompatible scheme validation - 1831 tests passing * fix: extract shared auth test helpers, global config isolation, align docstring - Move _inject_github_config / make_github_auth_entry to tests/auth_helpers.py to eliminate duplication across test_extensions, test_presets, test_upgrade - Move auth config isolation fixture to global conftest.py (autouse) so ALL tests are isolated from ~/.specify/auth.json, not just test_upgrade - Align load_auth_config docstring with actual behavior: ValueError may be caught by higher-level HTTP helpers that warn and continue unauthenticated - 1831 tests passing * fix: preserve auth header across multi-hop redirect chains - Read Authorization from both headers and unredirected_hdrs in _StripAuthOnRedirect to survive multi-hop chains within allowed hosts - Add test_multi_hop_redirect_within_hosts_preserves_auth - 1832 tests passing * fix: use resolved config path in warning/error messages and patch build_opener in no-network test Agent-Logs-Url: https://github.com/github/spec-kit/sessions/86df9557-54f1-4fe4-a25f-9501cb2356cf Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * fix: assert full resolved config path in rate-limit output test Agent-Logs-Url: https://github.com/github/spec-kit/sessions/86df9557-54f1-4fe4-a25f-9501cb2356cf Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * fix: close HTTPError on 401/403, remove _VALID_AUTH_SCHEMES, catch TimeoutExpired, skip POSIX test on Windows, remove unused import Agent-Logs-Url: https://github.com/github/spec-kit/sessions/a1e29737-dd6e-4287-96c1-509e0c96fb21 Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * fix: use stable ~/.specify/auth.json in rate-limit message, skip POSIX permission check on Windows Agent-Logs-Url: https://github.com/github/spec-kit/sessions/4636bcdb-87ae-45d6-9545-a40e4effd617 Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * fix: validate host patterns, cache auth config per-process Agent-Logs-Url: https://github.com/github/spec-kit/sessions/889b58a7-7f8c-47e2-8056-931ebcc671cc Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * fix: clarify _is_valid_host_pattern docstring, clean up test sentinel type Agent-Logs-Url: https://github.com/github/spec-kit/sessions/889b58a7-7f8c-47e2-8056-931ebcc671cc Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * fix: improve _is_valid_host_pattern docstring and test observability Agent-Logs-Url: https://github.com/github/spec-kit/sessions/889b58a7-7f8c-47e2-8056-931ebcc671cc Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
cd44dc2147 | fix(goose): Declare args parameter in generated recipes (#2402) | ||
|
|
f5b675e9ee |
feat: Add lingma support (#2348)
* add lingma support * fix * fix context file * Update CONTEXT_FILE path in test integration * fix IntegrationOption.default * fix IntegrationOption.defaultfix * fix: address Copilot review feedback - Add blank line after __future__ import (PEP 8) - Remove trailing whitespace at end of lingma/__init__.py - Bump integrations/catalog.json updated_at timestamp - Add Lingma to supported agent list in README.md * fix: address Copilot review feedback (round 4) - Reword module docstring: Lingma is a brand-new skills-only integration with no prior command-mode history, so 'deprecated since v0.5.1' wording (copied from Trae) was misleading - Remove Lingma from README CLI-tool check list: Lingma is IDE-based (requires_cli=False) and is explicitly skipped by specify init / specify check tool detection |
||
|
|
2d5e63005d |
fix: default non-interactive init to copilot integration (#2414)
* fix: default non-interactive init integration * chore: clarify non-interactive init default integration * Address non-interactive init review feedback * Fix interactive init test after fallback |
||
|
|
793632089a |
fix(forge): use hyphen notation for command refs in Forge integration (#2462)
* fix(forge): use hyphen notation for command refs in Forge integration - Add invoke_separator = "-" class attribute to ForgeIntegration so effective_invoke_separator() returns "-" for shared-template installs - Add "invoke_separator": "-" to ForgeIntegration.registrar_config so agents.py CommandRegistrar can resolve refs with the correct separator - Pass invoke_separator to process_template() in ForgeIntegration.setup() so all .forge/commands/*.md bodies use /speckit-foo notation - Replace literal /speckit.specify with __SPECKIT_COMMAND_SPECIFY__ in extensions/git/commands/speckit.git.feature.md so every agent resolves the reference through its own separator - Apply resolve_command_refs re.sub in agents.py register_commands() after argument-placeholder substitution so extension commands registered for Forge get /speckit-foo refs; all other agents continue to get /speckit.foo Fixes ZSH compatibility: dot-notation command invocations (/speckit.specify) are misinterpreted by ZSH as file-path operations; hyphen notation (/speckit-specify) works correctly in all shells. * fix(agents): propagate invoke_separator from integration class into AGENT_CONFIGS Skills-based agents (claude, codex, kimi, …) inherit invoke_separator="-" from SkillsIntegration but do not repeat it in their registrar_config dicts. _build_agent_configs() was copying registrar_config verbatim, so register_commands() fell back to "." when resolving __SPECKIT_COMMAND_*__ tokens for those agents — emitting /speckit.specify instead of the correct /speckit-specify for extension commands like speckit.git.feature. Fix: after copying registrar_config, inject invoke_separator from the integration's class attribute when it is not already declared explicitly. This makes the integration class the single source of truth for all agents, without requiring each SkillsIntegration subclass to duplicate the field. Also replace the inline re.sub in register_commands() with a call to IntegrationBase.resolve_command_refs() (deferred import to avoid the existing circular dependency) so token-resolution logic is not duplicated. Adds two tests in test_agent_config_consistency.py: - test_skills_agents_have_hyphen_invoke_separator_in_agent_configs: asserts every /SKILL.md agent has invoke_separator="-" in AGENT_CONFIGS. - test_skills_agent_command_token_resolves_with_hyphen: end-to-end check via CommandRegistrar that the git extension's speckit.git.feature command is installed for Claude with /speckit-specify (not /speckit.specify). Addresses review comment on PR #2462. |
||
|
|
b4060d5620 |
Load constitution context in /speckit.implement to enforce governance during implementation (#2460)
* Initial plan * fix implement command to load constitution context Agent-Logs-Url: https://github.com/github/spec-kit/sessions/05663d9d-149b-4c13-a22d-2552b3fa619c Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
30e6fa9e32 |
fix: validate URL scheme in build_github_request (#2449)
* fix: validate URL scheme in build_github_request * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * test: add missing hostname validation test for build_github_request * fix: update docstring and fix import grouping per Copilot feedback * fix: sort imports and simplify url validation in build_github_request --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
a7201c183e | fix(workflows): require project for catalog list (#2436) | ||
|
|
259494a328 |
fix: honor template overrides for tasks-template (#2278) (#2292)
* fix: honor template overrides for tasks-template (#2278) - Add scripts/bash/setup-tasks.sh mirroring setup-plan.sh pattern - Add scripts/powershell/setup-tasks.ps1 mirroring setup-plan.ps1 pattern - Update tasks.md frontmatter to use dedicated setup-tasks scripts - Resolve tasks template via override stack and emit path as TASKS_TEMPLATE in JSON output - Reference resolved TASKS_TEMPLATE path in generate step instead of hardcoded path * fix: remove stray EOF tokens from setup-tasks scripts * fix: improve error messages for unresolved tasks-template * test: update file inventory tests to include setup-tasks scripts * fix: use Console::Error.WriteLine instead of Write-Error in setup-tasks.ps1 * fix: write prerequisite error messages to stderr in setup-tasks.ps1 * fix: validate tasks template is a file and normalize path in setup-tasks.ps1 * fix: improve tasks-template error message to mention full override stack * test: add setup-tasks.sh to TestCopilotSkillsMode file inventory * fix: skip feature-branch validation when feature.json pins FEATURE_DIR * fix: correct override path in tasks-template error messages * test: add integration tests for setup-tasks template resolution and branch validation * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * fix: correct fixture paths and add spec.md prerequisite checks * fix: use correct .registry schema in preset priority test * fix: remove stale aaa-preset block and duplicate comment in preset priority test * fix: align preset directory names with registry IDs in priority test --------- Co-authored-by: Nimraakram22 <nimra.akram123451@gmail.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |