[Feature]: Add --json output to preset info and extension info with fully-expanded per-contribution detail (per-pack detail view; complements list --json summary counts)
- Lenguaje dominante
- Python
- Estrellas
- 137k
- Forks
- 12.3k
- Merge medio
- 2 d 12 h
- PR fusionados (30 d)
- 159
Descripción
### Problem Statement
Today `specify preset info ` and `specify extension info ` emit only text. `speckit-wizard-canvas` reads the raw `preset.yml` / `extension.yml` files with `js-yaml` and re-normalizes the shape itself in `composition/collect.mjs::parseProvidesEntries` and `::parseHookDeclarations` — including strategy inference from shorthand keys (`replaces` vs. `wraps` vs. `prepends` vs. `appends`), hook-phase normalization (`phase` vs. `trigger`, `command` vs. `targetCommand`), and script-runtime inference by globbing `scripts/{bash,powershell,python}/*.{sh,ps1,py}`. All of this is server-side data being reconstructed on the client.
Downstream consumers need the structured shape returned by the CLI itself so they can stop reconstructing it.
**How this differs from the companion `preset list --json` / `extension list --json` issue.** The `list` variant operates on **the collection of installed packs** and returns a **JSON array**, one row per pack, where `provides` is **just integer counts** (`{ commands: 4, templates: 2, scripts: 1, hooks: 3 }`) — a summary/catalog view suitable for "here's every preset the project has". `info` operates on **one pack, addressed by id**, and returns a **single JSON object** where `provides` is **fully expanded** — every command, template, script, and hook enumerated with its full per-contribution schema (`id`, `name`, `description`, `artifact`, `optional`, `handoffs`, `strategy`, `sourcePath`, `runtimes`, etc.). A wizard rendering "here's what `speckit.git` contributes to my project" needs the `info` detail; `list`'s counts are not enough. Both surfaces are required; neither is a subset of the other.
### Proposed Solution
Add `--json` to both `info` commands. Output shape:
```json
{
"id": "…", "name": "…", "description": "…", "version": "…",
"author": "…", "priority": 100, "enabled": true, "source": { "…": "…" },
"commands": [
{ "id": "…", "name": "speckit.plan", "description": "…",
"artifact": "specs/{feature}/plan.md", "optional": false,
"handoffs": [ { "to": "speckit.tasks", "when": "…", "message": "…" } ],
"strategy": "wrap",
"source": { "layer": "preset", "presetId": "…" },
"sourcePath": "commands/speckit.plan.md" }
],
"templates": [
{ "id": "…", "name": "…", "description": "…",
"strategy": "replace",
"source": { "…": "…" }, "sourcePath": "templates/…" }
],
"scripts": [
{ "id": "…", "name": "…", "description": "…",
"strategy": "replace",
"source": { "…": "…" }, "sourcePath": "scripts/bash/…",
"runtimes": ["bash", "powershell", "python"] }
],
"hooks": [
{ "id": "…", "name": "…", "description": "…",
"trigger": "before_speckit.plan", "targetCommand": "speckit.git.checklist",
"sourcePath": "commands/speckit.git.checklist.md",
"optional": false, "priority": 10 }
]
}
```
Notes:
- Preset objects omit `hooks`.
- Extension `commands`/`templates`/`scripts` entries omit `strategy` (they are always `replace`, per the replace-only rule enforced at `extensions/__init__.py:622-626`).
- `id` values use the stable-id scheme from the companion "stable id / lookupId" issue; the `id` on a per-contribution entry is what the companion `specify artifact info --json` stack's `lookupId` points at.
- `artifact`, `optional`, `handoffs` on commands come from the companion "artifact/optional/handoffs" issue.
- `runtimes` on scripts comes directly from the manifest (extensions post-#4010; add the same field to preset script entries for parity).
- All shorthand-key normalization (`replaces`/`wraps`/`prepends`/`appends` → `strategy: "replace"|"wrap"|"prepend"|"append"`) is done server-side.
### Alternatives Considered
- Fold everything into the companion `specify artifact info --json` command. Rejected — `artifact info` is per-artifact (walks one composition stack across all installed packs); `info --json` is per-source (walks one preset/extension across all its contributions). Both are needed and neither is a subset of the other.
- Fold everything into the companion `preset list --json` / `extension list --json`. Rejected — that surface is per-collection with summary counts; `info` is per-pack with full expansion. Different shape, different call pattern, different use cases (see Problem Statement).
- Emit YAML. Rejected — the whole point is to let the wizard drop `js-yaml`.
- Leave hook-phase / strategy shorthand un-normalized. Rejected — every consumer would reproduce the client-side logic from `composition/collect.mjs` and drift over time.
### Component
Specify CLI (initialization, commands)
### AI Agent (if applicable)
Not applicable
### Use Cases
1. `speckit-wizard-canvas` deletes `composition/collect.mjs::parseProvidesEntries` and `::parseHookDeclarations`, replacing them with `JSON.parse(execFileSync("specify", ["preset", "info", "", "--json"]))` / `specify extension info --json`. This is the change that removes the `js-yaml` dependency.
2. An IDE plugin renders hover-cards on command names by looking up the `commands[]` entry (description, `artifact`, `handoffs`).
3. A pre-commit check walks `extension info --json`'s `hooks[]` to warn when two extensions register the same `trigger` at the same `priority`.
### Acceptance Criteria
- [ ] `specify preset info --json` and `specify extension info --json` emit a single JSON object with top-level fields matching the companion `list --json` issue (`id`, `name`, `description`, `version`, `author`, `priority`, `enabled`, `source`) **plus** fully-expanded `commands`, `templates`, `scripts` arrays (and `hooks` for extensions) — not integer counts.
- [ ] Command entries include `artifact`, `optional`, `handoffs` (from the companion command-fields issue).
- [ ] Script entries include `runtimes` for both presets and extensions.
- [ ] Preset command/template/script entries include `strategy`; extension entries omit it (or set to `"replace"` for informational purposes).
- [ ] Shorthand keys (`replaces`/`wraps`/`prepends`/`appends`) are normalized server-side into `strategy`.
- [ ] Hook entries include `trigger`, `targetCommand`, `sourcePath`, `optional`, `priority` — `phase`/`command` shorthand normalized.
- [ ] All `id` values follow the stable-id scheme and are stable across reinstalls, matching the `lookupId` values emitted by `specify artifact info --json`.
- [ ] Unknown id → non-zero exit + stderr JSON error.
- [ ] Tests: schema round-trip, strategy normalization (all four shorthand forms), hook shorthand normalization, extension replace-only enforcement, handoffs frontmatter-merge, and `id` cross-reference with `specify artifact info --json` output.
- [ ] Docs: `preset info` / `extension info` sections in the CLI reference show the `--json` shape and normalization rules, plus a note contrasting `info --json` (per-pack, full expansion) with `list --json` (per-collection, count summary).
### Additional Context
Direct replacement for `plugins/spec-kit-copilot-wizard/extensions/speckit-wizard-canvas/composition/collect.mjs::parseProvidesEntries` and `::parseHookDeclarations` in `github/spec-kit-copilot`. Depends on the companion "artifact/optional/handoffs" and "stable id / lookupId" issues; benefits from the companion "structured source provenance" issue.
Guía de contribución
Línea de trabajo
Comienza con los puntos de entrada de CLI `preset info --json` y `extension info --json`, y después sigue la normalización existente en `composition/collect.mjs::parseProvidesEntries` y `::parseHookDeclarations`; revisa `extensions/__init__.py:622-626` para conocer el comportamiento que solo permite reemplazos. Compara la salida con los contratos complementarios de `list --json` y `artifact info --json`. Se considera terminado cuando los esquemas ampliados especificados, los IDs estables, la normalización, los errores, las pruebas y la documentación de referencia de la CLI estén completos.
Escrito por el modelo de indexación a partir del texto del issue.
Evaluación
- Stack tecnológico
- javascript, python
- Área
- cli, documentation, testing
- Tipo de issue
- Nueva funcionalidad
- Dificultad
- 5/5
- Tiempo estimado
- Más de una semana
- Estado de actividad
- Activo
- Claridad
- Bastante claro
- Aptitud para principiantes
- 45/100