mirror of
https://github.com/github/spec-kit.git
synced 2026-07-03 12:28:06 +08:00
bba473c223dc298fb76ecf8f87f86d5d2b8ed11c
1305 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
bba473c223 |
fix(integrations): cursor-agent honors executable/extra-args env overrides (#3265)
* fix(integrations): cursor-agent ignores executable/extra-args env overrides cursor-agent's build_exec_args() hardcoded self.key as argv[0] and never called _apply_extra_args_env_var(), so the documented SPECKIT_INTEGRATION_CURSOR_AGENT_EXECUTABLE (issue #2596) and SPECKIT_INTEGRATION_CURSOR_AGENT_EXTRA_ARGS (issue #2595) hooks were silently dropped — unlike every other CLI-dispatch integration (codex, devin). Route argv[0] through _resolve_executable() and apply the extra-args hook after the mandatory headless flags, mirroring the twins. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(integrations): pin extra-args insertion order for cursor-agent Per Copilot feedback: the extra-args override test only asserted the injected tokens were present, not that they land before Spec Kit's canonical --model / --output-format flags. Exercise build_exec_args with both a model and JSON output and assert the extra args are inserted before --model / --output-format (and the canonical flags stay intact and paired). Verified this fails if the _apply_extra_args_env_var call is moved after the flag extends. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
288bd679f3 |
docs: drop stale kimi KIMI.md->AGENTS.md migration note (#3291)
* docs: drop stale kimi KIMI.md->AGENTS.md migration note #3097 made the agent-context extension a full opt-in and removed the KIMI.md -> AGENTS.md context migration from the kimi integration (_migrate_legacy_kimi_context_file and the context_file handling are gone). kimi's --migrate-legacy now only moves the skills directory. two lines in the integrations reference still promised the removed context migration; drop that clause so the docs match the code. * docs: clarify kimi legacy migration is skill naming, not directory names address review: the parenthetical said 'dotted->hyphenated directory names', but the migration is about skill naming (speckit.xxx -> speckit-xxx), matching the module docstring. reword to match. |
||
|
|
9bd3512025 |
chore: release 0.12.4, begin 0.12.5.dev0 development (#3305)
* chore: bump version to 0.12.4 * chore: begin 0.12.5.dev0 development --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
bbe86310ca |
feat(cli): add py script type & Python interpreter resolution (#3278) (#3285)
* feat(cli): add `py` script type & Python interpreter resolution (#3278) Introduce a third script variant alongside `sh`/`ps` as the foundation for unifying workflow scripts under a single Python implementation. - Add `"py": "Python"` to `SCRIPT_TYPE_CHOICES`; `VALID_SCRIPT_TYPES` consumers (init workflow step, init command, _helpers) pick it up automatically since they derive from that mapping. - Add `IntegrationBase.resolve_python_interpreter()` (project venv → `python3` → `python`, falling back to `python3`). - Prefix the resolved interpreter when `process_template()` expands `{SCRIPT}` for the `py` script type so `.py` scripts run portably (notably on Windows); thread `project_root` through callers so venv preference works. - Make `install_scripts()` mark copied `.py` files executable too. Includes positive and negative unit tests for interpreter resolution, `py` template processing, the new choice, and script installation. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(cli): return repo-relative venv interpreter & correct docstring Address PR review feedback on #3285: - `resolve_python_interpreter()` now returns the venv interpreter as a path relative to the project root (`.venv/bin/python` / `.venv/Scripts/python.exe`) instead of an absolute/joined path, so the generated `{SCRIPT}` invocation stays portable and runnable from the repo root regardless of where the project lives. - Update `install_scripts()` docstring to note `.py` scripts are now made executable alongside `.sh`. - Update tests to assert the repo-relative interpreter path. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(cli): fall back to sys.executable for interpreter resolution When neither python3 nor python is discoverable on PATH (and no project venv is found), resolve_python_interpreter() now returns the running interpreter (sys.executable) so the generated {SCRIPT} invocation works in the current environment, falling back to "python3" only if that is also unavailable. Update unit tests accordingly. * fix(cli): quote py interpreter path when it contains whitespace For the `py` script type, the resolved interpreter may be an absolute path containing spaces (notably `sys.executable` under Windows `Program Files`). Quote it when it contains whitespace so the `{SCRIPT}` invocation isn't split into multiple arguments. Add positive/negative tests for the quoting behavior. * test: guard executable-bit assertions from Windows chmod semantics The Windows CI job failed because `os.chmod` does not set POSIX executable bits on Windows, so `install_scripts()` cannot make `.py`/ `.sh` files executable there (nor is it needed — the interpreter is invoked explicitly). Split the install_scripts test so file-copy behavior is still verified cross-platform, and skip the executable-bit assertions on win32 (matching the repo's existing pattern). --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
3b30e40aaa |
fix: resolve GitHub release asset API URL for private repo bundle downloads (#3136)
* fix: resolve GitHub release asset API URL for private repo bundle downloads For private/SSO-protected GitHub repos, browser release download URLs (https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>) redirect to an HTML/SSO page instead of delivering the asset, causing bundle manifest downloads to fail. Extends the pattern from #2855 (presets/workflows) to cover the bundle manifest download path in _download_remote_manifest: - Resolves browser release URLs to GitHub REST API asset URLs via resolve_github_release_asset_api_url before downloading - Direct REST API asset URLs (api.github.com/repos/.../releases/assets/<id>) are passed through directly - Both cases use Accept: application/octet-stream so the API returns the binary payload rather than JSON metadata - The original catalog URL is used to determine artifact format (.zip vs YAML) since the resolved API URL does not carry the file extension Adds two CLI-level contract tests: - bundle info resolves browser release URL via GitHub tags API - bundle info passes direct API asset URL through with octet-stream Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: detect ZIP payload by magic bytes; add zip and API-asset tests Address Copilot review feedback on PR #3136: 1. Detect ZIP payloads by magic bytes (PK\x03\x04) in addition to the '.zip' URL suffix so that direct GitHub REST asset URLs — which carry no file extension — are correctly routed through the ZIP extraction path when the asset is a ZIP bundle artifact. 2. Add two new contract tests: - test_bundle_info_resolves_github_browser_release_url_zip: exercises the '.zip' browser release URL path end-to-end, verifying the tags API lookup fires, octet-stream header is used, and bundle.yml is successfully extracted from the ZIP payload. - test_bundle_info_api_asset_url_zip_detected_by_magic_bytes: verifies that a direct REST asset URL returning ZIP bytes is detected by magic and parsed correctly without a tags API call. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: improve error message, broaden ZIP magic, drop unused tmp_path Address second-round Copilot review feedback on PR #3136: - Error message: when the download fails, report the original catalog download_url so the user knows which entry to fix; include the resolved REST API URL when it differs for easier debugging. - ZIP detection: broaden the magic-bytes check from PK\x03\x04 to raw[:2] == b"PK", covering all valid ZIP variants (local-file header PK\x03\x04, empty-archive PK\x05\x06, spanned/split PK\x07\x08). - Tests: remove the unused tmp_path parameter from test_bundle_info_resolves_github_browser_release_url_zip. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: use full 4-byte ZIP signatures instead of 2-byte PK prefix Address Copilot feedback: raw[:2] == b"PK" is too broad and could misclassify any payload starting with ASCII "PK" as a ZIP, producing a confusing "not a valid bundle" error. Use the three specific 4-byte ZIP magic signatures instead: PK\x03\x04 — local file header (standard ZIP) PK\x05\x06 — end-of-central-directory (empty archive) PK\x07\x08 — data descriptor / spanning marker Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: harden _download_remote_manifest parsing and tighten tests - Promote _ZIP_SIGNATURES to module-level constant (was redefined per call) - Use PurePosixPath for URL path suffix extraction so query strings and fragments are ignored and URL paths are treated as POSIX on all OSes - Move yaml/BundleManifest imports to function top to flatten the previously nested try/except into a single handler with explicit except _yaml.YAMLError and except Exception clauses - Re-add None guard on _local_manifest_source return: the function is typed Optional[BundleManifest] and without the guard a None return propagates silently to callers that degrade gracefully rather than raising an actionable error; comment explains it is defensive not dead - Assert exact resolved asset URL in browser-URL download tests, not just the Accept header, so a regression where download uses the original URL instead of the resolved one would be caught - Add resolution-failure test: when tags API finds no matching asset the code falls back to the original URL and exits non-zero with Error: Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(bundle): pass github_provider_hosts() for GHES private release downloads Extends the GHES support pattern from extensions and presets (#2855, #3157) to the bundle manifest download path: resolve_github_release_asset_api_url now receives github_hosts=github_provider_hosts() so browser release URLs from GitHub Enterprise Server instances are resolved via /api/v3 rather than falling back to the unauthenticated download path. Also adds a contract test covering the GHES resolution path for _download_remote_manifest (analogous to the existing github.com tests). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(bundle): remove unused ghes_entry variable from GHES contract test The dict was defined but never consumed — the test drives GHES host recognition entirely through the github_provider_hosts() patch. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(bundle): include source URL in remote manifest parse errors Thread the catalog URL (and resolved API URL when it differs) into the YAML parse, generic parse, and ZIP-extraction error paths of _download_remote_manifest so failures point at the offending source instead of an opaque temp path. Addresses PR review feedback. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
6288dea6ae |
[extension] Add Analytics extension to community catalog (#3296)
* Add Analytics extension to community catalog Add analytics extension submitted by @Huljo to: - extensions/catalog.community.json (alphabetical order) - docs/community/extensions.md community extensions table Closes #3288 Assisted-by: GitHub Copilot (model: claude-sonnet-4.6, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix empty changelog field for analytics extension Set the analytics extension changelog to the GitHub releases page instead of an empty string, which the catalog treats as a URI when present and can fail schema validation and downstream tooling. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com> |
||
|
|
5b682b2cb3 |
fix: interpolate multi-expression templates instead of returning None (#3208) (#3228)
* fix: interpolate multi-expression templates instead of returning None (#3208) `evaluate_expression` returned None for templates containing two or more `{{ }}` blocks with no surrounding literal text, e.g. `"{{ context.run_id }} {{ inputs.issue }}"`. The single-expression fast path used `_EXPR_PATTERN.fullmatch()`, but `fullmatch` defeats the pattern's non-greedy `(.+?)` body: for two adjacent expressions it still matches, capturing everything between the first `{{` and the last `}}` (`"context.run_id }} {{ inputs.issue"`) as the body. That garbage failed dot-path resolution and returned None directly, bypassing the `sub()` interpolation path that would have resolved each expression. Downstream this surfaced as the literal string "None" reaching commands. Guard the fast path on `stripped.count("{{") == 1` so only genuine single-expression templates take the typed return; multi-expression templates fall through to `sub()` and interpolate correctly. Add regression tests for two expressions separated by a space and for adjacent expressions with no separator. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.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(expressions): use match-span guard so single expressions with literal {{ keep their type The previous `stripped.count("{{") == 1` guard misclassified a genuine single expression whose string argument contains a literal `{{` (e.g. `{{ inputs.text | contains('{{') }}`) as multi-expression, routing it through `sub()` interpolation and coercing the typed (bool/int/list) return value to a string -- breaking the type-preservation the docstring promises (Copilot review on #3228). Anchor a single match at the start and require it to consume the whole stripped string instead. The non-greedy body stops at the first `}}`, so a two-block template fails the span check (falls through to interpolation, fixing #3208) while a lone expression -- including one with a `{{` inside a string literal -- matches to the end and keeps its typed value. Add a regression test for the literal-brace single-expression case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * fix(expressions): detect single expression with quote-aware scan The match-span guard using the non-greedy _EXPR_PATTERN stopped at the first `}}`, so a lone expression whose string argument contains a literal `}}` (e.g. `{{ inputs.text | contains('}}') }}`) was misclassified as multi-expression and mis-parsed by the interpolation path, raising ValueError and turning CI red (Copilot review on #3228). Replace the span check with `_is_single_expression`, which scans the `{{ ... }}` body for a block-closing `}}` outside string literals (mirrors the quote handling already in `_split_top_level_commas`). A genuine two-block template closes early and falls through to interpolation (fixing #3208); a lone expression with a literal `{{` or `}}` inside a string argument keeps its typed return value. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
490566847c |
feat(cli): honor SPECIFY_INIT_DIR in the specify CLI project resolver (#3186)
* feat(cli): honor SPECIFY_INIT_DIR in the specify CLI project resolver The shell resolver honors SPECIFY_INIT_DIR (#2892), but the Python CLI did not: it resolved the project as Path.cwd() + a .specify/ check and never read the override. So setup-plan.sh respected it while `specify integration install` ignored it, and you still had to cd into the member project. Route project resolution through a shared _resolve_init_dir_override() that applies the shell resolver's validation rules (relative to cwd, must exist and contain .specify/, hard error, no fallback, same error strings). It's wired into _require_specify_project() — the chokepoint for every project-scoped subcommand (integration/extension/workflow/preset/...) — and the `workflow run <file>` standalone path, which re-applies its symlinked-.specify guard on the override branch too. init is unchanged: it creates .specify/, so the must-pre-exist rule doesn't apply. The resolver canonicalizes symlinks via Path.resolve() while the shell keeps the logical path; they agree for non-symlinked paths (documented in the resolver). Tests in tests/test_init_dir_cli.py mirror the strict cases from test_init_dir.py through the CLI; conftest now strips SPECIFY_* for the whole suite so a stray export can't perturb the now-env-reading resolver. Docs note the CLI applies the same rules. Discussion: github/spec-kit#2834 (Disclosure: I used an AI coding agent to audit the call sites and resolver, draft the change, and run an adversarial code review; reviewed by me.) * fix(cli): honor SPECIFY_INIT_DIR for bundle commands Assisted-by: Codex (model: GPT-5, autonomous) * fix(bundler): refuse symlinked .specify on the SPECIFY_INIT_DIR override path find_project_root refuses a symlinked .specify (following it could read/write outside the tree, and a test pins that), but the SPECIFY_INIT_DIR override added for bundle commands returned early and skipped that guard: _resolve_init_dir_override validates .specify with is_dir(), which follows symlinks. So `specify bundle` accepted via the override a layout the cwd path rejects. Re-check the override result with the same guard, plus a regression test. (Disclosure: found via an AI code review and fixed with an AI coding agent; reviewed by me.) * fix(cli): keep SPECIFY_INIT_DIR strict for bundles Treat an explicit symlinked SPECIFY_INIT_DIR project as a hard bundle error instead of returning no project, which could initialize the current directory. Align the docs with the actual unset resolver behavior. Assisted-by: Codex (model: GPT-5, autonomous) * docs(core): note symlinked .specify handling differs across CLI surfaces A symlinked .specify is followed by integration/extension/workflow (matching the shell resolver) but refused by bundle and workflow run <file> (write confinement). Document the asymmetry so it reads as intentional. (Disclosure: AI-assisted; reviewed by me.) * docs(core): reframe symlinked .specify note around the override invariant Per maintainer feedback on #3186: SPECIFY_INIT_DIR relocates where the project is, not how a surface treats symlinks. Each surface keeps its cwd-path stance (write surfaces refuse a symlinked .specify, read/config surfaces follow it), so the split is one policy relocated, not an inconsistency. * docs: address Copilot review on resolver docstrings - _project.py: the error messages "mirror" the shell wording rather than "match" it (the CLI renders a Rich `Error:` line, the shell a plain `ERROR:`). - find_project_root: document that honoring SPECIFY_INIT_DIR when start is None can raise typer.Exit / BundlerError, so the Path | None signature isn't surprising to direct callers. * docs(bundler): note require_project_root inherits the override raise behavior find_project_root can raise typer.Exit / BundlerError under the SPECIFY_INIT_DIR override (start=None); require_project_root inherits that, so document it alongside its own BundlerError-on-missing-project. * docs: clarify symlinked project root behavior Assisted-by: OpenAI Codex (model: GPT-5, autonomous) * Address SPECIFY_INIT_DIR review feedback Assisted-by: OpenAI Codex (model: GPT-5, autonomous) * Route workflow JSON errors to stderr Assisted-by: OpenAI Codex (model: GPT-5, autonomous) |
||
|
|
f59fd81608 |
fix(extensions): resolve core-command dirs via _assets helpers (#3274) (#3287)
`_load_core_command_names()` computed its candidate command dirs with bespoke `Path(__file__)` arithmetic. The #3014 move of this module from `specify_cli/extensions.py` to `specify_cli/extensions/__init__.py` pushed the file one directory deeper but left the `.parent` counts unchanged, so both candidates resolved to non-existent paths: wheel -> specify_cli/extensions/core_pack/commands (real: specify_cli/core_pack/commands) source -> src/templates/commands (real: repo-root templates/commands) Neither exists, so every call silently fell through to `_FALLBACK_CORE_COMMAND_NAMES`. Discovery is latent-dead: the fallback happens to equal the real stems today, but the shadowing guard (#1994) that depends on it now relies on someone hand-editing the fallback on every core-command add/remove (as already happened for `converge`, #3001). Delegate path resolution to the canonical `_locate_core_pack` / `_repo_root` resolvers in `_assets` — the same ones the presets and bundle loaders use. They are anchored to the package root, so discovery survives future module moves. Add regression tests that point the resolvers at a temp tree with *different* command names, proving discovery reads from disk rather than returning the fallback (they fail on the pre-fix code). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
1849543611 |
fix: fall back to feature dir basename for empty CURRENT_BRANCH (#3026) (#3229)
* fix: fall back to feature dir basename for empty CURRENT_BRANCH (#3026) When a feature is resolved via SPECIFY_FEATURE_DIRECTORY or .specify/feature.json without SPECIFY_FEATURE set, get_current_branch() returns empty, so get_feature_paths / Get-FeaturePathsEnv emitted CURRENT_BRANCH= (empty) even though the feature directory was resolvable. Downstream scripts and agents that expect a non-empty identifier got misleading output. Fall back to the basename of the resolved feature directory when the branch is empty, in both the bash (`${feature_dir##*/}`) and PowerShell (`Split-Path -Leaf`) resolvers. An explicit SPECIFY_FEATURE still takes precedence, so this only fills the previously-empty case. Add bash + PowerShell regression tests: the basename fallback fires when SPECIFY_FEATURE is unset, and an explicit SPECIFY_FEATURE still overrides it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.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> * 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: address Copilot feedback — PS 5.1 compat + parametrize bash test - common.ps1: replace [System.IO.Path]::TrimEndingDirectorySeparator (a .NET Core-only method that throws MethodNotFound on Windows PowerShell 5.1 / .NET Framework) with a portable String.TrimEnd, so the trailing-slash trim actually works on 5.1. - tests: parametrize the bash fallback test to cover feature.json, SPECIFY_FEATURE_DIRECTORY, and the explicit SPECIFY_FEATURE override (mirrors the PowerShell test), folding in the old explicit-override test; add the missing blank line before the next test (PEP 8). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
c34a505d1c |
feat(bug-fix): add label-driven bug-fix agentic workflow (#3258)
* feat(bug-fix): add label-driven bug-fix agentic workflow Add a `bug-fix` gh-aw workflow as stage 2 of the assess -> fix -> test bug pipeline, mirroring the existing `bug-assess` stage. It triggers when a maintainer applies the `bug-fix` label, recovers the slug and remediation contract from the prior bug-assess assessment comment, applies the fix, and opens a draft pull request plus a summary comment for human review. The workflow is intentionally decoupled from Spec Kit specifics: it consumes the assessment from the issue comment rather than any `.specify/` files, so it is portable to other repositories running the matching bug-assess stage. - .github/workflows/bug-fix.md authored and compiled to bug-fix.lock.yml - Label-gated trigger (github.event.label.name == 'bug-fix') - Draft PR via create-pull-request safe-output; scoped permissions - Untrusted-input / URL-safety guardrails consistent with bug-assess - Maintainer remains the gatekeeper; no unattended automation Refs #3238 Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(bug-fix): tighten bash allowlist and block protected files Address Copilot review feedback on PR #3258: - Trim tools.bash to the inspect set plus a small test-runner set (pytest, npm, go, cargo, dotnet), dropping package-manager/build tools (pip, npx, pnpm, yarn, mvn, gradle, make, bundle, rake, ruby, node) to reduce blast radius under prompt injection. - Set create-pull-request.protected-files.policy: blocked so edits to sensitive files (dependency manifests, README/CHANGELOG/SECURITY, etc.) block PR creation, matching the stronger contract used by the other PR-creating workflows in this repo. Refs #3238 Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot App <223556219+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> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * fix(bug-fix): resync lock body_hash after review edits The Copilot autofix commits edited bug-fix.md (verdict phrasing, Assisted-by trailer) but did not recompile the lock, leaving body_hash stale. Since the workflow runs with strict integrity, the runtime-imported bug-fix.md must match the lock's recorded body_hash. Recompiled with gh-aw v0.79.8 (checkout pin kept at v7.0.0 to match sibling locks); the only change is the body_hash. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot App <223556219+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(bug-fix): align add-labels max to 1 and soften next-stage label reference Address two Copilot review findings: - add-labels.max: the authored frontmatter said max:1 but the committed lock enforced max:2 (stale from an earlier frontmatter), and Step 8 said 'max 2 labels total'. The workflow only ever applies ONE status label per run (fix-proposed | needs-reproduction | fix-blocked | needs-assessment), so 1 is the correct, tightest contract. Recompiled so the lock now enforces max:1, and reworded Step 8 to 'exactly one status label per run'. - bug-test label: Step 7 hard-coded applying a 'bug-test' label that does not exist in this repo. Since the workflow is portable, reworded to present the stage-3 bug-test workflow as the planned next stage 'if the repository has it configured' rather than assuming it exists. Recompiled with gh-aw v0.79.8; checkout pins kept at v7.0.0 to match sibling locks. No compile drift. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot App <223556219+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(bug-fix): set add-labels max to 1 consistently across source and lock A prior autofix flipped the authored frontmatter add-labels.max back to 2, re-introducing the mismatch: source said 2, the compiled lock enforced 1, and Step 8 prose says 'exactly one status label per run'. The workflow only ever applies a single status label per run (needs-assessment | needs-reproduction | fix-proposed | fix-blocked), so 1 is the correct, tightest contract and matches the compiled lock. Set the frontmatter to max:1 so source, lock, and prose all agree (also avoids the lock staleness guard failing on a frontmatter mismatch). Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(bug-fix): relax protected files and number bug-fix branches Address the two new Copilot review findings: - was still covering README.md and CHANGELOG.md, which can legitimately need updates as part of a prior bug remediation. Add them to the exclude list so the workflow can still open a PR when the assessment calls for documentation changes, matching the pattern used by add-community-extension. - The generated branch name used , but the repo convention for bug fixes requires so branches are traceable and aligned with AGENTS.md. Update the branch naming guidance to use . Recompiled with gh-aw v0.79.8; lock reflects the protected-files exclusion and keeps the v7.0.0 checkout pin fixups. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(bug-fix): accept workflow-authored assessment comments from bot/service accounts Address the open Copilot finding on assessment-author matching. The workflow previously required the prior assessment comment to be authored by `github-actions[bot]`. That is too strict for portable repos where bug-assess may post through a different bot/service account token. Updated Step 1 to select the most recent assessment comment that appears workflow-authored by combining: - bot/service-account authorship, and - expected bug-assess structure (assessment header plus remediation/files/tests sections). This keeps the spoof-resistance intent while removing dependence on one fixed login. Recompiled with gh-aw v0.79.8 and kept checkout v7.0.0 pin fixups. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(bug-fix): clarify local-check guardrails for dependency fetching Address Copilot feedback on Step 5 consistency around network-dependent checks. The workflow previously listed `go test ./...` and `cargo test` as examples while also forbidding network-dependent commands, which could be ambiguous on clean runners. Updated Step 5 to: - keep those commands as examples only when dependencies are already present - explicitly disallow dependency-fetch/install commands during verification (go mod download/go get/cargo fetch/npm|pnpm|yarn install) Recompiled with gh-aw v0.79.8 and kept checkout v7.0.0 pin fixups. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(bug-fix): make status label application conditional on label existence Address Copilot feedback about missing status labels causing runtime failures. The workflow previously instructed unconditional application of `needs-assessment`, `fix-blocked`, and `fix-proposed`. In repositories where those labels are not pre-created, `add_labels` fails and can break the run. Updated Steps 1/3/4/8 to require existence checks before adding those labels: - add the label only if it exists - otherwise skip labeling and explicitly note that in the comment This preserves the status-label UX when labels exist while keeping execution robust in repos that have not created every optional status label yet. Recompiled with gh-aw v0.79.8 and kept checkout v7.0.0 pin fixups. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) |
||
|
|
ac6eef4520 |
feat(workflows): add label-driven bug-test workflow (#3239) (#3257)
* feat(workflows): add label-driven bug-test workflow (#3239) Add the third stage (assess → fix → test) of the semi-automated, human-gated bug pipeline. The `bug-test` agentic workflow triggers when a maintainer applies the `bug-test` label, runs the relevant tests in isolation against the fix, compiles a readable pass/fail report, and posts it back as a single issue comment. - Locates the fix under test: linked PR → named fix branch → current checkout fallback, only ever from origin. - Stack-agnostic test detection (uv+pytest, npm/pnpm/yarn, go, make) so it is decoupled from Spec Kit specifics and reusable by other projects. - Runs tests under a timeout as untrusted code; scoped read-only permissions; same URL-safety / untrusted-input guardrails as bug-assess. - Verification mode compares a generated fix against the historical fix for old/closed bugs to surface discrepancies. - Optional single result label (tests-passing / tests-failing / tests-inconclusive). Compiled bug-test.lock.yml with `gh aw compile`. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(workflows): bump actions/checkout from 6.0.3 to 7.0.0 in bug-test workflow Align with repo standards (e.g. dependabot PR #3064, other workflows). Manually pinned in the compiled lock file for consistency. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> |
||
|
|
774a0222a3 |
chore: release 0.12.3, begin 0.12.4.dev0 development (#3295)
* chore: bump version to 0.12.3 * chore: begin 0.12.4.dev0 development --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
d982c2f67f |
feat(copilot): warn before skills default rollout (#3256)
* feat(copilot): default to skills mode * feat(copilot): warn before skills default rollout * Make Copilot skills warning test less brittle --------- Co-authored-by: root <kinsonnee@gmail.com> |
||
|
|
e8ade110da | Add June 2026 newsletter (#3289) | ||
|
|
876e532d76 |
docs(toc): add Bundles and Authentication to the Reference nav (#3267)
docs/reference/bundles.md and docs/reference/authentication.md exist on disk but were absent from the Reference section of docs/toc.yml, so both pages were orphaned and undiscoverable in the published docs sidebar. Add the two nav entries (Bundles after Workflows, matching the ordering in reference/overview.md; Authentication last). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
b4a0f8b564 |
fix(integrations): add zed to discovery catalog.json (#3266)
zed is registered, registrar-aligned and registry-tested, but it was the only one of the 34 integrations absent from integrations/catalog.json, making it undiscoverable through the discovery manifest. Add the missing 'zed' entry (mirroring the sibling skills entries) and a registry<->catalog parity regression test so a future integration can't silently drift. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
2d56dfd73d |
fix(integrations): cline hook note collapses onto instruction at EOF (#3263)
The hook-note injection regex matches the line terminator via (\r\n|\n|$), so the captured eol group is empty when the instruction is the final line of a file with no trailing newline. The cline integration emitted the note with that empty eol, mashing the note text and the instruction onto a single line. Default eol to '\n', matching the agy integration twin which already guards this case. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
810d6fcfe1 |
refactor: move workflow command handlers to workflows/_commands.py (PR-8/8) (#3159)
* refactor: move workflow command handlers to workflows/_commands.py (PR-8/8) Final PR of the __init__.py split. Moves the workflow command group out of __init__.py into the existing workflows/ package, completing the domain-dir layout established in PR-5 (integrations), PR-6 (presets) and PR-7 (extensions). - New workflows/_commands.py holds the four Typer apps (workflow / catalog / step / step-catalog), all 25 command handlers, the six workflow-only helpers (_parse_input_values, _workflow_run_payload, _emit_workflow_json, _stdout_to_stderr_when, _validate_step_id_or_exit, _resolve_steps_base_dir_or_exit), and a register(app) entry point. - workflows is already a package, so no rename is needed; intra-package imports change from `.workflows.x` to `.x`. The only root-helper dep (_require_specify_project) is reached through a call-time shim so test monkeypatching of specify_cli._require_specify_project keeps working. - __init__.py drops ~1445 lines (2066 -> 621); the workflow group is re-attached via register(app). Dead `contextlib` import removed. - tests/test_workflows.py: import the now-relocated _stdout_to_stderr_when helper from its new home (workflows._commands) instead of the package root. No behavior change. Full suite green (3847 passed), ruff clean. * Prevent workflow state writes through symlinked storage Workflow commands persist run state under .specify/workflows/runs, so the command-local project shim now rejects symlinked workflow storage before any workflow command proceeds. The standalone YAML path uses the same guard because it intentionally bypasses the normal project requirement while still creating workflow state under the current directory. Constraint: Local YAML workflow runs do not require an existing .specify project directory but still create .specify/workflows/runs state Rejected: Guard only .specify in the file-source path | .specify/workflows and runs can independently redirect writes Confidence: high Scope-risk: narrow Directive: Keep workflow storage symlink checks centralized before constructing WorkflowEngine Tested: .venv/bin/python -m pytest tests/test_workflow_run_without_project.py tests/test_workflows.py::TestWorkflowAddSymlinkGuard -v Tested: .venv/bin/python -m py_compile src/specify_cli/workflows/_commands.py tests/test_workflow_run_without_project.py tests/test_workflows.py Not-tested: Ruff lint; ruff is not installed in the repo virtualenv Assisted-by: OpenAI Codex (model: GPT-5, autonomous) * fix(workflows): pass github_hosts allowlist to GHES release asset resolver workflow add resolved GitHub release download URLs without forwarding the github_provider_hosts() allowlist, so resolve_github_release_asset_api_url never treated any host as GHES. This regressed GitHub Enterprise Server release asset resolution and diverged from presets/extensions, which already pass github_hosts. Forward github_provider_hosts() at both the direct-URL and catalog install call sites. The allowlist remains the anti-SSRF gate. * fix(workflows): reject symlinked/traversal <id> dir on workflow install Local/URL and catalog installs wrote to .specify/workflows/<id>/workflow.yml without guarding the <id> segment. A pre-planted symlink at <id> or <id>/workflow.yml let mkdir+copy/download follow it and write outside the project root; a non-directory <id> made mkdir raise unhandled. Add _safe_workflow_id_dir() to reject path traversal, symlinked or non-directory <id>, and a symlinked workflow.yml leaf before any write. Fold the catalog branch's existing traversal check into the helper. * fix(workflows): harden _safe_workflow_id_dir output and leaf checks - Reorder symlink/non-directory check before resolve() so a symlinked <id> reports as symlinked instead of misleading "Invalid workflow ID" - Reject a pre-existing <id>/workflow.yml that is not a file, avoiding an unhandled IsADirectoryError on later write/copy2 - Escape workflow_id in Rich output to prevent markup injection; escape the repr (not the raw id) so repr-added backslashes cannot re-expose brackets, matching extensions/_commands.py hardening - Add tests for workflow.yml-as-directory and markup-escaped invalid id * Avoid stale lint failures from config helper imports Move PyYAML loading into the helpers that read and write agent-context configuration, and replace the broad Any annotation with object. The runtime behavior stays the same while the module no longer exposes top-level imports that can be flagged as unused when CI analyzes a narrower code shape. * Prevent workflow commands from targeting reserved storage Workflow install and removal paths are derived from workflow IDs before any catalog download, local copy, or directory deletion. Validate that IDs are single workflow-id path segments and reject names reserved for workflow runtime storage so commands cannot target .specify/workflows/runs or .specify/workflows/steps. |
||
|
|
36501d459f |
chore: retire Roo Code integration — extension shut down (#3167) (#3212)
* chore: retire roo integration — extension shut down (#3167) Remove the Roo Code integration after the extension was shut down: subpackage, registry entry, catalog entry, docs, tests, and issue-template options. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: remove stale Roo Code mention in upgrade guide Assisted-by: GitHub Copilot (model: gpt-5.3-codex, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: remove leftover Roo Code references after merge Drop roo from presets/ARCHITECTURE.md example and the agent-context defaults map; these came in from main and were flagged by review. Assisted-by: GitHub Copilot (model: claude-opus-4.8, autonomous) --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
c5ac90b245 |
fix(bundle): allow 'catalog remove' by the same relative path used to add (#3242)
* fix(bundle): allow 'catalog remove' by the same relative path used to add
add_source canonicalizes a local catalog path to an absolute url before persisting it, but remove_source compared only the raw input against the stored id/url. So 'bundle catalog remove ./cat.json' could not undo 'bundle catalog add ./cat.json' -- the stored url was absolute, the removal target relative, and they never matched ('No project-scoped catalog source found'). Match the canonicalized form too (a no-op for ids and remote urls), so a local source is removable by the same path it was added with.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(bundle): match catalog removal target exactly first, canonical only as fallback
Address Copilot review: canonicalizing the removal target unconditionally could let 'remove <id>' also delete a different source whose url equals that id's canonicalized path (ids are treated as local paths by _canonicalize_url, empty scheme). Try an exact id/url match first; only fall back to a canonicalized-url match when no exact match is found, so relative-path removal still works without collateral deletion.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
3571ba72d8 |
fix(workflows): reject bool max_iterations in while/do-while validation (#3237)
* fix(workflows): reject bool max_iterations in while/do-while validation while/do-while validate() checked 'not isinstance(max_iter, int) or max_iter < 1'. Since bool is a subclass of int, isinstance(True, int) is True and True < 1 is False, so 'max_iterations: true' passed validation and then ran as a single iteration (range(True) == range(1)) instead of being reported as a type error. Reject bools explicitly, matching the fail-fast-on-bool handling already used for number inputs and gate options. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: assert empty error list for the valid do-while max_iterations case Address Copilot review: the accepted-config assertion only checked that no error mentioned 'max_iterations', which could let an unrelated validation error pass unnoticed. For a known-good config, assert the entire error list is empty (consistent with the other validate tests in this file). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6fb7e77b3e |
fix: allow prerelease spec-kit versions in compatibility checks (#2695)
* docs: generate integrations reference from catalog * refactor: integrate table rendering into specify integration search --markdown - Remove standalone scripts/generate_integrations_reference.py - Strip doc injection machinery from catalog_docs.py; keep only table rendering - Wire render_integrations_table() into existing --markdown flag of integration search - Remove old simple markdown table block from integration_search (was Name|ID|Version|Description|Author) - Simplify tests: drop subprocess/doc-path tests, keep table rendering and metadata tests - Clean up docs/reference/integrations.md: remove generated markers, update note * fix: address Copilot review feedback on catalog_docs and integration_search - Warn when --markdown is combined with filters (query/--tag/--author) which are silently ignored; catch ValueError/FileNotFoundError and surface clean error via console instead of raw traceback (r3244821516) - Add coverage enforcement in list_integrations_for_docs(): raises ValueError with actionable message if any registry key is missing from INTEGRATION_DOC_URLS, preventing silently incomplete doc tables (r3244821589) - Rename test to accurately reflect sources: label derives from registry config, URL comes from INTEGRATION_DOC_URLS doc map — not solely from registry (r3244821607) - Simplify test dict construction to idiomatic dict comprehension (r3244821619) * fix: add sync test, INTEGRATIONS_REFERENCE_PATH constant, and fix naming * revert: restore docs/reference/integrations.md to upstream/main; remove sync test (GH Actions job will handle) * fix: remove dead INTEGRATIONS_REFERENCE_PATH, drop URL-length padding, fix docstring, drop FileNotFoundError * fix: send --markdown warnings/errors to stderr, rename test for clarity * fix: detect stale doc-map keys, test _render_cell escaping, strengthen header assertion * refactor: promote _render_cell to public render_cell function * test: mock registry and doc maps to avoid brittle live registry coupling * refactor: flatten patches, remove unused imports, fix trailing whitespace, optimize missing calculation * refactor: make validation non-fatal, fix context manager syntax, add CLI tests * fix: improve docstring clarity, test robustness, and exception handling * fix: improve test assertions, disable warnings by default, enhance exception handling * fix: make CLI tests deterministic and improve config access resilience * fix: remove extra blank line, add stale keys validation, add regression test for docs sync * Fix 5 remaining feedback items: - Rename _get_mocked_cli_runner() to _get_catalog_docs_patches() for clarity - Use ExitStack context manager for guaranteed patch cleanup - Add explicit UTF-8 encoding to file reads - Skip doc sync test gracefully when docs aren't present - Remove exception chaining from typer.Exit to avoid noisy tracebacks * address all outstanding copilot review feedback on PR 2563 * Address Copilot feedback: escape URLs in markdown links, deduplicate cell rendering, fix table parser for escaped pipes * Address 3 new Copilot feedback: add URL escaping test, fix parse_first_markdown_table for escaped pipes, guard community tests with skip * Address 3 new Copilot feedback: escape id field, remove unused alias, escape integration URLs * Address 3 new Copilot feedback: fix comment name, include all integrations in list * Fix architectural issue: escape raw fields before composing Markdown to prevent double-escaping * Deduplicate _escape_url_for_markdown_link and add URL escaping test * Address 4 new Copilot feedback: add trailing newline, fix test helper ExitStack, update warning message * Address 4 new Copilot feedback: make escape function public, fix error message, validate test rows, prevent double newline * Update error message in test_missing_catalog_file for clarity * Remove obsolete integrations sync test * keep integrations docs in sync * fix: allow prerelease spec-kit versions in compatibility checks Allow prerelease/dev builds to satisfy extension and preset compatibility checks when their version number falls within the required specifier range. Also harden the integrations docs rendering helpers and add regression coverage for the markdown table parsing and version gating paths. Tests: pytest -q; python3 -m compileall -q .; black/flake8 unavailable Reference: branch 002-generate-integrations-docs; source patch /tmp/spec-kit-changes.patch * fix: isolate prerelease compatibility gate changes Keep the prerelease/version compatibility fix on its own branch and remove the unrelated integrations docs updates that belong with PR 2563. Tests: full suite passed on the prerelease branch before splitting; docs branch covered by targeted docs tests Reference: upstream/main; source patch /tmp/spec-kit-changes.patch * Address PR 2695 feedback: Centralize prerelease policy and add boundary test * Address remaining Copilot PR feedback: revert docs and add preset prerelease tests * Remove unreachable raise CompatibilityError * Fix PEP8 E302 and E303 formatting issues |
||
|
|
5e72b1d486 |
chore: release 0.12.2, begin 0.12.3.dev0 development (#3259)
* chore: bump version to 0.12.2 * chore: begin 0.12.3.dev0 development --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
86709f6089 |
fix(scripts): portable uppercase for branch-name acronym retention (bash 3.2) (#3192)
* fix(scripts): portable uppercase for branch-name acronym retention
Branch-name generation keeps short uppercase acronyms (e.g. "AI") by re-checking
the lowercased word against the original description with ${word^^}. That
parameter expansion is bash 4+ only; on macOS's default bash 3.2 it errors with
"bad substitution", so the acronym/short-word retention branch never matches and
those words are dropped ("go AI now" yields 001-now instead of 001-ai-now). Use
tr '[:lower:]' '[:upper:]' instead, which is portable.
Applies to both the core create-new-feature.sh and the git extension's
create-new-feature-branch.sh. The existing
test_branch_name_short_word_case_sensitivity / test_short_word_retention tests
cover this and now pass on bash 3.2 (CI runs on bash 4+/Linux, so they passed
there already).
(Disclosure: an AI coding agent surfaced the failure while running the suite on
macOS and pinned the root cause; fix written and reviewed by me.)
* fix(scripts): portability follow-ups from code review
- core create-new-feature.sh: match the acronym with `grep -qw` (POSIX
whole-word) instead of `\b...\b` (GNU/BSD-only), matching the git extension
and dropping a non-POSIX construct.
- lint: add a CI guard rejecting bash 4+ case-modification expansions in *.sh.
shellcheck assumes bash 4+ from the shebang and can't flag them, and CI has no
bash-3.2 lane, so this prevents silently re-shipping the macOS regression this
PR fixes.
- update a stale PowerShell extension comment that cited the removed bash idiom.
(Disclosure: prompted by an AI code review of the PR; written and reviewed by me.)
|
||
|
|
c47dd2b812 |
chore: retire Windsurf integration — absorbed into Cognition Devin (#3168) (#3213)
* chore: retire windsurf integration — absorbed into Cognition Devin (#3168) windsurf.com now permanently redirects to devin.ai/desktop following acquisition. Remove subpackage, registry/catalog entries, docs, and tests; re-point sample-agent test fixtures to Kilo Code. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: remove stale Windsurf support references Assisted-by: GitHub Copilot (model: gpt-5.3-codex, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: fix Kilo Code command path in upgrade guide Assisted-by: GitHub Copilot (model: gpt-5.3-codex, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * chore: align integration lists after rebase Assisted-by: GitHub Copilot (model: gpt-5.3-codex, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * docs: align kilocode example with runtime behavior Assisted-by: GitHub Copilot (model: gpt-5.3-codex, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
844c73685b |
[extension] Update Intake extension to v0.1.3 (#3254)
* Update Intake extension to v0.1.3 Update intake extension submitted by @bigsmartben to: - extensions/catalog.community.json (version, download_url, description, provides.commands, updated_at) - docs/community/extensions.md community extensions table Closes #3247 Assisted-by: GitHub Copilot (model: claude-sonnet-4.6, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Revert catalog-wide formatting churn; keep intake-only changes Addresses review feedback on PR #3254: the previous commit re-serialized the entire community catalog (escaping Unicode punctuation like — to \u2014 and reformatting unrelated entries). Restore the catalog to its prior formatting and limit the diff to the intake entry (version, download_url, description, provides.commands, updated_at). Assisted-by: GitHub Copilot (model: claude-opus-4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com> |
||
|
|
20f430686c |
feat(workflows): honor max_concurrency in fan-out via a bounded thread pool (#3224)
* feat(workflows): honor max_concurrency in fan-out via a bounded thread pool * feat(workflows): address review — sliding-window fan-out, locked output, faithful halt Address the reviewer feedback on the bounded fan-out concurrency: - Sliding submission window: keep at most `workers` items in flight and stop launching new items once the run is halting, instead of submitting all items up front (which let the pool keep starting queued work after a halt). - Faithful halt prefix: attribute a halt to the specific item whose own recorded result halted the run (replaying the sequential break condition, honoring continue_on_error/aborted), not the shared run status a later concurrent item may have flipped. The returned prefix now includes the actual halting item, matching the sequential path. An item that fails before recording a result (e.g. an unknown step type) is attributed too, since every item runs the same template. - Lock the parent fan-out output mutation: route the post-fan-out step_results[...]['output'] update through a new RunState.set_step_output() under the run lock, so it cannot race a concurrent save(). - Docstring: describe int() coercion accurately (numeric strings / floats are honored; only non-coercible or <= 1 runs sequentially). Tests: add concurrent halt-includes-halting-item, continue_on_error-does-not- truncate, and unknown-template-type-matches-sequential coverage; make the timing test use a monotonic clock with a looser threshold to avoid CI flakiness. * feat(workflows): address second review pass — concurrency hardening - append_log: serialize the log_entries append + log.jsonl write under a dedicated RunState._log_lock so concurrent fan-out workers can't interleave or corrupt log lines (kept separate from the state lock; never nested). - _run_fan_out.run_item: read the item output back through the item_ctx it executed against rather than the outer context closure — clearer and robust if StepContext ever stops sharing the steps dict by reference. - StepBase: document the thread-safety contract — STEP_REGISTRY holds one shared instance per type, so concurrent fan-out invokes execute() on the same object; implementations must be stateless/thread-safe (the built-ins already are). - test_concurrency_is_real: prove parallelism deterministically with a threading.Barrier (sequential execution can't clear it) instead of a wall-clock timing assertion. * feat(workflows): address review — stamp updated_at under lock, clarify cancel semantics - RunState.save(): move the updated_at timestamp assignment inside the run lock so the timestamp matches the snapshot the thread serializes and concurrent savers don't race on it. - _run_fan_out docstring: clarify that on a halt only not-yet-started items are cancelled; items already running finish but their outputs are ignored (Future.cancel() can't stop running work, and the pool joins on exit). * feat(workflows): serialize on_step_start callback under a lock The concurrent fan-out path invokes _execute_steps from worker threads, which calls the engine's on_step_start callback (the CLI sets it to a console.print lambda). Concurrent invocation could interleave/garble progress output. Guard the call with a WorkflowEngine._callback_lock so callbacks are serialized; the lock is uncontended for sequential runs. * feat(workflows): re-raise worker exceptions in-place to preserve traceback In _run_fan_out's concurrent path, a worker exception was stashed in first_exc and re-raised after the loop. Re-raise it from within the except block with a bare `raise` (after cancelling outstanding futures) so the original traceback is preserved, and drop the now-unneeded first_exc variable. The ThreadPoolExecutor __exit__ still joins any already-running workers before the exception escapes. * feat(workflows): lock final fan-out status, drop redundant output write, bound workers Address third review pass: - Remove the unlocked `context.steps[step_id]["output"] = …` writes in the fan-out parent update. context.steps[step_id] is the same dict object that set_step_output() updates under the run lock, so the direct (unsynchronized) mutation was redundant. - Preserve sequential halt semantics under concurrency: a later in-flight item could overwrite state.status after the halting item was identified. _run_fan_out now derives the halting item's run status (item_halt_status, replacing the bool item_halted) and restores it after the pool joins, so the final status is the first halting item's outcome. - Bound the pool: workers = min(max_concurrency, len(items)) and early-return for empty items, so a user-controlled max_concurrency can't over-allocate threads. Add coverage that an earlier PAUSED item's status wins over a later concurrent FAILED item. * feat(workflows): avoid unlocked context.steps writes when it aliases step_results On a resume run, StepContext is built with steps=state.step_results, so the two direct `context.steps[...] = ...` writes mutated the shared dict outside the run lock and could race save(). Route both through a new _record_result helper that mirrors into context.steps only when it is a distinct object (a fresh run) and otherwise relies solely on record_step_result's locked write. |
||
|
|
9c691e57b9 |
Update Architecture Workflow extension to v1.2.2 (#3255)
Update arch extension submitted by @bigsmartben to: - extensions/catalog.community.json (version, download_url, description, commands count) - docs/community/extensions.md community extensions table Closes #3246 Assisted-by: GitHub Copilot (model: claude-sonnet-4.6, autonomous) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
ada293e203 |
Add Repository Governance extension to community catalog (#3252)
Add repository-governance extension submitted by @bigsmartben to: - extensions/catalog.community.json (alphabetical order) - docs/community/extensions.md community extensions table Closes #3245 Assisted-by: GitHub Copilot (model: claude-sonnet-4.6, autonomous) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
5f440a8e20 |
Update Workflow Preset to v1.3.11 (#3251)
Update workflow-preset submitted by @bigsmartben: - presets/catalog.community.json (version, download_url, updated_at) Closes #3248 Assisted-by: GitHub Copilot (model: claude-sonnet-4.6, autonomous) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
28a38af6c1 |
chore: retire iflow integration — product discontinued (#3166) (#3211)
Remove the iFlow CLI integration whose product was shut down: subpackage, registry entry, catalog entry, docs, tests, and issue-template options. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
8215f3308b |
docs(codebuddy): fix dead install links and CodeBuddy capitalization (#3172) (#3216)
* fix(codebuddy): repoint install_url to codebuddy.cn (#3172) The codebuddy.ai domain no longer resolves; CodeBuddy consolidated onto codebuddy.cn (Tencent). Update install_url and docs links to https://www.codebuddy.cn/cli (verified live). Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: use canonical 'CodeBuddy' capitalization in installation prereqs Address Copilot review: the link text read 'Codebuddy CLI' while the rest of the docs and the integration metadata use 'CodeBuddy'. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
cb7c36c95b |
fix: reject host-less catalog URLs in base and preset validators (#3209) (#3227)
`CatalogStackBase._validate_catalog_url` (inherited by `IntegrationCatalog`) and `PresetCatalog._validate_catalog_url` checked `parsed.netloc`, which is truthy for host-less URLs like `https://:8080` (port only) or `https://user@` (userinfo only). Such URLs slipped past validation despite the error message promising "a valid URL with a host", then failed later with a confusing fetch error. Switch both validators to `parsed.hostname` (None for those inputs), matching the workflow, step, and bundler catalog validators that already do this. Add regression tests covering port-only and userinfo-only URLs for both validators. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
8025481eca |
chore: release 0.12.1, begin 0.12.2.dev0 development (#3253)
* chore: bump version to 0.12.1 * chore: begin 0.12.2.dev0 development --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
4038d370bf |
chore: align CI Python matrix with devguide lifecycle + fix bash 3.2 portability (#3244)
* chore: align CI Python matrix with devguide release lifecycle Run the pytest matrix only on the bugfix (maintenance) releases — 3.13 and 3.14 — instead of 3.11/3.12/3.13, and point the ruff lint job at the latest interpreter (3.14). The supported floor stays at requires-python >= 3.11 (oldest non-EOL security release): older security versions are supported by claim and fixed reactively rather than gated on a wide per-commit matrix. Also add macos-latest to the OS matrix so macOS regressions are caught. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: make bash scripts portable to bash 3.2 (macOS system /bin/bash) Adding macos-latest to the CI matrix surfaced two pre-existing bash 3.2 incompatibilities (macOS ships bash 3.2 as /bin/bash): 1. update-agent-context.sh embedded Python heredocs inside $(...) command substitution. bash 3.2 mis-parses an apostrophe in a heredoc body nested in $(...), failing with "unexpected EOF while looking for matching `''". Removed the apostrophes from the affected $()-nested heredoc body and documented the constraint to prevent regressions. 2. create-new-feature-branch.sh and create-new-feature.sh used the bash 4+ ${word^^} uppercase parameter expansion, which errors as a "bad substitution" on bash 3.2 and caused short uppercase acronyms (e.g. "GO") to be dropped from derived branch names. Replaced with a portable `tr '[:lower:]' '[:upper:]'` pipeline. Verified the full test suite passes under bash 3.2.57 and shellcheck (--severity=error) is clean. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: address review feedback on bash 3.2 portability changes - create-new-feature.sh: replace the non-portable `\b...\b` grep word-boundary (BSD grep treats `\b` as a backspace, so the acronym branch could silently fail) with `grep -qw`, matching its twin create-new-feature-branch.sh, and pipe the description via `printf '%s'` instead of `echo`. - create-new-feature-branch.sh: switch the acronym check to `printf '%s'` as well so both twins are identical and avoid `echo` on user-provided text. - update-agent-context.sh: reword the apostrophe-free self-seeding comment to be clearer and less easy to misread. Verified under bash 3.2.57 (full bash-script suite green) and shellcheck --severity=error. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
ea1827769a |
fix: stop check-prerequisites --paths-only from writing feature.json (#3025) (#3190)
* fix: stop check-prerequisites --paths-only from writing feature.json (#3025) check-prerequisites --paths-only / -PathsOnly is documented as pure, read-only path resolution, but when SPECIFY_FEATURE_DIRECTORY was set it called the persist routine and rewrote .specify/feature.json. That dirtied the working tree and overwrote a pinned feature directory during what should be a no-op. Add an explicit opt-out at the resolver boundary instead of a global env back-channel: - bash: get_feature_paths accepts a leading --no-persist flag that skips _persist_feature_json; check-prerequisites.sh passes it in --paths-only mode. - PowerShell: Get-FeaturePathsEnv gains a -NoPersist switch that skips Save-FeatureJson; check-prerequisites.ps1 passes it in -PathsOnly mode. Normal (non-paths-only) invocations are unchanged and still persist the override, so future sessions without the env var keep working. Add regression tests asserting --paths-only/-PathsOnly leaves a pinned feature.json untouched even when the env override differs, plus a guard that normal mode still persists. * fix: use ASCII hyphen in common.ps1 comment for PS 5.1 compatibility The em-dash in the persist comment introduced non-ASCII bytes, failing test_ps1_file_is_ascii_only which enforces ASCII-only PowerShell sources for Windows PowerShell 5.1 compatibility. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: add PowerShell normal-mode persistence guard (#3025) Addresses Copilot review feedback on #3190: the bash side had a `test_normal_mode_still_persists_feature_json` guard, but there was no symmetric PowerShell test asserting that running check-prerequisites.ps1 *without* -PathsOnly still persists the SPECIFY_FEATURE_DIRECTORY override into .specify/feature.json. Add test_ps_normal_mode_still_persists_feature_json, which guards against accidentally passing -NoPersist unconditionally (or flipping the default) in a future refactor. Verified it fails when -NoPersist is passed in the non -PathsOnly branch and passes with the current conditional. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
00f6a80201 |
docs: document integration catalog subcommands (#3206)
* docs: document integration catalog subcommands the integration reference omits the 'specify integration catalog' subcommand group (list/add/remove) that exists in code, while the extension, preset, and workflow references all document their catalog equivalents. add a catalog management section matching that structure. * docs: address review feedback on integration catalog section - catalogs are consulted by the discovery commands (search/info), not install; install resolves from the built-in registry - 'catalog list' shows project sources as removable only when configured, otherwise active sources are non-removable |
||
|
|
4badf3b5b1 |
fix(scripts): use ASCII [OK] marker in initialize-repo.sh (parity with PowerShell twin) (#3231)
* fix(scripts): use ASCII [OK] marker in initialize-repo.sh (parity with PowerShell twin)
initialize-repo.sh printed its success line with a Unicode checkmark ('✓ Git repository initialized'), while the PowerShell twin initialize-repo.ps1 and both auto-commit scripts use the ASCII marker '[OK]'. That is an output-text divergence across the bash/PowerShell twins and an inconsistency among sibling extension scripts. Use '[OK]' to match.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test: assert full [OK] init line and surface stderr on failure
Address Copilot review: assert the full success line '[OK] Git repository initialized' (not just the '[OK]' substring, which could pass if unrelated [OK] output is added later) and include result.stderr in the assertion message so a failure is debuggable.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
9dfef8629e |
docs: document integration search/info/scaffold subcommands (#3174) (#3194)
* docs: document integration search/info/scaffold subcommands (#3174) docs/reference/integrations.md omitted three subcommands that exist in code, breaking parity with the extension/preset/bundle/workflow references which all document their search/info equivalents. Added sections for: - `specify integration search [query]` (--tag, --author) - `specify integration info <integration_id>` - `specify integration scaffold <key>` (--type: markdown/skills/toml/yaml) Content mirrors the command docstrings, arguments, and options in src/specify_cli/integrations/_query_commands.py and _scaffold_commands.py. Fixes #3174. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.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: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
5a29e4b659 |
docs: remove Cursor from specify check agent list (#3178) (#3193)
* docs: remove Cursor from specify check agent list (#3178) Cursor is registered as an IDE-based integration (requires_cli=False), so `specify check` never probes for a "Cursor CLI". Listing it in the README's check description misled users into expecting a check that does not happen. Removed it from the list; the remaining entries all correspond to integrations with requires_cli=True. Fixes #3178. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.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> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
b1bd9180ca |
fix(goose): repoint install_url and docs to goose-docs.ai (#3171) (#3215)
* fix(goose): repoint install_url and docs to goose-docs.ai (#3171) Goose moved to the Agentic AI Foundation; docs moved from block.github.io/goose to goose-docs.ai. Update install_url and the docs reference link. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs(goose): restore table column alignment Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
804e7329b8 |
fix(scripts): route 'Plan template not found' per --json in setup-plan.ps1 (parity with bash) (#3241)
The 'template not found' fallback used Write-Warning, which emits 'WARNING: Plan template not found' on the warning stream -- diverging from the bash twin (echo 'Warning: Plan template not found' to stderr in --json, stdout in text mode) in both wording and routing, and inconsistent with the sibling 'Copied plan template' message (#3198) in the same block. Route it the same way so the two scripts share one status-output contract. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
c5fb3dc86f |
fix(bundle): send command errors to stderr so --json stdout stays parseable (#3235)
The bundle command group's _fail() helper is documented as printing 'to stderr', and the module contract is 'human logs go to stderr/console' while --json 'emits machine-readable data on stdout'. But it called console.print(), and the shared console writes to STDOUT, so every bundle error (every command routes through _fail) landed on stdout -- corrupting the JSON stream that --json consumers parse. Add a stderr-bound err_console to _console.py (its documented role as the single Console source) and use it in _fail. stdout now carries only the JSON payload. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
5a7d84311b |
chore: release 0.12.0, begin 0.12.1.dev0 development (#3243)
* chore: bump version to 0.12.0 * chore: begin 0.12.1.dev0 development --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
53d9543355 |
feat: make agent-context extension a full opt-in (#3097)
* docs: add Spec Kit spec for agent-context full opt-in Use Spec Kit's own specify workflow to author the spec that makes the agent-context extension a full opt-in, removing all agent-context configuration/support from the Python codebase and removing the deprecation message. Force-added despite specs/ being gitignored; the generated artifact will be purged prior to merge. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: add Spec Kit plan artifacts for agent-context full opt-in Phase 0/1 of the SDD plan workflow: plan.md, research.md, data-model.md, quickstart.md, and contracts/cli-behavior.md. Constitution Check is a documented no-op (repo has no ratified constitution). Force-added despite specs/ being gitignored; generated artifacts will be purged prior to merge. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: correct Constitution Check against ratified v1.0.0 Earlier draft wrongly treated the gate as a no-op; the fork's main is 16 commits behind upstream/main, which carries .specify/memory/constitution.md. Re-evaluate the feature against Principles I-V (all PASS) and note that Principle I mandates keeping context_file as a declared class attribute, validating the R1 metadata decision. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: refresh plan artifacts against synced upstream/main After syncing fork main to upstream and rebasing, re-scan the current agent-context surface. Upstream generalized the single context_file into a plural context_files concept with new resolver helpers (_resolve_context_files, _resolve_context_file_values, _format_context_file_values) and upsert/remove now loop over multiple files. Update research.md, data-model.md, contracts, quickstart grep guards, and the plan summary to cover the expanded removal scope. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: add Spec Kit tasks for agent-context full opt-in Phase 2 of SDD: dependency-ordered tasks.md (30 tasks) organized by the three user stories, with mandatory test tasks (Constitution Principle II) and a foundational phase decoupling __CONTEXT_FILE__ resolution from the extension config. Includes the extension self-seeding task (T015) and a static guard test (T002) enforcing zero agent-context references in the CLI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat!: remove agent-context lifecycle from the Specify CLI Make the agent-context extension a full opt-in. The CLI no longer installs the extension during init, writes agent-context-config.yml, or creates/updates/removes the managed Spec Kit section in agent context files. Context-section upsert/remove, marker resolution, extension-enabled gating, the config helpers, and the obsolete inline deprecation warning are all removed. Integration context_file stays as inert metadata; __CONTEXT_FILE__ now resolves from registry metadata. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(agent-context): self-seed context file from the active integration When agent-context-config.yml has no context_file/context_files, the bundled bash and PowerShell update scripts now resolve the context file from the active integration in .specify/init-options.json via the integration registry, so the extension no longer depends on the CLI writing its config. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test+docs: update suite and docs for agent-context opt-in Update integration/extension tests to expect no agent-context install, config, or context-section writes during init. Add a static guard test (test_agent_context_cli_free.py) asserting the CLI source is free of agent-context lifecycle symbols, plus backward-compatibility tests for legacy projects. Refresh AGENTS.md, the extension README, and add a CHANGELOG entry describing the opt-in behavior change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(agent-context): warn on self-seed failure, correct docs, speed up guard test Address PR review feedback: - Self-seed scripts (bash + PowerShell) now emit an actionable warning when an active integration is configured but specify_cli cannot be imported by the chosen Python (e.g. pipx installs), or when the integration declares no context file, instead of silently falling through to 'nothing to do'. - Correct the extension README disable note: command rendering never reads the extension config; __CONTEXT_FILE__ is always substituted from integration metadata, so a stale context_files value cannot affect rendering. - Cache CLI source reads in the static guard test via a module-scoped fixture so the directory walk happens once instead of once per forbidden symbol. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(agent-context): ship self-owned per-agent context-file defaults The extension now bundles agent-context-defaults.json (key→context_file map) and self-seeds from it, dropping any dependency on the Specify CLI registry. Both the bash and PowerShell update scripts read the bundled JSON map keyed by the active integration from init-options.json. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat!: remove all agent-context state from the Specify CLI Strip every context_file reference from the CLI: the field on all 35 integration classes, the IntegrationBase plumbing (process_template param/step, _context_file_display, docstrings), the __CONTEXT_FILE__ resolution in agents.py, the legacy context_file/context_markers popping in _helpers.py, and the context_file template in integration_scaffold.py. Also drop the Agent context update step and __CONTEXT_FILE__ placeholder from templates/commands/plan.md. The agent-context extension now solely owns all context-file knowledge, including the per-agent default mapping. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: drop context_file coverage and guard against CLI reintroduction Remove CONTEXT_FILE attrs and context_file assertions across the base mixins, all 35 per-integration test files, shared integration tests, and conftest stubs. Rewrite the base-mixin context tests to assert no managed section is written and no __CONTEXT_FILE__ placeholder survives. Extend the CLI-free static guard to forbid context_file, __CONTEXT_FILE__, and _context_file_display in src/specify_cli, and have the extension tests copy the bundled defaults JSON so self-seed runs without the CLI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: reflect full removal of agent-context state from the CLI Update AGENTS.md (integration examples, required-fields table, context behavior section, pitfalls), CHANGELOG, and the SDD spec artifacts (FR-007, SC-002, data-model) to state that the CLI carries no context_file and the extension fully owns the per-agent default mapping via agent-context-defaults.json. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: align SDD artifacts with full context_file removal Update research.md (R1, R2, R4, summary table), contracts/cli-behavior.md (C3, C5), tasks.md (Phase 2, T026, notes), plan.md (Principle I, source map), and checklists/requirements.md so the spec artifacts reflect the implemented decision: the CLI carries no context_file attribute or __CONTEXT_FILE__ resolution, and the per-agent defaults map lives in the extension. Resolves PR review #4548130110. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: scrub stale context-file mentions from CLI docstrings Update the multi_install_safe docstring (drop the removed "context file" invariant), the RovoDev setup docstring (no longer upserts a context section), the Copilot module docstring (drop the context-file line), and tighten the _update_init_options_for_integration note. Pure docstring changes — no behavioral impact. Resolves PR review #4548237085. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test+docs: harden agent-context test helper and fix stale docs - base.py: document multi_install_safe as an optional subclass attribute in the IntegrationBase docstring. - test_cli.py: clarify the init-options assertion is guarding against leftover legacy agent-context keys, not relocation. - test_extension_agent_context.py: _install_agent_context_config now asserts the bundled agent-context-defaults.json exists and always copies it, so self-seeding tests fail loudly instead of silently skipping when the map is missing. - test_integration_cursor_agent.py: drop Path/IntegrationManifest imports left unused after removing the context-section frontmatter tests. Resolves PR review #4548293116. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: remove gitignored SDD artifacts from specs/ The specs/001-agent-context-full-optin/ artifacts were force-added for dogfooding visibility, but specs/ is gitignored and these were always intended to be purged before merge. Remove them so merging does not add an intentionally-untracked directory to repo history. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: keep CHANGELOG.md identical to upstream CHANGELOG.md is auto-generated at release time, so the branch should not carry a manual entry. Restore it to match upstream/main exactly. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: preserve Cursor .mdc frontmatter in agent-context updater scripts The bundled agent-context updater scripts wrote the managed section as plain text. For Cursor-style `.mdc` targets this dropped the required `---\nalwaysApply: true\n---` frontmatter, reintroducing the rule-loading bug originally fixed in #1699. Port the `_ensure_mdc_frontmatter` logic into both the bash and PowerShell updaters: prepend frontmatter when missing, repair `alwaysApply` when set to the wrong value, and leave non-`.mdc` targets untouched. Add regression tests covering both shells. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: scope CLI-free guard to agent-context-specific symbols Drop the bare "context_file" substring from FORBIDDEN_SYMBOLS so the guard no longer fails on unrelated future CLI fields named context_file. The list still covers agent-context-specific identifiers (__CONTEXT_FILE__, _context_file_display, _resolve_context_files, _resolve_context_file_values). Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: harden agent-context bash self-seed against malformed init JSON Two robustness fixes in the embedded Python self-seed logic: - Coerce the integration value from init-options.json to a string only when it is actually a string; otherwise treat it as unset so a corrupted dict/list value degrades to the existing nothing-to-do behavior instead of breaking the agents-map lookup. - Normalize agent-context-defaults.json: only use 'agents' when both the JSON root and the 'agents' value are dicts, so a wrong-shaped (but valid) JSON falls back to the warning path instead of raising on .get. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: correct PowerShell hyphenated key lookup and regex replace count - Self-seed now reads the defaults mapping via $defaults.agents.PSObject.Properties[$integrationKey].Value instead of member access ($defaults.agents.$integrationKey), which parsed hyphenated keys like 'cursor-agent'/'kiro-cli' as subtraction and failed to resolve. - Replace the static [regex]::Replace(..., 1) call, whose trailing 1 was interpreted as RegexOptions.IgnoreCase rather than a replacement count, with an instance Regex whose Replace(input, replacement, 1) limits to the first match as intended. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: make bash .mdc frontmatter guard case-insensitive The bash updater only injected Cursor .mdc frontmatter when ctx_path ended in lowercase '.mdc', so a mixed/upper-case extension (e.g. specify-rules.MDC) was skipped and Cursor would not auto-load the rule file. Compare against the casefolded path. The PowerShell variant already uses -match, which is case-insensitive by default, so no change is needed there. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: document separator-agnostic agent-context update invocation The README hard-coded the dot-notation slash command (/speckit.agent-context.update), which hyphen-separator agents like Forge and Cline do not recognize. Document the canonical command ID plus both slash invocations so users copy the form their agent accepts. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
5367f69f6c |
docs(workflows): add the built-in 'init' step type to the Step Types table (#3234)
The Step Types table in docs/reference/workflows.md listed command, prompt, shell, gate, if, switch, while, do-while, fan-out, and fan-in, but omitted 'init' -- which IS a registered built-in (workflows/__init__.py _register_builtin_steps registers InitStep) and is documented in steps/init/__init__.py as bootstrapping a project (equivalent to 'specify init'). Add the missing row so the reference matches the registry. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
876dca8659 |
fix(workflows): gate validate() must not crash on non-string options (#3233)
GateStep.validate() reports non-string options as an error, but then -- when on_reject is 'abort'/'retry' -- still runs the reject-choice check 'any(o.lower() in ... for o in options)'. For a non-string option (e.g. options: [123]) o.lower() raised AttributeError, which escaped validate() and broke validate_workflow's documented 'return a list of errors, never raise' contract. Guard the check so it only runs when every option is a string (the non-string case is already reported above). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
9ece347a77 |
fix(workflows): make pipe-filter detection quote-aware in expressions (#3232)
_evaluate_simple_expression used 'if "|" in expr' / expr.split("|", 1) to detect a filter pipe, so a literal '|' inside a quoted operand (e.g. inputs.x == 'a|b') was mistaken for a filter separator and raised a spurious ValueError ('unknown filter') instead of comparing the string. Use the existing quote/bracket-aware _find_top_level helper (added for the operator-splitting fix) so only a top-level pipe is treated as a filter separator.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
3036fe6954 |
fix(workflows): reject a fan-in wait_for that names an unknown step at validation (#3225)
* fix(workflows): reject a fan-in wait_for that names an unknown step at validation * fix(workflows): reject fan-in wait_for self-reference and non-string entries Address review feedback on the fan-in wait_for validator: - A fan-in's own id is added to seen_ids before the wait_for check, so `wait_for: [<self>]` passed validation while producing a silent empty join at runtime. Reject self-references explicitly. - Non-string entries (e.g. YAML `wait_for: [123]`) were skipped by the isinstance(str) guard and validated even though they can never match a real step id. Flag them as wiring errors. Add coverage for both cases. |