mirror of
https://github.com/github/spec-kit.git
synced 2026-08-15 14:02:04 +02:00
main
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
16cfab7724 |
feat(presets): resolve constitution templates at command time (#3984)
* feat(presets): resolve constitutions at command time Gate install-time constitution materialization behind the constitution-sync preset while preserving one-time init seeding and authored-file safeguards. Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7dbce70f-80c6-4e14-a30d-78cb358bcb84 * fix(presets): emit composed template content Add a machine-readable preset resolve mode backed by PresetResolver.resolve_content and require the constitution command to consume it. Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7dbce70f-80c6-4e14-a30d-78cb358bcb84 * fix(presets): unify runtime template composition Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7dbce70f-80c6-4e14-a30d-78cb358bcb84 * fix(presets): secure runtime template resolution Align runtime resolution across script variants, validate registry path components, and honor canonical extension ordering and convention paths. Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(presets): align runtime priority semantics Normalize and tie-break preset priorities consistently across script variants, and preserve template bytes when Python materializes generated files. Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(presets): stop at effective template base Avoid parsing irrelevant lower layers once resolution reaches a replace base, and decode raw bytes so Python preserves source line endings. Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(presets): align extension template resolution Support root-level extension templates across runtime resolvers, fail safely when Bash cannot parse an extension registry, and validate requested templates in every prerequisite output mode. Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(presets): resolve dotted command identifiers Route safe dotted names through command resolution, correct traversal coverage, and make Windows CI text decoding explicit. Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Avoid orphan feature directories on template errors Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Align malformed preset manifest handling Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3158e06f-95df-4e3a-843f-f159a35aa30c * Complete runtime resolver parity Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3158e06f-95df-4e3a-843f-f159a35aa30c * Fail closed on resolver input errors Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3158e06f-95df-4e3a-843f-f159a35aa30c * Force UTF-8 and full manifest validation Force UTF-8 decoding for registry and manifest reads in the Bash and PowerShell embedded-Python parsers so resolution no longer depends on the process locale, and validate every manifest template entry's required fields, type, and strategy consistent with the canonical PresetManifest. Assisted-by: GitHub Copilot (model: claude-opus-4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3158e06f-95df-4e3a-843f-f159a35aa30c * Fail closed on empty manifests and corrupt registries Reject manifests missing the provides/templates sections or declaring an empty template list in all three runtime resolvers, matching the canonical PresetManifest which treats those as invalid instead of silently degrading a composing layer to a convention `replace` lookup. Make a corrupt or unreadable extension registry fail closed in Bash, PowerShell, and Python instead of swallowing the error and treating every on-disk extension directory as unregistered-and-enabled, which could activate a disabled extension. Read the preset and extension registries as explicit UTF-8 in the PowerShell resolver so priority/enabled-state decoding no longer depends on the process code page under Windows PowerShell 5.1. Assisted-by: GitHub Copilot (model: claude-opus-4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3158e06f-95df-4e3a-843f-f159a35aa30c * fix(presets): fail closed when extension registry is not a regular file The Bash and Python resolvers used is_file()/`-f` to gate reading the extension `.registry`, which returns false for a directory or a broken symlink at that path. In those cases the resolvers treated the registry as absent and scanned every on-disk extension directory as unregistered and enabled — a fail-open path. Detect any filesystem entry at the registry path (including broken symlinks) and reject unless it is a readable regular file. PowerShell now rejects a non-leaf entry explicitly for parity. Adds directory- and broken-symlink parity regressions. Assisted-by: GitHub Copilot (model: claude-opus-4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3158e06f-95df-4e3a-843f-f159a35aa30c * fix(presets): fail closed on corrupt registry in canonical resolver and PowerShell Two remaining fail-open paths for an invalid extension registry: - The canonical PresetResolver enumerated extensions through ExtensionRegistry, whose _load() normalizes a corrupt or unreadable registry to an empty mapping. The directory scan then admitted every on-disk extension directory as unregistered-and-enabled, so a corrupt registry could still supply constitution content at init and through constitution-sync materialization. Add a non-invasive is_corrupt() probe (recovery behavior for install/enable/disable is unchanged) and raise from _get_all_extensions_by_priority() when the registry exists but is invalid. _load() now also recovers from OSError/UnicodeDecodeError so a directory or unreadable registry no longer crashes construction. - The PowerShell resolver gated the registry read with Test-Path, which returns false for a dangling symlink on Windows, letting a broken .registry symlink bypass the guard and enable every on-disk extension. Detect the entry via directory enumeration (which observes a broken symlink) and reject it unless it is a readable regular file. Adds canonical corrupt/directory-registry regressions and extends the broken-symlink parity test to PowerShell. Assisted-by: GitHub Copilot (model: claude-opus-4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3158e06f-95df-4e3a-843f-f159a35aa30c * fix(presets): detect dangling registry symlink in ExtensionRegistry.is_corrupt is_corrupt() gated on Path.exists(), which follows symlinks and returns False for a dangling .registry symlink — so the canonical PresetResolver treated it as an absent registry and fell back to scanning every on-disk extension directory as unregistered-and-enabled, reopening the fail-open path this guard closes. Detect lexical existence with os.path.lexists and require a regular file before parsing, so a broken symlink (or directory) is reported corrupt and resolution fails closed. Adds a canonical broken-symlink regression alongside the directory case. Assisted-by: GitHub Copilot (model: claude-opus-4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3158e06f-95df-4e3a-843f-f159a35aa30c --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7dbce70f-80c6-4e14-a30d-78cb358bcb84 Copilot-Session: 3158e06f-95df-4e3a-843f-f159a35aa30c |
||
|
|
43a54bf2d6 |
feat(presets): add opt-in constitution-sync preset (#3873)
* feat(presets): add opt-in constitution-sync preset Follow-up to #3790, which removed the consistency-propagation pass from the core /constitution command in favor of runtime resolution. Teams that treat materialized plan/spec/tasks templates as reviewed, committed artifacts lost the auto-sync of amended constitutional guidance on a non-forced upgrade. Add a bundled, opt-in `constitution-sync` preset that restores that behavior via a wrap-strategy override of speckit.constitution (composes on {CORE_TEMPLATE} so it stays forward-compatible). It only writes into the project's own .specify/templates scaffolds and installed command files, never into stack-owned template layers. - presets/constitution-sync/: preset.yml (requires >=0.14.4), wrap command, README documenting the tension between auto-propagation and the resolution stack - presets/catalog.json: bundled entry - docs/upgrade.md: document the 0.14.4 behavior change and the opt-in - tests/test_presets.py: structural + composition coverage (TestConstitutionSyncPreset) Assisted-by: GitHub Copilot (model: Claude Opus 4.8, supervised) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: afa7c1d2-147b-4f62-a6fc-a2cc824cfa3e * fix(presets): ship constitution-sync in wheel, clarify scope guard, assert composition Address review feedback on #3873: - pyproject.toml: force-include presets/constitution-sync into the wheel's core_pack so `_locate_bundled_preset` resolves it in a released install; the bundled advertisement was otherwise unshippable. - tests/contract/test_wheel_bundled_presets.py: new contract test asserting every bundled preset in presets/catalog.json is force-included (guards lean too). - commands/speckit.constitution.md: explicitly state the propagation section supersedes the core Scope Guard, which otherwise says dependent templates are not modified here. - tests/test_presets.py: assert resolve_content substitutes {CORE_TEMPLATE} and the effective command embeds both the core body and the sync pass. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, supervised) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: afa7c1d2-147b-4f62-a6fc-a2cc824cfa3e * test(presets): parse frontmatter as YAML in constitution-sync wrapper test Address review feedback on #3873: assert `strategy: wrap` structurally by parsing the Markdown frontmatter as YAML (instead of a substring match that could false-positive on body text), and assert {CORE_TEMPLATE} in the body section only. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, supervised) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: afa7c1d2-147b-4f62-a6fc-a2cc824cfa3e * docs(presets): reframe constitution-sync README around behavior and caveats Rework the preset's user-facing docs to describe what it does, what it does not do, and the caveats you take on — rather than leading with version/origin history. The preset stack is the project's forward direction, so the README no longer positions this as "restoring pre-0.14.4 behavior." Also make the edit-in-place vs. composition conflict explicit and consistent across the wrapper command and docs: propagation into command files/templates that are provided or wrapped by a preset/extension is clobbered on stack reconciliation (integration use/upgrade, preset/extension install/remove), so the wrapper restricts propagation to project-local artifacts the team owns. - README: forward-looking "What it does / does not do / When to use / Caveats" - speckit.constitution.md: step 4 no longer hand-edits composed command files; closing caveat covers command files too - docs/upgrade.md: note the composition-model conflict in the opt-in section - tests: assert the updated closing-caveat wording Assisted-by: GitHub Copilot (model: Claude Opus 4.8, supervised) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: afa7c1d2-147b-4f62-a6fc-a2cc824cfa3e * docs(presets): tweak constitution-sync README default-behavior wording Phrase the default-behavior note as "the current version of Spec Kit" and rewrap the opening paragraph. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, supervised) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: afa7c1d2-147b-4f62-a6fc-a2cc824cfa3e * docs(presets): keep emphasis spans on one line in constitution-sync README Avoid **bold** spans broken across soft line breaks (runtime resolution, reviewed committed artifacts) so they render consistently. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, supervised) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: afa7c1d2-147b-4f62-a6fc-a2cc824cfa3e * docs(presets): refocus constitution-sync README on what it restores Reframe the intro around what the preset restores and what the user opts into, rather than describing current Spec Kit default behavior. Be honest that propagation was removed deliberately (duplicates the source of truth, fights composition) and this preset knowingly reintroduces it and its tradeoffs. Minor flow fixes (comma splice, terse bullet). Assisted-by: GitHub Copilot (model: Claude Opus 4.8, supervised) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: afa7c1d2-147b-4f62-a6fc-a2cc824cfa3e * docs(upgrade): de-pin version from /constitution behavior-change section The upgrade guide always describes the latest version, so hard-pinning "0.14.4" in the heading and "Starting in 0.14.4" in the body added no value. Keep the #3790 provenance link and the "no longer propagates" framing; the machine-readable version gate stays in preset.yml. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, supervised) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: afa7c1d2-147b-4f62-a6fc-a2cc824cfa3e * docs(upgrade): clarify non-breaking nuance and presets direction Note that the /constitution scope change is only noticeable if you relied on the old edit-in-place behavior, and add the forward-looking framing: presets and extensions — not in-place file edits — are how Spec Kit now governs, versions, and audits shared assets across repositories. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, supervised) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: afa7c1d2-147b-4f62-a6fc-a2cc824cfa3e --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: afa7c1d2-147b-4f62-a6fc-a2cc824cfa3e |
||
|
|
8750e94d10 |
docs(presets): add lean preset README and enrich catalog metadata (#2340)
* docs(presets): add lean preset README and enrich catalog metadata - Add README.md documenting the lean workflow preset, its commands, when to use it, and development instructions. - Add license, requires.speckit_version, and provides.commands fields to the lean preset catalog entry. - Add "core" tag to preset.yml for discoverability. * fix: bump catalog updated_at and add provides.templates for consistency Address PR review feedback: - Bump updated_at to reflect catalog modification time - Add provides.templates (0) to lean preset entry for consistency with catalog schema used in catalog.community.json |
||
|
|
43cb0fa7ab |
feat: add bundled lean preset with minimal workflow commands (#2161)
* feat: add bundled lean preset with minimal workflow commands Add a lean preset that overrides the 5 core workflow commands (specify, plan, tasks, implement, constitution) with minimal prompts that produce exactly one artifact each — no extension hooks, no scripts, no git branching, no templates. Bundled preset infrastructure: - Add _locate_bundled_preset() mirroring _locate_bundled_extension() - Update 'specify init --preset' to try bundled -> catalog fallback - Update 'specify preset add' to try bundled -> catalog fallback - Add bundled guard in download_pack() for presets without download URLs - Add lean to presets/catalog.json with 'bundled: true' marker - Add lean to pyproject.toml force-include for wheel packaging - Align error messages with bundled extension error pattern Tests: 15 new tests (TestLeanPreset + TestBundledPresetLocator) * refactor: address review — clean up unused imports, strengthen test assertions - Remove unused MagicMock import and cache_dir setup in download test - Assert 'bundled' and 'reinstall' in CLI error output (not just exit code) - Mock catalog in missing-locally test for deterministic bundled error path - Fix test versions to satisfy updated speckit_version >=0.6.0 requirement * refactor: address review — fix constitution paths, add REINSTALL_COMMAND to presets.py - Fix constitution path to .specify/memory/constitution.md in plan, tasks, implement commands (matching core command convention) - Include REINSTALL_COMMAND in download_pack() bundled guard for consistent recovery instructions across bundled extensions and presets * refactor: address review — explicit feature_directory paths, ZIP cleanup in finally - Prefix spec.md/plan.md/tasks.md with <feature_directory>/ in plan, tasks, and implement commands so the agent doesn't operate on repo root by mistake - Move ZIP unlink into finally block in init --preset path so cleanup runs even when install_from_zip raises (matching preset_add pattern) * refactor: address review — replace Unicode em dashes with ASCII, fix grammar - Replace all Unicode em dashes with ASCII hyphens in preset.yml and catalog.json to avoid decode errors on non-UTF-8 environments - Fix grammar: 'store it in tasks.md' -> 'store them in tasks.md' * refactor: address review - align task format between tasks and implement - Remove undefined [P] marker from implement (lean uses sequential execution) - Clarify checkbox update: 'change - [ ] to - [x]' instead of ambiguous '[X]' - Simplify implement to execute tasks in order without parallel complexity * refactor: address review - parse frontmatter instead of raw substring search - Use CommandRegistrar.parse_frontmatter() to check for scripts/agent_scripts keys in YAML frontmatter instead of brittle 'scripts:' substring search |
||
|
|
69ee7a836e |
feat(presets): Pluggable preset system with catalog, resolver, and skills propagation (#1787)
* Initial plan * feat(templates): add pluggable template system with packs, catalog, resolver, and CLI commands Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * test(templates): add comprehensive unit tests for template pack system Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * feat(presets): pluggable preset system with template/command overrides, catalog, and resolver - Rename 'template packs' to 'presets' to avoid naming collision with core templates - PresetManifest, PresetRegistry, PresetManager, PresetCatalog, PresetResolver in presets.py - Extract CommandRegistrar to agents.py as shared infrastructure - CLI: specify preset list/add/remove/search/resolve/info - CLI: specify preset catalog list/add/remove - --preset option on specify init - Priority-based preset stacking (--priority, lower = higher precedence) - Command overrides registered into all detected agent directories (17+ agents) - Extension command safety: skip registration if target extension not installed - Multi-catalog support: env var, project config, user config, built-in defaults - resolve_template() / Resolve-Template in bash/PowerShell scripts - Self-test preset: overrides all 6 core templates + 1 command - Scaffold with 4 examples: core/extension template and command overrides - Preset catalog (catalog.json, catalog.community.json) - Documentation: README.md, ARCHITECTURE.md, PUBLISHING.md - 110 preset tests, 253 total tests passing * feat(presets): propagate command overrides to skills via init-options - Add save_init_options() / load_init_options() helpers that persist CLI flags from 'specify init' to .specify/init-options.json - PresetManager._register_skills() overwrites SKILL.md files when --ai-skills was used during init and corresponding skill dirs exist - PresetManager._unregister_skills() restores core template content on preset removal - registered_skills stored in preset registry metadata - 8 new tests covering skill override, skip conditions, and restore * fix: address PR check failures (ruff F541, CodeQL URL substring) - Remove extraneous f-prefix from two f-strings without placeholders - Replace substring URL check in test with startswith/endswith assertions to satisfy CodeQL incomplete URL substring sanitization rule * fix: address Copilot PR review comments - Move save_init_options() before preset install so skills propagation works during 'specify init --preset --ai-skills' - Clean up downloaded ZIP after successful preset install during init - Validate --from URL scheme (require HTTPS, HTTP only for localhost) - Expose unregister_commands() on extensions.py CommandRegistrar wrapper instead of reaching into private _registrar field - Use _get_merged_packs() for search() and get_pack_info() so all active catalogs are searched, not just the highest-priority one - Fix fetch_catalog() cache to verify cached URL matches current URL - Fix PresetResolver: script resolution uses .sh extension, consistent file extensions throughout resolve(), and resolve_with_source() delegates to resolve() to honor template_type parameter - Fix bash common.sh: fall through to directory scan when python3 returns empty preset list - Fix PowerShell Resolve-Template: filter out dot-folders and sort extensions deterministically * fix: narrow empty except blocks and add explanatory comments * fix: address Copilot PR review comments (round 2) - Fix init --preset error masking: distinguish "not found" from real errors - Fix bash resolve_template: skip hidden dirs in extensions (match Python/PS) - Fix temp dir leaks in tests: use temp_dir fixture instead of mkdtemp - Fix self-test catalog entry: add note that it's local-only (no download_url) - Fix Windows path issue in resolve_with_source: use Path.relative_to() - Fix skill restore path: use project's .specify/templates/commands/ not source tree - Add encoding="utf-8" to all file read/write in agents.py - Update test to set up core command templates for skill restoration * fix: remove self-test from catalog.json (local-only preset) * fix: address Copilot PR review comments (round 3) - Fix PS Resolve-Template fallback to skip dot-prefixed dirs (.cache) - Rename _catalog to _catalog_name for consistency with extension system - Enforce install_allowed policy in CLI preset add and download_pack() - Fix shell injection: pass registry path via env var instead of string interpolation * fix: correct PresetError docstring from template to preset * Removed CHANGELOG requirement * Applying review recommendations * Applying review recommendations * Applying review recommendations * Applying review recommendations --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> |