feat: add specify artifact command exposing composition stacks as JSON - #4267
Draft
nicolehaugen wants to merge 1 commit into
Draft
feat: add specify artifact command exposing composition stacks as JSON#4267nicolehaugen wants to merge 1 commit into
specify artifact command exposing composition stacks as JSON#4267nicolehaugen wants to merge 1 commit into
Conversation
Adds a new `specify artifact` command group with two subcommands:
* `specify artifact list --json` — flat inventory of every command,
template, and script SpecKit exposes for the current project. Each row
carries a stable `id` (`{kind}:{name}`), an author-declared
`name`, its `kind`, and a `description` string that is never
omitted (empty string when the author declared none).
* `specify artifact info <name> --json` — the same row plus its full
ordered composition `stack`: highest-priority contributor first, with
`active` marking the winner `PresetResolver.resolve_content` would
return and `hidden` marking rows shadowed by a higher-priority
`replace`. Each stack entry carries a portable POSIX `manifestPath`
(or `null` for the core baseline) and a `lookupId` from the
contribution-id grammar so the output round-trips against
`specify preset info` and `specify extension info`.
The two commands share one strict JSON error envelope on stderr
(`{ "error": "..." }`) with exit code 1 for the three logical errors
(unknown artifact, ambiguous artifact, not a Spec Kit project) and exit
code 2 for the "`--json` is required" usage error. stdout is always
empty on error, so the two streams stay independently parseable.
Implementation lives in a new `src/specify_cli/artifacts/` subpackage
that mirrors the existing `presets/` and `extensions/` layout — pure
logic in `__init__.py` and thin Typer wiring in `_commands.py`. The
subpackage reuses `PresetResolver.collect_all_layers` for the actual
composition math and only reshapes each layer into a `StackLayer` JSON
row, so `active` and `hidden` stay in lockstep with the resolver's
winner-selection logic.
Skills (`.github/skills/**/SKILL.md`) are intentionally excluded from
the inventory — they are integration-specific installation output, not a
shipped asset family. The command still surfaces the underlying command
that a skill was generated from.
Tests:
* `tests/test_artifact_command.py` — 32 tests: contract shape, sort
order, empty-inventory behavior, kind-hint parsing, ambiguous-name
error, unknown-artifact error, not-a-project error, skills exclusion,
CLI wiring end-to-end (`--json` required, JSON envelope shape,
stderr-only errors, empty stdout on error, UTF-8 with no BOM), and
preset-replace hiding the core layer.
* `tests/test_artifact_command_parity.py` — 6 tests: `manifestPath`
uses forward slashes on every OS and is never absolute, the `active`
row corresponds to the resolver's actual winner, and the pretty-printed
JSON has no trailing whitespace and ends in exactly one newline.
All 38 new tests pass. Full presets + extensions regression suite is
green modulo pre-existing Windows-symlink-privilege failures that
predate this branch.
Assisted-by: GitHub Copilot (model: claude-opus-4.7, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a40fb96-1bbe-4fb2-99d8-411170046cb0
|
|
||
|
|
||
| def test_module_imports(): | ||
| import specify_cli.artifacts # noqa: F401 |
|
|
||
|
|
||
| def test_module_imports(): | ||
| import specify_cli.artifacts # noqa: F401 |
Comment on lines
+18
to
+25
| from specify_cli.artifacts import ( | ||
| AmbiguousArtifactError, | ||
| Artifact, | ||
| ArtifactCatalog, | ||
| ArtifactNotFoundError, | ||
| NotASpecKitProjectError, | ||
| StackLayer, | ||
| ) |
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.
Description
Testing
uv run specify --helpuv sync && uv run pytestAI Disclosure