Skip to content

[Feature]: Add specify core inventory command exposing the baked-in commands, templates, and scripts as JSON #4215

Description

@nicolehaugen

Problem Statement

Consumers of SpecKit sometimes need "the baseline set of commands, templates, and scripts baked into SpecKit itself — what a project has with zero presets and zero extensions installed." Command names are already enumerable via CORE_COMMAND_NAMES at extensions/__init__.py:119 — and, importantly, this is not a static list. It's populated by _load_core_command_names() at extensions/__init__.py:78-108, which already dynamically scans core_pack/commands (wheel-time) or templates/commands (source checkout) at import time, using the canonical _locate_core_pack() / _repo_root() asset resolvers. Current commands in the baseline (from that scan): analyze, checklist, clarify, constitution, converge, implement, plan, specify, tasks, taskstoissues.

There is no equivalent scan for the shipped templates or scripts, so consumers who want the baseline have to either:

  1. Enumerate the filesystem directly (fragile — internal directory layout isn't a stable contract), or
  2. Maintain their own hardcoded list. speckit-wizard-canvas does exactly this — composition/collect.mjs::loadCoreInventory imports pipeline/canonical.mjs, which is a hand-maintained copy of what SpecKit ships. Every SpecKit release risks drift.

A companion issue (specify artifact info --json, filed separately) already surfaces core as layer: "core" rows inside each artifact's stack, but that's a per-artifact view. For "what does a bare-bones SpecKit look like?" a consumer needs an inventory view too.

Proposed Solution

Add a specify core inventory --json command that returns the full baseline inventory:

{
  "commands": [
    { "id": "core:_:command:speckit.plan",
      "name": "speckit.plan",
      "description": "",
      "sourcePath": "<internal-path-relative-to-package>",
      "artifact": "specs/{feature}/plan.md",
      "optional": false,
      "handoffs": [ "" ] }
  ],
  "templates": [
    { "id": "core:_:template:constitution",
      "name": "constitution",
      "description": "",
      "sourcePath": "" }
  ],
  "scripts": [
    { "id": "core:_:script:setup",
      "name": "setup",
      "description": "",
      "sourcePath": "",
      "runtimes": ["bash", "powershell", "python"] }
  ]
}

The entry shape mirrors the per-contribution schema returned by the companion preset info --json / extension info --json issue, minus strategy (core is always the base layer). Under the hood the command:

  • Reuses CORE_COMMAND_NAMES for the command list.
  • Extends the existing file-scan pattern used by _load_core_command_names() — same _locate_core_pack() + _repo_root() canonical asset resolvers — to scan core_pack/templates and core_pack/scripts (with the equivalent source-checkout fallbacks under templates/ and scripts/). No new asset-discovery paths.
  • Runs a small frontmatter parser over the enumerated command / template markdown files (and reads script headers), extracting name / description / artifact / optional / handoffs / (script) runtimes, and emits the JSON.
  • Uses the same stable-id scheme (core:_:kind:name) as the companion specify artifact issue so consumers can join this output to that command's layer: "core" stack rows via lookupId.

Alternatives Considered

  • Fold entirely into the companion specify artifact command's layer: "core" rows. Rejected — that command gives per-artifact stacks; this one gives the flat inventory needed for "here's what you get out of the box" listings. Both views point at the same data with the same id.
  • Return only names (no description / sourcePath / handoffs). Rejected — the wizard's pipeline/canonical.mjs needs the metadata, which is why it exists in the first place.
  • Ship the inventory as a static JSON file inside the package. Rejected — still requires the wizard to know the package path; a CLI command is more stable.

Component

Specify CLI (initialization, commands)

AI Agent (if applicable)

Not applicable

Use Cases

  1. speckit-wizard-canvas deletes pipeline/canonical.mjs and replaces composition/collect.mjs::loadCoreInventory with JSON.parse(execFileSync("specify", ["core", "inventory", "--json"])).
  2. A "SpecKit tour" UI shows first-time users exactly which commands / templates / scripts they get with zero installs.
  3. A CI check asserts "no preset is shadowing a core artifact I depend on" by cross-referencing this inventory with the companion specify artifact info --json stacks and the companion preset list --json installed set.

Acceptance Criteria

  • specify core inventory --json emits {commands, templates, scripts} arrays; each entry has id, name, description, sourcePath, and the kind-specific fields (artifact/optional/handoffs on commands, runtimes on scripts).
  • id values follow the core:_:kind:name scheme (matching the stable-id scheme used by the companion specify preset info --json / specify extension info --json / specify artifact info --json commands) so consumers can join this inventory to those commands' output via lookupId.
  • Command list is populated from CORE_COMMAND_NAMES; template and script lists come from a scan that reuses the existing _locate_core_pack() / _repo_root() canonical asset resolvers — not Path(__file__) arithmetic. (Path-arithmetic discovery has silently broken in this codebase once already; see refactor: move extension command handlers to extensions/_commands.py (PR-7/8) #3014 / Bug: extensions core-command discovery is dead — off-by-one paths after the #3014 module move #3274.) No filesystem paths outside the package leak into the output.
  • Deterministic: same SpecKit version always produces identical output (stable order + stable ids).
  • Non-zero exit + stderr JSON error on packaging problems (missing shipped file, malformed frontmatter).
  • Tests: enumeration parity with CORE_COMMAND_NAMES for commands; count parity with the shipped file set for templates/scripts; lookupId cross-reference with the companion specify artifact info --json output on a bare-bones project.
  • Docs: new specify core inventory section in the CLI reference plus a note that it complements the companion specify artifact command.

Additional Context

Direct replacement for plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/pipeline/canonical.mjs and composition/collect.mjs::loadCoreInventory in github/spec-kit-copilot. Depends on the companion "stable id / lookupId" issue; benefits from the companion "artifact/optional/handoffs" issue.

Metadata

Metadata

Assignees

Labels

enhancementfeature-assessRun the Spec Kit idea-assessment pipeline on this feature requestfeature-goFeature assessment verdict: go — ready to hand off to /speckit.specify

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions