4 Commits

Author SHA1 Message Date
Kennedy
22e76995c7 feat: implement preset wrap strategy (#2189)
* feat: implement strategy: wrap

* fix: resolve merge conflict for strategy wrap correctness

* feat: multi-preset composable wrapping with priority ordering

Implements comment #4 from PR review: multiple installed wrap presets
now compose in priority order rather than overwriting each other.

Key changes:
- PresetResolver.resolve() gains skip_presets flag; resolve_core() wraps
  it to skip tier 2, preventing accidental nesting during replay
- _replay_wraps_for_command() recomposed all enabled wrap presets for a
  command in ascending priority order (innermost-first) after any
  install or remove
- _replay_skill_override() keeps SKILL.md in sync with the recomposed
  command body for ai-skills-enabled projects
- install_from_directory() detects strategy: wrap commands, stores
  wrap_commands in the registry entry, and calls replay after install
- remove() reads wrap_commands before deletion, removes registry entry
  before rmtree so replay sees post-removal state, then replays
  remaining wraps or unregisters when none remain

Tests: TestResolveCore (5), TestReplayWrapsForCommand (5),
TestInstallRemoveWrapLifecycle (5), plus 2 skill/alias regression tests

* fix: resolve extension commands via manifest file mapping

PresetResolver.resolve_extension_command_via_manifest() consults each
installed extension.yml to find the actual file declared for a command
name, rather than assuming the file is named <cmd_name>.md.  This fixes
_substitute_core_template for extensions like selftest where the manifest
maps speckit.selftest.extension → commands/selftest.md.

Resolution order in _substitute_core_template is now:
  1. resolve_core(cmd_name) — project overrides win, then name-based lookup
  2. resolve_extension_command_via_manifest(cmd_name) — manifest fallback
  3. resolve_core(short_name) — core template short-name fallback

Path traversal guard mirrors the containment check already present in
ExtensionManager to reject absolute paths or paths escaping the extension
root.

* fix: add bundled core_pack as Priority 5 in PresetResolver.resolve()

resolve_core() was returning None for built-in commands (implement,
specify, etc.) because PresetResolver only checked .specify/templates/
commands/ (Priority 4), which is never populated for commands in a
normal project. strategy:wrap presets rely on resolve_core() to fetch
the {CORE_TEMPLATE} body, so the wrap was silently skipped and SKILL.md
was never updated.

Priority 5 now checks core_pack/commands/ (wheel install) or
repo_root/templates/commands/ (source checkout), mirroring the pattern
used by _locate_core_pack() elsewhere.

Updated two tests whose assertions assumed resolve_core() always
returned None when .specify/templates/commands/ was absent.

* fix: harden preset wrap replay removal

* fix: stabilize existing directory error output

* fix: track outermost_pack_id from contributing preset; use Path.parts in tests

- outermost_pack_id now updates alongside outermost_frontmatter inside
  the wrap loop, so it reflects the actual last contributing preset
  rather than always taking wrap_presets[0] (which may have been skipped)
- Replace str(path) substring checks in TestResolveCore with Path.parts
  tuple comparisons for correct behaviour on Windows (CI runs windows-latest)

* fix: guard against non-mapping YAML manifests; apply integration post-processing in replay

- ExtensionManifest._load raises ValidationError for non-dict YAML roots instead of TypeError
- PresetManager._replay_wraps_for_command calls integration.post_process_skill_content,
  matching _register_skills behaviour
- PresetResolver skips extensions that raise OSError/TypeError/AttributeError on manifest load
- Tests: non-mapping YAML, OSError manifest skip, and replay integration post-processing
2026-04-21 15:02:31 -05:00
Manfred Riem
0945df9ec8 Add community content disclaimers (#2058)
* Add community content disclaimers

Add notes clarifying that community extensions, presets, walkthroughs,
and community friends are independently created and maintained by their
respective authors and are not reviewed, nor endorsed, nor supported
by GitHub.

Disclaimers added to:
- README.md: Community Extensions, Community Presets, Community
  Walkthroughs, and Community Friends sections
- extensions/README.md: Community Reference Catalog and Available
  Community Extensions sections
- presets/README.md: Catalog Management section

* Refine community disclaimers per PR review feedback

- Clarify that GitHub/maintainers may review catalog PRs for formatting
  and policy compliance, but do not review, audit, endorse, or support
  the extension/preset code itself (avoids contradiction with submission
  process that mentions PR reviews)
- Add missing 'use at your own discretion' guidance to Community
  Walkthroughs and Community Friends sections for consistency
2026-04-01 19:41:18 -05:00
Manfred Riem
497b5885e1 docs: Add Extensions & Presets section to README (#1898)
* docs: add Extensions & Presets section to README

Add a new 'Making Spec Kit Your Own: Extensions & Presets' section that covers:
- Layering diagram (Mermaid) showing resolution order
- Extensions: what they are, when to use, examples
- Presets: what they are, when to use, examples
- When-to-use-which comparison table
- Links to extensions/README.md and presets/README.md

* docs: clarify project-local overrides in layering diagram

Address review feedback: explain the project-local overrides layer
shown in the diagram, and adjust the intro to acknowledge it as a
third customization mechanism alongside extensions and presets.

* docs: Clarify template vs command resolution in README

- Separate template resolution (top-down, first-match-wins stack) from
  command registration (written directly into agent directories)
- Update Mermaid diagram paths to use <preset-id> and <ext-id>
  placeholders consistent with existing documentation

Addresses PR review feedback on #1898.

* docs: Clarify install-time vs runtime resolution for commands and templates

- README: label templates as runtime-resolved (stack walk) and commands
  as install-time (copied into agent directories, last-installed wins)
- presets/README: add runtime note to template resolution, contrast with
  install-time command registration

* docs: Address review — fix template copy wording, tighten command override description

- presets/README: clarify that preset files are copied at install but
  template resolution still walks the stack at runtime
- README: describe priority-based command resolution and automatic
  restoration on removal instead of vague 'replacing whatever was there'
2026-03-18 14:21:20 -05:00
Copilot
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>
2026-03-13 15:09:14 -05:00