chore: merge upstream 1.0.3 (0.16.2+adlc1 → 1.0.3+adlc1) - #114
Merged
Conversation
Update a11y-governance preset submitted by @hindermath to: - presets/catalog.community.json (version, download_url, documentation, description, templates count, tags) - docs/community/presets.md community presets table Closes github#4064 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>
…ithub#4073) * Add Junie integration with dot-to-hyphen behavior, command formatting, and file transformations. Based on Cline Integration. * Fix references to Cline in Junie integration and update class/test names for consistency. * Fix references to Cline in Junie integration and update class/test names for consistency. * Modified to generate correct formatting in junie
…-019 (github#3886) `resolve_install_plan`'s two FR-019 guards are a truthiness test and an `is None` test: if active_integration and required != active_integration: # clash if active_integration is None and not integration_explicit: # indeterminate An empty string satisfies neither, so it falls through to `effective_integration = required` and the bundle's pinned integration is silently adopted — the exact outcome the docstring says the guard prevents ("resolution fails instead of silently adopting the bundle's required integration"). active=None -> BundlerError: ... could not be determined active='' (blank) -> effective_integration='copilot' <-- silent adopt active='claude' -> BundlerError: ... targets integration 'copilot' Normalise a blank value to None before the guards, and strip first to match the writer (`integration_state.clean_integration_key`, which returns `None` for empty/whitespace and strips otherwise) so a padded value is not reported as clashing with its own unpadded form. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Update cross-platform-governance preset submitted by @hindermath to: - presets/catalog.community.json (version, download_url, documentation, description, provides, tags, updated_at) - docs/community/presets.md community presets table Closes github#4078 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>
…github#4082) * Fix: scaffold self-contained namespaced preset commands (github#4076) Preset command templates named `speckit.<ns>.<cmd>` were silently dropped whenever `.specify/extensions/<ns>/` was absent, while `speckit.<cmd>` always scaffolded. The `_extension_installed_for_command` guard filtered purely on name shape, conflating "override of an installed extension's command" with "a preset shipping its own namespaced command." Because a `type: command` template always ships its own body, such a command is self-contained and must scaffold like any short-named command. Remove the name-shape guard at all four call sites (registration, both reconciliation passes, and skills). The reconciliation loop already skips names that resolve to no layers (`if not layers: continue`), and the composed-None branch still cleans up commands whose base layer disappeared. Convert the command-mode "no base layer to compose onto" hard error into a warn + skip, matching the existing behavior in _reconcile_composed_commands so command-mode install and reconciliation stay consistent. Update the two tests that encoded the old drop behavior to assert the new consistent-scaffold contract, and add coverage proving 2-part and 3-part preset commands scaffold identically with no extension installed. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: cfc4f1ce-6acb-465a-aa7b-999f2e4197fb * Skip uncomposable commands in skills mode too (PR github#4082 review) When _register_commands skips an uncomposable composition command (a wrap/prepend/append with no base layer to compose onto — e.g. the command it wraps comes from an uninstalled extension), install still passed the full manifest to _register_skills. For a command-backed integration in skills mode, _register_skills created the missing skill and fell back to the raw preset body because no `.composed` file existed, materializing a broken SKILL.md — a literal `{CORE_TEMPLATE}` for wrap, or just the preset's own fragment for prepend/append. Previously the raise in _register_commands aborted before skills ran, so this never surfaced. Make _register_skills apply the same skip: for a composition-strategy command with no `.composed` file, resolve the stack and skip when no base exists (resolve_content is None). The skip is silent because _register_commands already warned for the same command in the same pass. Add a regression test proving an uncomposable wrap command renders no skill and never leaks a literal {CORE_TEMPLATE} in skills mode. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: cfc4f1ce-6acb-465a-aa7b-999f2e4197fb --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: cfc4f1ce-6acb-465a-aa7b-999f2e4197fb
…) (github#4104) `specify preset list` now sorts installed presets by (priority, id) so the printed order matches the actual resolution/composition order used by PresetRegistry.list_by_priority(). Lower priority number = higher precedence; ties are broken alphabetically by preset id. Adds a header and footer note clarifying the ordering, updates the presets reference docs, and adds tests. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
) * fix: narrow bare except Exception in VS Code settings merge Replace overly broad except Exception with (OSError, ValueError, KeyError) to let programming errors like TypeError or AttributeError propagate while still handling expected I/O and parse errors gracefully. * test: verify programming errors propagate through handle_vscode_settings The narrow exception change from 'except Exception' to 'except (OSError, ValueError, KeyError)' was not covered by a regression test. Add a test that monkeypatches merge_json_files to raise TypeError and verifies it propagates rather than being swallowed.
* chore: bump version to 0.16.3 * chore: begin 0.16.4.dev0 development --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…#4105) * Add spec-kit-atlas extension to community catalog Add atlas extension submitted by @ashbrener to: - extensions/catalog.community.json (alphabetical order) - docs/community/extensions.md community extensions table Closes github#3993 Assisted-by: GitHub Copilot (model: claude-sonnet-4.6, autonomous) Co-authored-by: Copilot <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> --------- 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> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* feat: add Mistral Vibe integration with Claude parity
- Add VibeIntegration class with ARGUMENT_HINTS, user-invocable, disable-model-invocation
- Add comprehensive test suite matching Claude integration
- Support all Spec Kit workflows (py/sh/ps script types)
* fix: address Vibe integration issues and test cleanup
- Fix Vibe to use .vibe/hooks.toml with toml-vibe format instead of
ignored .vibe/settings.json, adding toml-vibe event handler
- Remove unsupported argument-hint injection (Vibe schema doesn't support it)
- Restructure test file to inherit from SkillsIntegrationTests mixin
- Remove all unused imports to pass Ruff F401 checks
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
* fix: add name field to Vibe hooks and fix toml regex patterns
- Add required 'name' field for each Vibe hook in hooks.toml
- Fix regex patterns in _merge_vibe_toml_fragment and _remove_vibe_toml_entries
to correctly match [[hooks]] blocks instead of [} characters
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
* fix: align Vibe hooks with HookConfig schema and drop stray devcontainer lock
- use Vibe's 'match' field (re:-prefixed regex translation) instead of
unsupported 'matcher'; emit only on tool hooks (rejected on post_agent)
- limit CANONICAL_TO_NATIVE to Vibe's three hook types (pre_tool,
post_tool, post_agent); unsupported events skip with a warning
- deduplicate generated hook names (Vibe drops duplicates by name)
- add behavioral tests for toml-vibe generation, merging, and teardown
- remove accidentally committed .devcontainer/devcontainer-lock.json
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: wrap Vibe hook stdout in structured JSON response envelope
Vibe parses any non-empty hook stdout as a JSON HookStructuredResponse;
plain text is reported as a hook failure and its output dropped. Add a
hook_specific_output envelope to the dispatcher (template and runtime)
that emits {"decision": "allow", "hook_specific_output":
{"additional_context": ...}} and declare it for all Vibe events:
post_tool injects the context, pre_tool/post_agent parse cleanly and
ignore it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: quote Vibe hook commands for cmd.exe on Windows hosts
Vibe launches hooks via asyncio.create_subprocess_shell, which is
%COMSPEC% (cmd.exe) on Windows — POSIX single-quoting is not quoting
there, so an interpreter or dispatcher path containing spaces made every
hook fail to start. Add a 'cmd' quoting target to _shell_quote
(double-quote when needed, embedded quotes doubled per MSVCRT argv
rules), resolve it host-side like 'host', and select it for Vibe when
generating on a Windows host.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: pin POSIX quoting target in Vibe test for Windows CI runners
test_posix_host_keeps_shlex_quoting asserts host (shlex) quoting, but on
a Windows runner _vibe_target_os() resolves to 'cmd' and the command is
double-quoted. Monkeypatch the target so the test exercises the POSIX
path on every platform.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Mistral Vibe <vibe@mistral.ai>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Assisted-by: OpenAI Codex (model: GPT-5, autonomous)
… only its legacy aliases (github#3880) * fix(bundler): read the authoritative default_integration field `active_integration()` resolves a project's integration with data.get("integration") or data.get("id") or data.get("active") and never looks at `default_integration` — which is the key the CLI actually writes. `integration_state.set_default_integration` persists `data["default_integration"] = integration_key`, and the canonical reader in that module orders it the other way round: key = state.get("default_integration") or state.get("integration") So a project initialised by any current version of the CLI looks to the bundler as though it has no active integration: {"default_integration": "copilot"} -> None (expected "copilot") {"integration": "copilot"} -> "copilot" (legacy alias) That silently changes bundler behaviour that keys off the active integration, including the FR-019 clash guard, which treats an undeterminable integration differently from a known one. Read `default_integration` first and keep the three legacy aliases as fallbacks for projects initialised by older versions. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(bundler): correct the justification for reading default_integration Review catch: the comment cited a nonexistent `integration_state.set_default_integration` and overstated the impact. The real writer is `write_integration_json`, which persists BOTH `integration` and `default_integration` (integration_state.py:248-250), so a marker produced by the current CLI already resolved through the `integration` alias. Measured: {"integration": "copilot", "default_integration": "copilot"} -> 'copilot' {"default_integration": "copilot"} -> 'copilot' (after fix) Reword both the source comment and the test docstring: this is about which field is authoritative when they disagree, plus resolving a marker that carries only `default_integration` — not about every current project being undetectable. The precedence itself still has its precedent, the canonical reader at integration_state.py:199. Behaviour unchanged; comments and docstrings only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Add SpecJudge extension to community catalog Add specjudge extension submitted by @JoaquinRuiz to: - extensions/catalog.community.json (alphabetical order) - docs/community/extensions.md community extensions table Closes github#4068 Assisted-by: GitHub Copilot (model: claude-sonnet-4.6, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Revert unrelated formatting-only edits in community catalog Restore the compact single-line `tools` array formatting for existing extensions that were incidentally expanded, keeping the diff scoped to the SpecJudge addition and the catalog `updated_at` bump. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot App <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>
…ng (github#3975) * fix: log progress tracker refresh errors instead of silently swallowing The bare 'except Exception: pass' in StepTracker._maybe_refresh() completely hid rendering bugs in the Rich progress display. Now logs at DEBUG level with full traceback for diagnostics. * test: add regression test for StepTracker refresh error logging - Test that _maybe_refresh logs exceptions instead of silently swallowing - Verify diagnostic message and traceback are recorded in DEBUG logs - Confirm tracker update completes normally despite refresh callback failure Requested by Copilot in PR github#3975
Update agent-parity-governance preset submitted by @hindermath to: - presets/catalog.community.json (version, download_url, documentation, description, templates count) - docs/community/presets.md community presets table Closes github#4109 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>
Review follow-up: feeding PyYAML the byte stream let its Reader honour a UTF-16 BOM and accept a manifest yamlio.load_yaml rejects, so zip and directory sources diverged. Decode raw as UTF-8 (UnicodeError -> BundlerError 'Could not read ...') then parse, and cover a well-formed UTF-16 manifest in the regression tests. Assisted-by: GitHub Copilot (model: claude-fable-5, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove exists() check before open() and catch FileNotFoundError directly. This prevents a race where the file is deleted between check and open, while preserving the descriptive error message.
…ines (github#3891) Test-FileExists / Test-DirHasFiles report their line with Write-Output and ALSO return $true/$false — both on the Success stream. The callers piped the whole call to `| Out-Null` to discard the boolean, which discarded the report line with it, so text mode printed the header and nothing under it: BEFORE (measured, powershell.exe -NoProfile -File ... -IncludeTasks): FEATURE_DIR:...\specs\001-f AVAILABLE_DOCS: (2 lines) AFTER: FEATURE_DIR:...\specs\001-f AVAILABLE_DOCS: [OK] research.md [FAIL] data-model.md [FAIL] contracts/ [FAIL] quickstart.md [FAIL] tasks.md (7 lines) The bash and Python twins both list every document under that header, so the PowerShell variant silently returned less information for the same inputs. Filter out only the boolean, keeping the report lines. Adds the first PowerShell text-mode test in this file (every existing PS test is -Json). File stays ASCII-only (verified 0 non-ASCII bytes). Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…) (github#3781) * fix(integrations): dispatch goose commands via `goose run` (github#2416) `YamlIntegration` never overrode `build_exec_args()`, so `GooseIntegration` inherited the `IntegrationBase` no-op that returns `None`. Callers read `None` as "this CLI is unavailable", so every workflow command/prompt step targeting Goose reported `CLI not found or not installed` even with `goose` on PATH. Reproduced with the agent CLI present on PATH (shutil.which stubbed to a real path, subprocess.run stubbed): amp -> completed argv=['amp', '-p', '/speckit.specify'] opencode -> completed argv=['opencode', 'run', '--command', 'speckit.specify'] goose -> FAILED "integration 'goose' CLI not found or not installed" Implement `build_exec_args()` for Goose. Per the goose CLI docs there is no `-p` flag; the non-interactive entry point is `goose run`, which takes `-t/--text` for free-form text, `--recipe` for a stored recipe, `--params KEY=VALUE` for recipe parameters, plus `--model` and `--output-format`. Spec Kit installs its commands as Goose *recipes* under `.goose/recipes/`, each declaring an optional `args` parameter (already enforced by test_setup_declares_args_parameter_for_args_prompt), so a `/speckit.<name> <rest>` invocation maps exactly onto `--recipe <path> --params args=<rest>`. This mirrors `OpencodeIntegration`, which maps the same leading slash-command onto opencode's `--command`. The recipe path is derived from the same two sources `setup()` uses -- `config["folder"]` + `config["commands_subdir"]` and `command_filename()` -- so the dispatch target cannot drift from the installed file; a test asserts the resolved `--recipe` path exists after `setup()`. Dotted extension commands (`speckit.git.commit`) round-trip. Extra args are applied before the canonical flags so Spec Kit's selection stays authoritative, matching opencode. No behaviour change for other integrations, and `requires_cli` is untouched. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(goose): only map the speckit. namespace onto --recipe build_exec_args() treated every prompt starting with "/" as a Spec Kit recipe. Because command_filename() unconditionally re-adds the "speckit." prefix, a free-form slash prompt was silently promoted into a recipe run against a file that was never installed: /help -> --recipe .goose/recipes/speckit.help.yaml /plan the sprint -> --recipe .goose/recipes/speckit.plan.yaml /speckit. -> --recipe .goose/recipes/speckit..yaml PromptStep passes arbitrary prompt: strings to build_exec_args, and both /help and /plan are Goose's own session commands, so this is reachable. Unlike opencode's --command or hermes' -s, which hand a bare name to the agent's own resolver, --recipe is a path Spec Kit synthesizes -- so only the namespace it can actually spell may take that branch. Gate the branch on "/speckit." and fall through to -t otherwise. A bare "/speckit." leaves no stem and also falls through. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(goose): stop asserting an argv that goose would reject test_goose_extra_args_cannot_clobber_prompt_derived_recipe asserted that a duplicated --recipe is merely reordered, on a "last value wins" premise. That premise is wrong for goose: `goose run` is clap-derive based and --recipe/--model/--output-format are single-value args without args_override_self, so a duplicate makes goose exit with "cannot be used multiple times" whichever side comes first. The test passed in pytest while pinning a command line that cannot run. Replace it with an ordering-parity test that asserts only what Spec Kit actually controls: extra args precede the canonical flags (matching opencode/codex/cursor-agent), and Spec Kit never emits a duplicate single-value flag itself. Verified non-vacuous -- it fails if the extra-args hook is moved after the canonical flags. The ordering comment claimed precedence it cannot deliver; corrected to state positional parity only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add specassay-check extension submitted by @rdryfoos to: - extensions/catalog.community.json (alphabetical order) - docs/community/extensions.md community extensions table Closes github#4057 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>
…o 4.37.6 (github#4114) * chore(deps): bump github/codeql-action/analyze from 4.37.5 to 4.37.6 Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.37.5 to 4.37.6. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@d1ba80a...5595cca) --- updated-dependencies: - dependency-name: github/codeql-action/analyze dependency-version: 4.37.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps): bump github/codeql-action init to match analyze (v4.37.6) Bump github/codeql-action/init to 5595cca (v4.37.6) so it matches the analyze bump already in this PR. init and analyze must be pinned to the same version; a mismatch fails CodeQL with "Loaded a configuration file for version '4.37.5', but running version '4.37.6'". This subsumes github#4115. Also group github/codeql-action* in dependabot.yml so future bumps of init and analyze arrive as a single PR and can't drift apart again. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6a01da34-7431-4ffc-84ee-e51ecf224334 --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6a01da34-7431-4ffc-84ee-e51ecf224334
* Harden community submission workflow outputs Restrict extension and preset submission PRs to the expected catalog and docs files. * test: check community allowlists pairwise --------- Co-authored-by: root <kinsonnee@gmail.com>
Return an actionable validation error when a workflow step type is a YAML list or mapping instead of raising during registry membership checks. Assisted-by: OpenAI Codex (model: GPT-5, autonomous)
Add arch-governance extension submitted by @ashbrener to: - extensions/catalog.community.json (alphabetical order) - docs/community/extensions.md community extensions table Closes github#4084 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>
…gits (github#4101) * fix(taskstoissues): widen task-ID regex to match IDs longer than 3 digits /speckit.converge assigns new IDs with T{M+1:03d}, where :03d is a floor not a cap, so IDs already exceed three digits once a tasks.md passes 999 entries. The dedup regex `\bT\d{3}\b` cannot match those titles because the trailing \b can't fall between two digits, so affected tasks are silently skipped instead of deduped or created. * fix(taskstoissues): use command placeholder for the converge reference The literal `/speckit.converge` added to the dedup step is not rewritten by the dot-to-hyphen pass, so every skills-mode integration emitted a SKILL.md containing dot notation and 19 integration tests failed. Use the `__SPECKIT_COMMAND_CONVERGE__` placeholder, which resolves to `/speckit.converge` or `/speckit-converge` per the agent's separator. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Update superspec extension submitted by @CrazyBaran: - extensions/catalog.community.json (version, download_url, updated_at) - docs/community/extensions.md community extensions table Closes github#4117 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>
Update intake-authoring-governance preset submitted by @hindermath: - presets/catalog.community.json (version, download_url, documentation, description, templates count) - docs/community/presets.md community presets table Closes github#4118 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>
Add specassay preset submitted by @rdryfoos to: - presets/catalog.community.json (alphabetical order) - docs/community/presets.md community presets table Closes github#4058 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>
* chore: bump version to 0.16.4 * chore: begin 0.16.5.dev0 development --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Adds specassay v0.3.4 to bundles/catalog.community.json and docs/community/bundles.md. Validation results: - Bundle ID matches ^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$: pass - Version 0.3.4 is valid semver X.Y.Z: pass - Repository https://github.com/rdryfoos/specassay is a public GitHub repo containing bundle.yml, README.md, and LICENSE: pass - bundle.yml fields match submission (id, name, version, role, author, license, speckit_version, provides 1 extension + 1 preset): pass - README documents role, components, required catalogs, and install steps: pass - Download URL is a valid HTTPS GitHub release asset under the submitted repo: pass - Release v0.3.4 exists and specassay-0.3.4.zip is attached: pass - Catalog entry fields match submission and manifest; verified=false: pass - Tags are 2-5 lowercase strings: pass (5 tags) - Required catalogs documented (extensions + presets); README includes catalog add commands and testing details confirm catalog registration: pass - All checklist items checked: pass Closes github#4059 cc @rdryfoos Assisted-by: GitHub Copilot (model: claude-sonnet-4.6, autonomous) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Assisted-by: GitHub Copilot (model: gpt-5.6-sol, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: GitHub Copilot (model: gpt-5.6-sol, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: GitHub Copilot (model: gpt-5.6-sol, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chore: bump version to 1.0.3 * chore: begin 1.0.4.dev0 development --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat: add DeepSeek Harness (DSH) integration Add a skills-based integration for DeepSeek Harness (DSH), the agent runtime behind the dsh CLI and Web GUI. DSH discovers project skills one level deep from .dsh/skills (its native root, provider rank 100) and .agents/skills (rank 200), using speckit-<name>/SKILL.md directory bundles with name/description frontmatter — the same agentskills.io layout SkillsIntegration already scaffolds. Users invoke skills via the '/'-trigger input in the DSH Web GUI (/speckit-specify <description>); the harness injects the rendered <skill_content> into the turn. Project AGENTS.md guidance is loaded natively by DSH, so no context-file handling is needed. - Install skills to .dsh/skills/speckit-<name>/SKILL.md (isolated static root; multi-install safe alongside other agents) - CLI dispatch via 'dsh --profile headless "<prompt>"' one-shot mode - Register 'dsh' in ALWAYS_SLASH_AGENTS so init output and hook messages render /speckit-<command> invocations - Add init next-steps hint, catalog entry, issue-template lists, devcontainer install step, docs table row - Verify with the real dsh-skill-filesystem provider: all 10 skills discovered as user- and model-invocable, bodies loadable Assisted-by: DeepSeek Harness (model: glm-5.3, supervised) * Address review: bump catalog updated_at, assert DSH next-steps output - Refresh integrations/catalog.json updated_at to the date of this catalog change (matches the bump convention in prior catalog PRs). - Add TestDshNextSteps asserting the init next-steps panel shows the 'Start DSH (dsh web)' guidance and .dsh/skills install path, and renders /speckit-<command> rather than dotted invocations — modeled on TestGrokNextSteps so a regression in the hint or path fails CI. Assisted-by: DeepSeek Harness (model: glm-5.3, supervised) * Address DSH review feedback Place launcher extra arguments before the headless task positional and keep the discovery-depth test compatible with valid skill resource directories. Assisted-by: OpenAI Codex (model: GPT-5, autonomous) --------- Co-authored-by: sunxing <sunxing@qianxin.com>
…one" (github#4136) `InstalledBundleRecord.from_dict` and `_component_from_dict` read required fields with `str(data.get(key, "")).strip()`. The `""` default covers only a MISSING key. A key that is present but JSON `null` — how a hand-edited or corrupt `.specify/bundle-records.json` spells an empty field — yields `None`, and `str(None)` is the literal `"None"`. That text is non-empty, so it sails past the required-field guards that exist to reject such a file: bundle_id=null -> ACCEPTED: bundle_id='None' version='1.0.0' version=null -> ACCEPTED: bundle_id='a' version='None' id=null -> ACCEPTED: components=[('presets', 'None')] The phantom component then feeds the collateral-protection refcount, so `components_still_needed()` reports ('presets', 'None') as protected. Reuse `manifest._text`, the sibling helper whose docstring describes this exact trap and which fixed the same bug for bundle.yml in github#3798. The falsy-non-list half of this hardening already landed in this file as github#3666; the explicit-null half was never mirrored here. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ription (github#4138) Get-BranchName's fallback assumed the pipeline yields at least one element: $fallbackWords = ($result -split '-') | Where-Object { $_ } | ... return [string]::Join('-', $fallbackWords) ConvertTo-CleanBranchName blanks every non-[a-z0-9] character, so a description written in any non-Latin script leaves nothing for the pipeline to emit. It yields $null, [string]::Join throws ArgumentNullException, and $ErrorActionPreference = 'Stop' makes that terminating — the script dies with a .NET stack trace, empty stdout, exit 1. Measured with all three twins installed in one project: desc='добавить авторизацию' bash rc=0 BRANCH_NAME='001-' py rc=0 BRANCH_NAME='001-' ps rc=1 Exception calling "Join" ... Identical for 添加用户认证 and '!!! ??? ***'. This hits every PowerShell user who phrases a feature in their own language. Wrap the pipeline in @() so it stays an array; Join on an empty array returns "", matching the twins. Verified the normal case is unchanged ('a-to-the-of' -> 'a-to-the', 'add user authentication' -> '001-user-authentication'), and the file stays ASCII-only (0 non-ASCII bytes) for tests/test_ps1_encoding.py. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Assisted-by: GitHub Copilot (model: gpt-5.6-sol, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, supervised) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0d15dc1e-c6ea-48a5-a255-0c4644cbcddf
…kage (github#4351) Bundled extensions (agent-context, git, assess) have no download URL, so `specify extension update` could offer a version bump it then failed to install: step 5 unconditionally called catalog.download_extension(), which errors out for catalog entries without a URL (github#4345). Resolve the update source for bundled extensions from the copy shipped with the running spec-kit release instead: - `_bundled_update_source()` locates the local bundled copy and parses its manifest version. - `_archive_extension_directory()` packages that copy as a ZIP so the update flows through the identical hardened archive pipeline (bounded extraction, manifest preflight, ID/version checks, backup/rollback) rather than growing a second install path. Symlinks are never followed into the archive. - When the local copy lags the catalog (or is missing), the update is blocked with an explicit "upgrade spec-kit, then rerun" message instead of installing an intermediate version or crashing; when the local copy is newer than the catalog, it installs the local version. Tests pin the install-from-local-copy route, every blocked-update branch, the newer-local-copy case, archive content/symlink behavior, and execute-bit restoration through the archive install route (POSIX-only; install_from_directory's trailing ensure_executable_scripts() re-establishes modes that ZIP extraction drops). Part 1 of the series requested in review on github#4351; refs github#4345. Assisted-by: Claude Code (model: claude-fable-5) Co-authored-by: Jakub Baranowski <cr4zybaran@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(bundler): reject unsupported catalog payload versions Assisted-by: GitHub Copilot (model: gpt-5.6-sol, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(bundler): cover compatible catalog schema versions Assisted-by: GitHub Copilot (model: gpt-5.6-sol, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(presets): let a preset declare a required extension A preset whose command overrides call into an extension is inert without it, but the overrides fall through to the core workflow, so nothing errors -- the feature just silently does less than the user expects. Until now the only place that dependency could be stated was the README, which fails exactly the user who did not read it. Add an optional requires.extensions to preset.yml, accepting either a bare extension id or a mapping with an optional version specifier and an optional required flag. Validation mirrors the requires.speckit_version strictness from github#3980: a non-list, a member that is neither string nor mapping, a missing or malformed id, a non-string or unparseable version, and a non-boolean required each raise PresetValidationError rather than surfacing later as a bare TypeError from re.match or SpecifierSet. On `specify preset add`, warn once for each unsatisfied dependency, naming the extension and the command that installs it. The check runs at the single point where the --dev, --from, and catalog paths converge, so all three behave the same. It warns rather than fails: these presets are written to degrade safely, and three catalog entries already declare the dependency, so failing would break installs that work today. The field is optional, so every existing preset stays valid and silent. Closes github#4231 Assisted-by: Claude Code (model: Claude Opus 5, autonomous) * fix(presets): match dependency remediation to the reason, and flag disabled Addresses review feedback on github#4250. `specify extension add <id>` refuses an already-installed extension without --force, so suggesting it for a version mismatch handed the user a command that could only fail. Suggest `extension update` for a version mismatch and `extension enable` for a disabled one, keeping `add` for a genuinely missing extension. A disabled extension was also treated as satisfied, because the registry entry exists. Resolution skips disabled extensions, so the preset stays exactly as inert as if the extension were absent, with no warning to explain it. Report it as a distinct "disabled" reason, ahead of any version check -- enabling is the prerequisite, and the version may be fine once it is. Also correct the closing line, which said the extensions "will do nothing until they are present" -- inaccurate for a disabled extension, which is present. Assisted-by: Claude Code (model: Claude Opus 5, autonomous) * fix(presets): avoid promising unsatisfiable extension updates Assisted-by: ChatGPT (model: GPT-5, supervised) * docs(presets): describe requires.extensions version as a full constraint Addresses review feedback on github#4250. The guide said a mapping was for "a version floor", but the field accepts any PEP 440 specifier, so upper bounds, exact pins, and exclusions were all undocumented. Say "version constraint", show a bounded range in the example, and state the accepted forms explicitly. Two adjacent claims had also drifted from the behaviour and are corrected in the same pass. The guide promised the warning would "name the command that fixes it", which stopped being true for a version mismatch once that case began stating the constraint instead of naming a command that cannot satisfy every specifier. And the notes listed only missing and version-unsatisfied dependencies as warned about, never mentioning disabled ones. Assisted-by: Claude Code (model: Claude Opus 5, autonomous) * fix(presets): treat stale entries as unmet and unparseable versions as uncomparable Addresses review feedback on github#4250. A registry entry is not proof the extension can contribute. When the entry survives after .specify/extensions/<id> is removed, PresetResolver skips the extension outright -- both template lookup and layer collection guard on is_dir() -- so the preset is as inert as if it were never installed, while the surviving entry read as satisfied. Report that state as a distinct "stale" reason, ahead of the disabled and version checks, and remediate it with a forced reinstall rather than a plain add. The uncomparable-version guard also only covered non-string values. An unparseable string such as "unknown" passed it and reached version_satisfies(), which catches InvalidVersion and returns False -- reporting a mismatch against a version that was never actually evaluated, and contradicting the documented behaviour that unusable versions are not invented into mismatches. Check parseability before comparing so only real comparisons reach the warning. The test helper now creates the extension directory alongside the registry entry, matching what the installer does, with an opt-out for the stale case. Assisted-by: Claude Code (model: Claude Opus 5, autonomous) * fix(presets): treat an unregistered extension directory as satisfied Addresses review feedback on github#4250. An absent registry entry was reported as a missing dependency, but it does not mean the extension is unusable. _get_all_extensions_by_priority() admits a safe on-disk directory as an unregistered extension at implicit priority 10, so its artifacts resolve and the preset works -- the warning was a false alarm telling users to install something already in use. Treat a matching directory as present, guarded the same way resolution guards itself: the id must be a safe registry id, and the registry must not be corrupt, since a corrupt one makes that path fail closed and contribute nothing. An unregistered extension has no recorded version, so a declared constraint is uncomparable rather than unsatisfied. This is the mirror of the stale case in the previous commit. Between them, the check now agrees with resolution in both directions: an entry without files is unmet, and files without an entry are satisfied. Assisted-by: Claude Code (model: Claude Opus 5, autonomous) * fix(presets): exclude corrupted registry ids and flag discovery-only installs Addresses review feedback on github#4250. ExtensionRegistry.get() returns None for a corrupted (non-dict) entry exactly as it does for an absent one, so a corrupted entry whose directory survived reached the unregistered-directory fallback and was reported satisfied. keys() deliberately retains corrupted ids precisely so resolution does not re-admit those directories, so the fallback now requires the id to be absent from keys() -- it can no longer revive what resolution excludes. is_corrupt() does not cover this, as it validates only the registry container. `extension add <id>` resolves through the catalogs, and the default community catalog is discovery-only, so installing by id is rejected for anything listed only there. That covers all three extensions this feature exists to serve -- aide, mde, and speckit-inventory -- meaning the first live warnings would have pointed at a command that exits 1. Note the --from <archive-url> form once, after the list, for the two reasons that suggest an install by id. Determining discovery-only status per dependency would mean a catalog fetch inside `preset add`, so the note is unconditional rather than risking a network call on an install path that has never needed one. Assisted-by: Claude Code (model: Claude Opus 5, autonomous) * fix(presets): keep leading-hyphen ids out of suggested extension commands Addresses review feedback on github#4250. `^[a-z0-9-]+$` admits a leading hyphen, so a dependency id such as `--force` produced `specify extension add --force`. Typer parses that as an option rather than the positional extension argument, so the advertised fix could not run at all -- and the stale, disabled, and version remedies had the same flaw. Reuse _command_safe_id from the extensions commands, which already rejects a leading hyphen and falls back to a `<extension-id>` placeholder, rather than adding a second implementation of the same rule that could drift from it. The displayed id keeps plain Rich escaping, since only the copyable command needs to survive the parser. Assisted-by: Claude Code (model: Claude Opus 5, autonomous) * fix(presets): report corrupt entries, survive an unreadable registry, correct the footer Addresses review feedback on github#4250, plus two issues found while self-reviewing the same code. ExtensionRegistry construction can raise OSError -- _load() recovers from malformed content but deliberately lets OSError through, and is_corrupt() re-reads the file. This check runs after the install has already completed and preset_add handles only preset-domain errors, so an unreadable registry turned a finished install into a traceback over what is only a warning. An unreadable registry now yields no results instead. A corrupted entry was conflated with an absent one. get() returns None for both, but is_installed() still counts the key, so the suggested `extension add <id>` is refused as already installed. It is now a distinct "corrupt" reason remediated with a forced reinstall. The closing note asserted that dependent features "will do nothing" and that the preset is "safe to use". Neither holds for a version mismatch: the extension is installed and enabled, so the preset does invoke it, and the combination is untested against the declared constraint rather than safe. The note is now split by consequence. Found while self-reviewing, in the same two areas this review keeps surfacing: the id pattern used re.match with an anchored `^...$`, but `$` also matches before a trailing newline, so "demo-ext\n" validated here while PresetResolver._is_safe_registry_id (fullmatch) rejects it, and the newline would have reached a printed command. And declaring one dependency twice warned twice; exact repeats now collapse, while two entries for one id with different constraints are still both checked, since both have to hold. Assisted-by: Claude Code (model: Claude Opus 5, autonomous) * fix(presets): stop advertising a bare-id install as a guaranteed fix Addresses review feedback on github#4250. The per-dependency line labelled `specify extension add <id>` as "Fix with", while the closing note said an archive URL is required -- the remedy and the note contradicted each other, and for every extension motivating this feature the bare-id command is refused outright, since all three are listed only in the discovery-only community catalog. Label the remedy by the action it performs rather than asserting it fixes the problem: "Install with", "Reinstall with", "Enable with", and "Needs" for a version constraint, which was never a command in the first place. Replace the contradictory note with what actually happens. The discovery-only rejection prints the exact `--from <archive-url>` invocation to use, so the bare command is a signpost rather than a dead end, and saying so is both accurate and useful. Determining which catalog an extension came from would require a catalog fetch on an install path that touches no network, so the note stays unconditional and is now only emitted when a suggested command actually resolves through the catalogs. Assisted-by: Claude Code (model: Claude Opus 5, autonomous)
Replace check-then-act pattern with unlink(missing_ok=True) to eliminate TOCTOU race condition in finally blocks.
* fix: add JSON error handling to auth config loader Wrap json.loads() in load_auth_config() with try/except to catch JSONDecodeError and raise a clean ValueError with a descriptive message, matching the convention used for all other validation failures in the same function. * fix: add JSON error handling to auth config loader Wrap json.loads() in load_auth_config() with try/except to catch malformed JSON and raise a clean ValueError with path context. Update test to expect ValueError instead of JSONDecodeError.
* fix: escape Rich markup in workflow error output Escape user-controlled exception text with _escape_markup() to prevent Rich from interpreting square brackets as markup tags, which could corrupt output or raise MarkupError. Matches the pattern used in workflow_resume and all other err.print() calls in this file. * fix: escape Rich markup in workflow error output Escape exception text with _escape_markup() to prevent Rich from interpreting square brackets as markup tags in error messages. Covers all three workflow error handlers: - load_workflow() ValueError handler - engine.execute() ValueError handler - engine.execute() generic Exception handler
…ead of silently mis-binding it (github#3894) * fix(workflows): refuse a filter mixed with a comparison operator The pipe is detected before the boolean/comparison operators, so a filter written on the right-hand operand was applied to the comparison's BOOLEAN RESULT instead of to the operand: {{ inputs.count > inputs.limit | default(5) }} -> False With count=10 and limit missing, `count > limit` is evaluated first and `default` is then applied to the resulting bool — a no-op, since a bool is never empty — so the expression silently returns the comparison against the *unfiltered* operand. The author meant `10 > 5` = True. This module already refuses the mirror case rather than guessing: {{ inputs.missing | default('7') > '5' }} -> ValueError: filter 'default' used in an unsupported form Same ambiguity, opposite handling. Refuse both the same way so an ambiguous expression is reported instead of quietly producing the answer the author did not ask for. No legitimate expression is affected: applying `default` to a bool is a no-op, and `join`/`map`/`contains` on a bool is an error, so there is no working use of a filter on a comparison result. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(workflows): detect a leading unary `not` in the ambiguity check Review catch: unary `not` is a leading prefix, not an infix token, so it has no surrounding space for the operator scan to match — the parser itself tests it with `expr.startswith("not ")`. It was therefore absent from the guard, and the mis-binding this PR exists to reject survived: {{ not inputs.missing | default(1) }} -> True `not inputs.missing` is evaluated first and `default` is applied to that boolean (a no-op), so the expression silently returns True where the author meant `not 1` = False. Check the prefix the same way the parser does. A `not` that follows `and`/`or` was already caught by those tokens. Verified: not inputs.missing | default(1) -> refused (operand of 'not') not inputs.value | default(1) -> refused (operand of 'not') inputs.count > inputs.limit | default(5) -> refused (operand of '>') inputs.flag and not inputs.value | default(1) -> refused (operand of 'and') not inputs.value / not inputs.flag -> unchanged (True / False) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ithub#3842) * fix: narrow bare except Exception in preset command reconciliation Replace overly broad except Exception with specific exception types (ImportError, FileNotFoundError, OSError, ValueError, TypeError) to let programming errors propagate while still falling back to generic path-based registration for expected failures. * fix: narrow bare except Exception in preset command reconciliation Remove ValueError and TypeError from the except tuple per Copilot feedback. These can mask programming/contract errors that should fail fast. Keep only ImportError, FileNotFoundError, OSError which are expected from extension discovery/import/IO failures.
…ithub#4145) `_read_prompts_yml` documents that it "Returns an empty list if the file is missing, malformed, or contains no valid prompt entries", but it only filters at the entry level (`isinstance(item, dict)`) — it never validates the entry's `name`. `_merge_prompt_entries` then does `name = entry.get("name", "")` followed by `if name in generated_by_name:`, a dict membership test. A hand-edited `.rovodev/prompts.yml` whose entry has a YAML sequence or mapping `name` therefore raises an unhandled TypeError out of setup(): name=list -> TypeError: unhashable type: 'list' name=mapping -> TypeError: unhashable type: 'dict' name=int -> OK name=null -> OK Only the unhashable shapes crash. Every `specify init` / `integration install` / `integration upgrade` for rovodev on that project then aborts with a raw traceback, and the user's prompts.yml is never rewritten. A non-string name can never match a generated entry, so treat it like any other unmatched entry and preserve it verbatim. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…thub#3904) * fix: remove TOCTOU race in RunState.load Remove exists() check before open() and catch FileNotFoundError directly. This prevents a race where the file is deleted between check and open, while preserving the descriptive error message. * fix: skip corrupted state.json in list_runs() instead of aborting Catch OSError, JSONDecodeError, and UnicodeDecodeError to skip bad entries gracefully so valid runs are still listed. Add regression tests: - test_list_skips_invalid_utf8_with_valid_sibling - test_list_skips_oserror_with_valid_sibling
…ithub#4146) `_parse_edit` collected the shorthand operation keys by iterating the frozenset: shorthand_keys = [key for key in _SHORTHAND_OPERATION_KEYS if key in edit_raw] `_SHORTHAND_OPERATION_KEYS` is `VALID_OPERATIONS`, a frozenset, so its iteration order depends on per-process string-hash randomization. The two error messages built from that list named the offending keys in a different order on every run for the exact same overlay file: ["Edit at index 0 has multiple operation keys: 'insert_after', 'remove'."] ["Edit at index 0 has multiple operation keys: 'remove', 'insert_after'."] ["Edit at index 0 has multiple operation keys: 'remove', 'insert_after'."] Iterating `edit_raw` instead yields the user's declared order and is deterministic. Dict keys are always hashable, so the membership test is safe in this direction too. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…github#4148) Both overlay writers in `overlays/_commands.py` called `yaml.safe_dump(data, sort_keys=False)` without `allow_unicode=True`, so every non-ASCII character was rewritten as a `\uXXXX` / `\xNN` escape inside a double-quoted scalar. Every other YAML writer in the repo already passes `allow_unicode=True` (agents.py, bundler/lib/yamlio.py, extensions, integrations/base.py, ...). Overlay files are explicitly hand-authored and hand-edited -- the format is documented in docs/reference/workflows.md and users are told to write these files. `overlay add`, `enable`, `disable` and `set-priority` all round-trip the file through `safe_dump`, so merely toggling an overlay mangled a UTF-8 file the user wrote by hand: message: "Revisar el plan — \xBFaprobar? 日本語" The value still parses back identically, so this is not corruption -- it is the loss of a documented, hand-edited file's legibility. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
… 1.0.3+adlc1) # Conflicts: # docs/docfx.json # pyproject.toml # src/specify_cli/commands/init.py # src/specify_cli/extensions/_commands.py # src/specify_cli/integrations/vibe/__init__.py # src/specify_cli/presets/__init__.py # src/specify_cli/presets/_commands.py # tests/integrations/test_integration_subcommand.py # tests/test_presets.py
The except-ImportError fallback for _init_fork defined accent() but omitted accent_style(), so a circular import during _utils initialization left accent_style undefined and caused test_handle_vscode_settings_preserves_mode_on_atomic_write to raise NameError. Add a cyan fallback matching accent(). Assisted-by: opencode (model: glm-5.2, supervised)
or-yam
approved these changes
Sep 3, 2026
or-yam
left a comment
Collaborator
There was a problem hiding this comment.
Reviewed a only by reading the description and the conflicts resolved decisions, which looks fine to me.
or-yam
approved these changes
Sep 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Upstream merge: syncs the tikalk fork with
github/spec-kitthrough release 1.0.3 plus 18 post-release commits (149 commits total; upstream HEAD0053c3a3). Bumps the fork to1.0.3+adlc1.Base:
0.16.2+adlc1→1.0.3+adlc1. Releases spanned: 0.16.3, 0.16.4, 0.16.5, 1.0.0, 1.0.1, 1.0.2, 1.0.3 + post-1.0.3 main.Notable upstream content
dsh(DeepSeek Harness, Add DeepSeek Harness (DSH) integration github/spec-kit#4336),docker-agent(feat(docker-agent): add Docker Agent integration github/spec-kit#4302); upstreamvibe(Mistral Vibe) rewrite adopted (feat: add Mistral Vibe integration with Claude parity github/spec-kit#4075); Junie dot-to-hyphen + command formatting (Integrate Junie with dot-to-hyphen behavior and command formatting github/spec-kit#4073); qodercli → SkillsIntegration ([bug-fix] Fix qodercli-skills-migration: migrate QodercliIntegration to SkillsIntegration github/spec-kit#4205); goosegoose rundispatch (fix(integrations): dispatch goose commands viagoose run(#2416) github/spec-kit#3781)install_from_directorybundled path); scaffold extension config via bundler (fix: scaffold extension config when installing via bundler github/spec-kit#4285)specify inithanging on arrow-key pickers in agent harnesses (fix(init): stop specify init hanging on arrow-key pickers in agent harnesses github/spec-kit#4178);--non-interactivenow errors on non-empty dir without--force--require-specin check-prerequisites (add --require-spec to check-prerequisites github/spec-kit#4367, ported toadlc.spec.analyze/adlc.spec.convergepresets); taskstoissues regex widened to\bT\d{3,}\b(fix(taskstoissues): widen task-ID regex to match IDs longer than 3 digits github/spec-kit#4101); PowerShellOut-Null/non-Latin fixes (fix(powershell): stop Out-Null swallowing setup-tasks AVAILABLE_DOCS lines github/spec-kit#4188/fix(powershell): stopOut-Nullswallowing the AVAILABLE_DOCS status lines github/spec-kit#3891/fix(powershell): stopcreate-new-feature.ps1crashing on a description with no ASCII words github/spec-kit#4138); feature-assess agentic workflow (feat: add feature-assess agentic workflow that installs and runs Spec Kit github/spec-kit#4186)missing_okZIP cleanup (fix: use missing_ok=True in extension ZIP cleanup github/spec-kit#3870), TOCTOURunState.load(fix: remove TOCTOU race in RunState.load github/spec-kit#3839), non-UTF-8 tolerance, bounded HTTP, malformed overlay/step rejectionConflicts resolved (9)
pyproject.toml1.0.3+adlc1; kept fork name/description/httpx/force-includedocs/docfx.jsoncommands/init.pyaccent()theming + upstream--non-interactiveerror branch (github#4178)extensions/_commands.pymissing_okcleanup (github#3870); kept forkaccent()theming,_command_safe_id, preserve-metadata/restore()logic; fixed undefinedsafe_id→cmd_idpresets/__init__.pyreplaced_commandsset on upstream'scommand_templateslooppresets/_commands.pyaccent()theming + upstream resolution-order sort (github#4104)integrations/vibe/__init__.pytests/integrations/test_integration_subcommand.pytests/test_presets.pyTest adaptations
test_integration_dsh.py— DSH/speckit-planinvocation assertions adapted to fork'sspec/speckitprefix split (ai_skills=True→/spec-plan, ALWAYS_SLASH →/speckit-plan)test_integration_junie.py— addedprojectkwarg to_expected_files+ fork file-scan block;/speckit-planhandoff assertion →/spec-plan(fork prefix)Verification
src/,tests/,presets/)test_ps_variant_prefixed_with_powershell_launcherrequirespwsh(not installed in this env; fails identically onorigin/main)specify init --team-ai-directives <KB> --integration opencode→ v1.0.3+adlc1;dsh/docker-agentinstall/uninstall clean; constitution template resolution + team-ai-directives hook wiring verifiedFork customizations preserved (auto-merged cleanly)
scripts/bash/common.sh:extract_constitution_rules,load_team_directives_configscripts/bash/create-new-feature.sh:replace_date_placeholders,-IssueToken_core_fork.py:FORK_DEFAULT_CATALOG_URL/FORK_COMMUNITY_CATALOG_URL_cmd_prefix()returns"spec"(upstream"speckit")🤖 Agent disclosure: This PR (merge, conflict resolution, test adaptation, and this PR body) was authored by opencode (model: glm-5.2) on behalf of @lior, under direct human supervision. No code was committed autonomously without review. The fork's standard
Assisted-by:trailer convention applies to any follow-up commits added during review.