* feat(#1430): versioned capability manifest + native stamping (ADR-1244 Phase 1) Make the capability manifest versioned — the data substrate the Capability Ecosystem (ADR-1244) keys off: - capability.json gains a REQUIRED semver `version` plus the optional ecosystem envelope (`engines.gsd`, `compatVersions`, `integrity`, `provenance`); the build-time conformance validator enforces them via a new `validateVersionEnvelope()` (exported for the Phase 2 runtime overlay). - All 32 native capabilities stamped with `version` (= package version, lockstep) + `engines.gsd`; `sync-manifest-versions.cjs` gains a glob sweep that keeps them in sync, and the issue-844 regression guard is extended. - Strict SemVer 2.0.0 grammar blocks metacharacter/space/unicode smuggling in version strings; range/integrity fields are shape-validated (satisfaction and the load-time gate are deferred to Phase 2/4). - Capability rel-paths emitted forward-slash for cross-platform git correctness. Closes #1430 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(#1430): add changeset for versioned capability manifest Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
5
.changeset/kind-dogs-dart.md
Normal file
5
.changeset/kind-dogs-dart.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Added
|
||||
pr: 1436
|
||||
---
|
||||
**Capability manifests are now versioned** — every `capability.json` carries a required semver `version`, plus optional `engines.gsd`, `compatVersions`, `integrity` and `provenance` fields, enforced by the capability conformance validator. First-party capabilities are version-stamped in lockstep with the GSD release. Foundation (ADR-1244 Phase 1) for installing, upgrading, and removing capabilities in later releases.
|
||||
@@ -1,12 +1,23 @@
|
||||
{
|
||||
"id": "ai-integration",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "AI design contract",
|
||||
"description": "AI-SPEC design contract workflow for phases that build AI systems; owns the AI integration command, agents, and workflow.ai_integration_phase activation key.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
|
||||
"skills": ["ai-integration-phase"],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
],
|
||||
"unsupported": []
|
||||
},
|
||||
"skills": [
|
||||
"ai-integration-phase"
|
||||
],
|
||||
"agents": [
|
||||
"gsd-framework-selector",
|
||||
"gsd-ai-researcher",
|
||||
@@ -24,9 +35,15 @@
|
||||
"steps": [
|
||||
{
|
||||
"point": "plan:pre",
|
||||
"ref": { "skill": "ai-integration-phase" },
|
||||
"produces": ["AI-SPEC.md"],
|
||||
"consumes": ["CONTEXT.md"],
|
||||
"ref": {
|
||||
"skill": "ai-integration-phase"
|
||||
},
|
||||
"produces": [
|
||||
"AI-SPEC.md"
|
||||
],
|
||||
"consumes": [
|
||||
"CONTEXT.md"
|
||||
],
|
||||
"when": "workflow.ai_integration_phase",
|
||||
"onError": "skip"
|
||||
}
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
{
|
||||
"id": "antigravity",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Antigravity",
|
||||
"description": "Google Antigravity IDE — nested under ~/.gemini/antigravity; probed across 1.x and 2.x layouts; Gemini hook event dialect; nested skill layout; tier-1 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home-nested",
|
||||
"name": "antigravity",
|
||||
"parent": ".gemini",
|
||||
"env": ["ANTIGRAVITY_CONFIG_DIR"],
|
||||
"probe": ["antigravity", "antigravity-ide", "antigravity-cli"]
|
||||
"env": [
|
||||
"ANTIGRAVITY_CONFIG_DIR"
|
||||
],
|
||||
"probe": [
|
||||
"antigravity",
|
||||
"antigravity-ide",
|
||||
"antigravity-cli"
|
||||
]
|
||||
},
|
||||
"configFormat": "settings-json",
|
||||
"artifactLayout": {
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
{
|
||||
"id": "audit",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Audit",
|
||||
"description": "Open-artifact audit and UAT-gap audit for milestone close gates; exposes `gsd-tools audit-uat` (cross-phase UAT outstanding items) and `gsd-tools audit-open` (structured open-artifact scan across debug, tasks, threads, todos, seeds, UAT, verification, context-questions).",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
],
|
||||
"unsupported": []
|
||||
},
|
||||
"skills": [],
|
||||
"agents": [],
|
||||
"config": {},
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
{
|
||||
"id": "augment",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Augment Code",
|
||||
"description": "Augment Code CLI — commands + nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
"name": ".augment",
|
||||
"env": ["AUGMENT_CONFIG_DIR"]
|
||||
"env": [
|
||||
"AUGMENT_CONFIG_DIR"
|
||||
]
|
||||
},
|
||||
"configFormat": "settings-json",
|
||||
"artifactLayout": {
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
{
|
||||
"id": "claude",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Claude Code",
|
||||
"description": "Anthropic Claude Code — primary development runtime; tier-1 support with full hook surface and skills-based global install.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
"name": ".claude",
|
||||
"env": ["CLAUDE_CONFIG_DIR"]
|
||||
"env": [
|
||||
"CLAUDE_CONFIG_DIR"
|
||||
]
|
||||
},
|
||||
"configFormat": "settings-json",
|
||||
"artifactLayout": {
|
||||
@@ -50,6 +56,11 @@
|
||||
"installSurface": "settings-json",
|
||||
"writesSharedSettings": true,
|
||||
"permissionWriter": null,
|
||||
"extendedHookEvents": ["SubagentStop", "Stop", "PreCompact", "FileChanged"]
|
||||
"extendedHookEvents": [
|
||||
"SubagentStop",
|
||||
"Stop",
|
||||
"PreCompact",
|
||||
"FileChanged"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
{
|
||||
"id": "cline",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Cline",
|
||||
"description": "Cline (VS Code extension) — global-only nested-skill layout; cline-rules hook surface (.clinerules); no hook events emitted; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
"name": ".cline",
|
||||
"env": ["CLINE_CONFIG_DIR"]
|
||||
"env": [
|
||||
"CLINE_CONFIG_DIR"
|
||||
]
|
||||
},
|
||||
"configFormat": "markdown-dir",
|
||||
"artifactLayout": {
|
||||
|
||||
@@ -1,13 +1,27 @@
|
||||
{
|
||||
"id": "code-review",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Code review",
|
||||
"description": "Source-file code review and review-fix workflow support for completed execution work.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
|
||||
"skills": ["code-review"],
|
||||
"agents": ["gsd-code-reviewer", "gsd-code-fixer"],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
],
|
||||
"unsupported": []
|
||||
},
|
||||
"skills": [
|
||||
"code-review"
|
||||
],
|
||||
"agents": [
|
||||
"gsd-code-reviewer",
|
||||
"gsd-code-fixer"
|
||||
],
|
||||
"hooks": [],
|
||||
"config": {
|
||||
"workflow.code_review": {
|
||||
@@ -17,7 +31,11 @@
|
||||
},
|
||||
"workflow.code_review_depth": {
|
||||
"type": "enum",
|
||||
"values": ["quick", "standard", "deep"],
|
||||
"values": [
|
||||
"quick",
|
||||
"standard",
|
||||
"deep"
|
||||
],
|
||||
"default": "standard",
|
||||
"description": "Default depth for code review when no --depth override is supplied."
|
||||
}
|
||||
@@ -25,9 +43,15 @@
|
||||
"steps": [
|
||||
{
|
||||
"point": "execute:post",
|
||||
"ref": { "skill": "code-review" },
|
||||
"produces": ["REVIEW.md"],
|
||||
"consumes": ["SUMMARY.md"],
|
||||
"ref": {
|
||||
"skill": "code-review"
|
||||
},
|
||||
"produces": [
|
||||
"REVIEW.md"
|
||||
],
|
||||
"consumes": [
|
||||
"SUMMARY.md"
|
||||
],
|
||||
"when": "workflow.code_review",
|
||||
"onError": "skip"
|
||||
}
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
{
|
||||
"id": "codebuddy",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "CodeBuddy",
|
||||
"description": "CodeBuddy (Tencent) — converted commands + skills artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
"name": ".codebuddy",
|
||||
"env": ["CODEBUDDY_CONFIG_DIR"]
|
||||
"env": [
|
||||
"CODEBUDDY_CONFIG_DIR"
|
||||
]
|
||||
},
|
||||
"configFormat": "settings-json",
|
||||
"artifactLayout": {
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
{
|
||||
"id": "codex",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "OpenAI Codex CLI",
|
||||
"description": "OpenAI Codex CLI — shell-var command style; per-agent sandbox tiers; config.toml + hooks.json hook surface; tier-1 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
"name": ".codex",
|
||||
"env": ["CODEX_HOME"]
|
||||
"env": [
|
||||
"CODEX_HOME"
|
||||
]
|
||||
},
|
||||
"configFormat": "toml",
|
||||
"artifactLayout": {
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
{
|
||||
"id": "copilot",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "GitHub Copilot",
|
||||
"description": "GitHub Copilot (VS Code) — markdown config format; copilot-inline hook surface; no hook events emitted; flat skill nesting (unconfirmed recursive loader); tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
"name": ".copilot",
|
||||
"env": ["COPILOT_CONFIG_DIR", "COPILOT_HOME"]
|
||||
"env": [
|
||||
"COPILOT_CONFIG_DIR",
|
||||
"COPILOT_HOME"
|
||||
]
|
||||
},
|
||||
"configFormat": "markdown",
|
||||
"artifactLayout": {
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
{
|
||||
"id": "cursor",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Cursor",
|
||||
"description": "Cursor IDE — skills + converted commands artifact layout; hooks.json surface; Claude hook event dialect; recursive skill loader (flat nesting); tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
"name": ".cursor",
|
||||
"env": ["CURSOR_CONFIG_DIR"]
|
||||
"env": [
|
||||
"CURSOR_CONFIG_DIR"
|
||||
]
|
||||
},
|
||||
"configFormat": "none",
|
||||
"artifactLayout": {
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
{
|
||||
"id": "drift",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Drift detection gates",
|
||||
"description": "Post-execution drift detection gates that run after each wave completes. Provides two gates at execute:wave:post: a blocking schema drift gate (detects schema files changed without a database push) and a non-blocking codebase drift gate (detects structural additions not reflected in STRUCTURE.md).",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
],
|
||||
"unsupported": []
|
||||
},
|
||||
"skills": [],
|
||||
"agents": [],
|
||||
"hooks": [],
|
||||
@@ -17,7 +26,10 @@
|
||||
},
|
||||
"workflow.drift_action": {
|
||||
"type": "enum",
|
||||
"values": ["warn", "auto-remap"],
|
||||
"values": [
|
||||
"warn",
|
||||
"auto-remap"
|
||||
],
|
||||
"default": "warn",
|
||||
"description": "Action taken by the codebase drift gate when the threshold is exceeded: warn (advisory message) or auto-remap (spawn gsd-codebase-mapper agent to refresh STRUCTURE.md)."
|
||||
},
|
||||
@@ -32,14 +44,18 @@
|
||||
"gates": [
|
||||
{
|
||||
"point": "execute:wave:post",
|
||||
"check": { "query": "verify.schema-drift" },
|
||||
"check": {
|
||||
"query": "verify.schema-drift"
|
||||
},
|
||||
"when": "workflow.schema_drift_gate",
|
||||
"blocking": true,
|
||||
"onError": "skip"
|
||||
},
|
||||
{
|
||||
"point": "execute:wave:post",
|
||||
"check": { "query": "verify.codebase-drift" },
|
||||
"check": {
|
||||
"query": "verify.codebase-drift"
|
||||
},
|
||||
"when": "workflow.schema_drift_gate",
|
||||
"blocking": false,
|
||||
"onError": "skip"
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
{
|
||||
"id": "gap-analysis",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Post-planning gap analysis",
|
||||
"description": "Proactive, non-blocking post-planning coverage report. After all PLAN.md files are generated, cross-references every REQ-ID and D-ID from REQUIREMENTS.md and CONTEXT.md against plan bodies. Emits a Source | Item | Status table. Does not block phase advancement.",
|
||||
"tier": "standard",
|
||||
"requires": [],
|
||||
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
],
|
||||
"unsupported": []
|
||||
},
|
||||
"skills": [],
|
||||
"agents": [],
|
||||
"hooks": [],
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
{
|
||||
"id": "gemini",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Gemini CLI",
|
||||
"description": "Google Gemini CLI — commands-only artifact layout (TOML); Gemini hook event dialect; settings-json hook surface; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
"name": ".gemini",
|
||||
"env": ["GEMINI_CONFIG_DIR"]
|
||||
"env": [
|
||||
"GEMINI_CONFIG_DIR"
|
||||
]
|
||||
},
|
||||
"configFormat": "settings-json",
|
||||
"artifactLayout": {
|
||||
@@ -42,6 +48,10 @@
|
||||
"installSurface": "settings-json",
|
||||
"writesSharedSettings": true,
|
||||
"permissionWriter": null,
|
||||
"extendedHookEvents": ["BeforeAgent", "AfterAgent", "BeforeModel"]
|
||||
"extendedHookEvents": [
|
||||
"BeforeAgent",
|
||||
"AfterAgent",
|
||||
"BeforeModel"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
{
|
||||
"id": "graphify",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Knowledge graph",
|
||||
"description": "Build, query, and inspect the project knowledge graph in `.planning/graphs/`; exposes graphify CLI subcommands (build, query, status, diff) and the /gsd-graphify skill.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
|
||||
"skills": ["graphify"],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
],
|
||||
"unsupported": []
|
||||
},
|
||||
"skills": [
|
||||
"graphify"
|
||||
],
|
||||
"agents": [],
|
||||
"activationKey": "graphify.enabled",
|
||||
"config": {
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
{
|
||||
"id": "hermes",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Hermes Agent",
|
||||
"description": "Hermes Agent (NousResearch) — skills nest under skills/gsd/ category bucket; nested skill layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
"name": ".hermes",
|
||||
"env": ["HERMES_HOME"]
|
||||
"env": [
|
||||
"HERMES_HOME"
|
||||
]
|
||||
},
|
||||
"configFormat": "settings-json",
|
||||
"artifactLayout": {
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
{
|
||||
"id": "intel",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Codebase intelligence",
|
||||
"description": "Code-intelligence store for codebase querying, diff, snapshot, and API-surface extraction; exposes `gsd-tools intel` subcommands (query, status, update, diff, snapshot, patch-meta, validate, extract-exports, api-surface) and backs `/gsd-map-codebase` and `gsd-intel-updater`.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
],
|
||||
"unsupported": []
|
||||
},
|
||||
"skills": [],
|
||||
"agents": [],
|
||||
"activationKey": "intel.enabled",
|
||||
@@ -27,8 +36,12 @@
|
||||
"steps": [
|
||||
{
|
||||
"point": "plan:pre",
|
||||
"ref": { "command": "intel api-surface" },
|
||||
"produces": [".planning/intel/API-SURFACE.md"],
|
||||
"ref": {
|
||||
"command": "intel api-surface"
|
||||
},
|
||||
"produces": [
|
||||
".planning/intel/API-SURFACE.md"
|
||||
],
|
||||
"consumes": [],
|
||||
"when": "intel.enabled",
|
||||
"onError": "skip"
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
{
|
||||
"id": "kilo",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Kilo Code",
|
||||
"description": "Kilo Code — XDG-based config dir; global skills at ~/.kilo/skills (separate from XDG config); flat command/ + skills artifact layout; no lifecycle hook registration; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "xdg",
|
||||
"name": "kilo",
|
||||
"env": ["KILO_CONFIG_DIR", "KILO_CONFIG", "XDG_CONFIG_HOME"],
|
||||
"env": [
|
||||
"KILO_CONFIG_DIR",
|
||||
"KILO_CONFIG",
|
||||
"XDG_CONFIG_HOME"
|
||||
],
|
||||
"skillsHome": {
|
||||
"kind": "dot-home",
|
||||
"name": ".kilo",
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
{
|
||||
"id": "kimi",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Kimi CLI",
|
||||
"description": "Kimi CLI (Moonshot AI) — generic agents root at ~/.config/agents; skills + kimi-agents artifact layout; no hook surface; no hook events; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "generic-agents-root",
|
||||
"name": "agents",
|
||||
"env": ["KIMI_CONFIG_DIR"],
|
||||
"probe": ["~/.config/agents", "~/.agents"],
|
||||
"env": [
|
||||
"KIMI_CONFIG_DIR"
|
||||
],
|
||||
"probe": [
|
||||
"~/.config/agents",
|
||||
"~/.agents"
|
||||
],
|
||||
"probeExists": "skills"
|
||||
},
|
||||
"configFormat": "none",
|
||||
|
||||
@@ -1,13 +1,27 @@
|
||||
{
|
||||
"id": "mempalace",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "MemPalace memory",
|
||||
"description": "Cross-session, cross-project memory: deliberate recall before discuss/plan and verbatim capture + temporal-KG sync at phase boundaries, via the MemPalace MCP server and CLI.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
|
||||
"skills": ["mempalace-recall", "mempalace-capture"],
|
||||
"agents": ["gsd-mempalace-curator"],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
],
|
||||
"unsupported": []
|
||||
},
|
||||
"skills": [
|
||||
"mempalace-recall",
|
||||
"mempalace-capture"
|
||||
],
|
||||
"agents": [
|
||||
"gsd-mempalace-curator"
|
||||
],
|
||||
"hooks": [],
|
||||
"config": {
|
||||
"mempalace.enabled": {
|
||||
@@ -17,7 +31,11 @@
|
||||
},
|
||||
"mempalace.memory_mode": {
|
||||
"type": "enum",
|
||||
"values": ["augment", "kg_backend", "replace"],
|
||||
"values": [
|
||||
"augment",
|
||||
"kg_backend",
|
||||
"replace"
|
||||
],
|
||||
"default": "augment",
|
||||
"description": "How MemPalace relates to GSD native memory. Only 'augment' (additive) is implemented today; 'kg_backend' and 'replace' are forward-declared (routing seam not yet built) and currently behave as 'augment'."
|
||||
},
|
||||
@@ -65,41 +83,63 @@
|
||||
"steps": [
|
||||
{
|
||||
"point": "discuss:post",
|
||||
"ref": { "skill": "mempalace-capture" },
|
||||
"ref": {
|
||||
"skill": "mempalace-capture"
|
||||
},
|
||||
"produces": [],
|
||||
"consumes": ["CONTEXT.md"],
|
||||
"consumes": [
|
||||
"CONTEXT.md"
|
||||
],
|
||||
"when": "mempalace.enabled",
|
||||
"onError": "skip"
|
||||
},
|
||||
{
|
||||
"point": "plan:pre",
|
||||
"ref": { "skill": "mempalace-recall" },
|
||||
"produces": ["MEMORY-RECALL.md"],
|
||||
"consumes": ["CONTEXT.md"],
|
||||
"ref": {
|
||||
"skill": "mempalace-recall"
|
||||
},
|
||||
"produces": [
|
||||
"MEMORY-RECALL.md"
|
||||
],
|
||||
"consumes": [
|
||||
"CONTEXT.md"
|
||||
],
|
||||
"when": "mempalace.enabled",
|
||||
"onError": "skip"
|
||||
},
|
||||
{
|
||||
"point": "plan:post",
|
||||
"ref": { "skill": "mempalace-capture" },
|
||||
"ref": {
|
||||
"skill": "mempalace-capture"
|
||||
},
|
||||
"produces": [],
|
||||
"consumes": ["PLAN.md"],
|
||||
"consumes": [
|
||||
"PLAN.md"
|
||||
],
|
||||
"when": "mempalace.enabled",
|
||||
"onError": "skip"
|
||||
},
|
||||
{
|
||||
"point": "verify:post",
|
||||
"ref": { "skill": "mempalace-capture" },
|
||||
"ref": {
|
||||
"skill": "mempalace-capture"
|
||||
},
|
||||
"produces": [],
|
||||
"consumes": ["SUMMARY.md"],
|
||||
"consumes": [
|
||||
"SUMMARY.md"
|
||||
],
|
||||
"when": "mempalace.enabled",
|
||||
"onError": "skip"
|
||||
},
|
||||
{
|
||||
"point": "ship:post",
|
||||
"ref": { "agent": "gsd-mempalace-curator" },
|
||||
"ref": {
|
||||
"agent": "gsd-mempalace-curator"
|
||||
},
|
||||
"produces": [],
|
||||
"consumes": ["UAT.md"],
|
||||
"consumes": [
|
||||
"UAT.md"
|
||||
],
|
||||
"when": "mempalace.enabled",
|
||||
"onError": "skip"
|
||||
}
|
||||
@@ -108,7 +148,9 @@
|
||||
{
|
||||
"point": "discuss:pre",
|
||||
"into": "orchestrator",
|
||||
"fragment": { "path": "fragments/recall-discuss.md" },
|
||||
"fragment": {
|
||||
"path": "fragments/recall-discuss.md"
|
||||
},
|
||||
"produces": [],
|
||||
"consumes": [],
|
||||
"when": "mempalace.enabled",
|
||||
@@ -117,7 +159,9 @@
|
||||
{
|
||||
"point": "execute:wave:post",
|
||||
"into": "verifier",
|
||||
"fragment": { "path": "fragments/capture-problems.md" },
|
||||
"fragment": {
|
||||
"path": "fragments/capture-problems.md"
|
||||
},
|
||||
"produces": [],
|
||||
"consumes": [],
|
||||
"when": "mempalace.enabled",
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
{
|
||||
"id": "nyquist",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Nyquist validation",
|
||||
"description": "Validation coverage audit that maps executed work back to tests and manual-only evidence.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
|
||||
"skills": ["validate-phase"],
|
||||
"agents": ["gsd-nyquist-auditor"],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
],
|
||||
"unsupported": []
|
||||
},
|
||||
"skills": [
|
||||
"validate-phase"
|
||||
],
|
||||
"agents": [
|
||||
"gsd-nyquist-auditor"
|
||||
],
|
||||
"hooks": [],
|
||||
"config": {
|
||||
"workflow.nyquist_validation": {
|
||||
@@ -19,9 +32,15 @@
|
||||
"steps": [
|
||||
{
|
||||
"point": "verify:post",
|
||||
"ref": { "skill": "validate-phase" },
|
||||
"produces": ["VALIDATION.md"],
|
||||
"consumes": ["SUMMARY.md"],
|
||||
"ref": {
|
||||
"skill": "validate-phase"
|
||||
},
|
||||
"produces": [
|
||||
"VALIDATION.md"
|
||||
],
|
||||
"consumes": [
|
||||
"SUMMARY.md"
|
||||
],
|
||||
"when": "workflow.nyquist_validation",
|
||||
"onError": "halt"
|
||||
}
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
{
|
||||
"id": "opencode",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "OpenCode",
|
||||
"description": "OpenCode — XDG-based config dir; flat command/ + skills artifact layout; settings-json config format; no lifecycle hook registration; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "xdg",
|
||||
"name": "opencode",
|
||||
"env": ["OPENCODE_CONFIG_DIR", "OPENCODE_CONFIG", "XDG_CONFIG_HOME"]
|
||||
"env": [
|
||||
"OPENCODE_CONFIG_DIR",
|
||||
"OPENCODE_CONFIG",
|
||||
"XDG_CONFIG_HOME"
|
||||
]
|
||||
},
|
||||
"configFormat": "settings-json",
|
||||
"artifactLayout": {
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
{
|
||||
"id": "pattern-mapper",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Pattern mapping",
|
||||
"description": "Optional codebase-pattern mapping before planning; owns the pattern mapper agent and workflow.pattern_mapper activation key.",
|
||||
"tier": "full",
|
||||
"requires": ["research"],
|
||||
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
|
||||
"requires": [
|
||||
"research"
|
||||
],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
],
|
||||
"unsupported": []
|
||||
},
|
||||
"skills": [],
|
||||
"agents": ["gsd-pattern-mapper"],
|
||||
"agents": [
|
||||
"gsd-pattern-mapper"
|
||||
],
|
||||
"hooks": [],
|
||||
"config": {
|
||||
"workflow.pattern_mapper": {
|
||||
@@ -19,10 +32,18 @@
|
||||
"steps": [
|
||||
{
|
||||
"point": "plan:pre",
|
||||
"ref": { "agent": "gsd-pattern-mapper" },
|
||||
"fragment": { "path": "fragments/plan-pre.md" },
|
||||
"produces": ["PATTERNS.md"],
|
||||
"consumes": ["RESEARCH.md"],
|
||||
"ref": {
|
||||
"agent": "gsd-pattern-mapper"
|
||||
},
|
||||
"fragment": {
|
||||
"path": "fragments/plan-pre.md"
|
||||
},
|
||||
"produces": [
|
||||
"PATTERNS.md"
|
||||
],
|
||||
"consumes": [
|
||||
"RESEARCH.md"
|
||||
],
|
||||
"when": "workflow.pattern_mapper",
|
||||
"onError": "skip"
|
||||
}
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
{
|
||||
"id": "profile-pipeline",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Developer profiling pipeline",
|
||||
"description": "Developer behavioral profiling from Claude Code session history; scans session JSONL files, extracts and samples user messages, and generates profile artifacts (USER-PROFILE.md, dev-preferences.md, CLAUDE.md sections). Exposes eight `gsd-tools` commands: scan-sessions, extract-messages, profile-sample (pipeline phase) and write-profile, profile-questionnaire, generate-dev-preferences, generate-claude-profile, generate-claude-md (output phase). Backs the /gsd-profile-user skill and gsd-user-profiler agent.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
|
||||
"skills": ["profile-user"],
|
||||
"agents": ["gsd-user-profiler"],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
],
|
||||
"unsupported": []
|
||||
},
|
||||
"skills": [
|
||||
"profile-user"
|
||||
],
|
||||
"agents": [
|
||||
"gsd-user-profiler"
|
||||
],
|
||||
"config": {
|
||||
"profile-pipeline.enabled": {
|
||||
"type": "boolean",
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
{
|
||||
"id": "qwen",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Qwen Code",
|
||||
"description": "Qwen Code (Alibaba) — nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
"name": ".qwen",
|
||||
"env": ["QWEN_CONFIG_DIR"]
|
||||
"env": [
|
||||
"QWEN_CONFIG_DIR"
|
||||
]
|
||||
},
|
||||
"configFormat": "settings-json",
|
||||
"artifactLayout": {
|
||||
@@ -42,6 +48,10 @@
|
||||
"installSurface": "settings-json",
|
||||
"writesSharedSettings": true,
|
||||
"permissionWriter": null,
|
||||
"extendedHookEvents": ["SubagentStop", "Stop", "PreCompact"]
|
||||
"extendedHookEvents": [
|
||||
"SubagentStop",
|
||||
"Stop",
|
||||
"PreCompact"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,24 @@
|
||||
{
|
||||
"id": "research",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Phase research",
|
||||
"description": "Optional phase research before planning; owns the phase researcher agent and workflow.research activation key.",
|
||||
"tier": "standard",
|
||||
"requires": [],
|
||||
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
],
|
||||
"unsupported": []
|
||||
},
|
||||
"skills": [],
|
||||
"agents": ["gsd-phase-researcher"],
|
||||
"agents": [
|
||||
"gsd-phase-researcher"
|
||||
],
|
||||
"hooks": [],
|
||||
"config": {
|
||||
"workflow.research": {
|
||||
@@ -19,10 +30,18 @@
|
||||
"steps": [
|
||||
{
|
||||
"point": "plan:pre",
|
||||
"ref": { "agent": "gsd-phase-researcher" },
|
||||
"fragment": { "path": "fragments/plan-pre.md" },
|
||||
"produces": ["RESEARCH.md"],
|
||||
"consumes": ["CONTEXT.md"],
|
||||
"ref": {
|
||||
"agent": "gsd-phase-researcher"
|
||||
},
|
||||
"fragment": {
|
||||
"path": "fragments/plan-pre.md"
|
||||
},
|
||||
"produces": [
|
||||
"RESEARCH.md"
|
||||
],
|
||||
"consumes": [
|
||||
"CONTEXT.md"
|
||||
],
|
||||
"when": "workflow.research",
|
||||
"onError": "skip"
|
||||
}
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
{
|
||||
"id": "schema-gate",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Schema push detection gate",
|
||||
"description": "Detects ORM schema-relevant files in the phase scope during planning and injects a mandatory [BLOCKING] schema push task into the plan. Prevents false-positive verification where build/types pass because TypeScript types come from config, not the live database.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
],
|
||||
"unsupported": []
|
||||
},
|
||||
"skills": [],
|
||||
"agents": [],
|
||||
"hooks": [],
|
||||
@@ -21,9 +30,13 @@
|
||||
{
|
||||
"point": "plan:pre",
|
||||
"into": "planner",
|
||||
"fragment": { "path": "fragments/plan-pre.md" },
|
||||
"fragment": {
|
||||
"path": "fragments/plan-pre.md"
|
||||
},
|
||||
"produces": [],
|
||||
"consumes": ["CONTEXT.md"],
|
||||
"consumes": [
|
||||
"CONTEXT.md"
|
||||
],
|
||||
"when": "workflow.schema_push_detection",
|
||||
"onError": "skip"
|
||||
}
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
{
|
||||
"id": "security",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Security enforcement",
|
||||
"description": "Threat mitigation verification and ship-time security blocking for phases with security enforcement enabled.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
|
||||
"skills": ["secure-phase"],
|
||||
"agents": ["gsd-security-auditor"],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
],
|
||||
"unsupported": []
|
||||
},
|
||||
"skills": [
|
||||
"secure-phase"
|
||||
],
|
||||
"agents": [
|
||||
"gsd-security-auditor"
|
||||
],
|
||||
"hooks": [],
|
||||
"config": {
|
||||
"workflow.security_enforcement": {
|
||||
@@ -22,7 +35,13 @@
|
||||
},
|
||||
"workflow.security_block_on": {
|
||||
"type": "enum",
|
||||
"values": ["critical", "high", "medium", "low", "none"],
|
||||
"values": [
|
||||
"critical",
|
||||
"high",
|
||||
"medium",
|
||||
"low",
|
||||
"none"
|
||||
],
|
||||
"default": "high",
|
||||
"description": "Minimum open threat severity that blocks advancement."
|
||||
}
|
||||
@@ -30,9 +49,15 @@
|
||||
"steps": [
|
||||
{
|
||||
"point": "verify:post",
|
||||
"ref": { "skill": "secure-phase" },
|
||||
"produces": ["SECURITY.md"],
|
||||
"consumes": ["SUMMARY.md"],
|
||||
"ref": {
|
||||
"skill": "secure-phase"
|
||||
},
|
||||
"produces": [
|
||||
"SECURITY.md"
|
||||
],
|
||||
"consumes": [
|
||||
"SUMMARY.md"
|
||||
],
|
||||
"when": "workflow.security_enforcement",
|
||||
"onError": "halt"
|
||||
}
|
||||
@@ -49,7 +74,9 @@
|
||||
"security_block_on": "workflow.security_block_on"
|
||||
},
|
||||
"produces": [],
|
||||
"consumes": ["CONTEXT.md"],
|
||||
"consumes": [
|
||||
"CONTEXT.md"
|
||||
],
|
||||
"when": "workflow.security_enforcement"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
{
|
||||
"id": "tdd",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Test-driven development",
|
||||
"description": "Injects TDD heuristics into the planner and enforces RED/GREEN gate compliance on type:tdd plans after execution. Owns workflow.tdd_mode; the --tdd CLI flag is the ephemeral override.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
],
|
||||
"unsupported": []
|
||||
},
|
||||
"skills": [],
|
||||
"agents": [],
|
||||
"hooks": [],
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
{
|
||||
"id": "trae",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Trae IDE",
|
||||
"description": "Trae IDE — nested-skill artifact layout; no hook surface (profile-marker-only config); tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
"name": ".trae",
|
||||
"env": ["TRAE_CONFIG_DIR"]
|
||||
"env": [
|
||||
"TRAE_CONFIG_DIR"
|
||||
]
|
||||
},
|
||||
"configFormat": "none",
|
||||
"artifactLayout": {
|
||||
|
||||
@@ -1,23 +1,95 @@
|
||||
{
|
||||
"id": "ui", "role": "feature", "title": "UI design contracts",
|
||||
"id": "ui",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "UI design contracts",
|
||||
"description": "UI-SPEC design contract + retrospective UI audit for frontend phases.",
|
||||
"tier": "full", "requires": [],
|
||||
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
|
||||
"skills": ["ui-phase", "ui-review"],
|
||||
"agents": ["gsd-ui-checker", "gsd-ui-auditor"],
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
],
|
||||
"unsupported": []
|
||||
},
|
||||
"skills": [
|
||||
"ui-phase",
|
||||
"ui-review"
|
||||
],
|
||||
"agents": [
|
||||
"gsd-ui-checker",
|
||||
"gsd-ui-auditor"
|
||||
],
|
||||
"hooks": [],
|
||||
"config": {
|
||||
"workflow.ui_phase": { "type": "boolean", "default": true, "description": "Enable the UI design-contract gate during planning." },
|
||||
"workflow.ui_review": { "type": "boolean", "default": true, "description": "Enable the retrospective UI audit." },
|
||||
"workflow.ui_safety_gate": { "type": "boolean", "default": true, "description": "Block execution on unmet UI-SPEC contracts." }
|
||||
"workflow.ui_phase": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Enable the UI design-contract gate during planning."
|
||||
},
|
||||
"workflow.ui_review": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Enable the retrospective UI audit."
|
||||
},
|
||||
"workflow.ui_safety_gate": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Block execution on unmet UI-SPEC contracts."
|
||||
}
|
||||
},
|
||||
"steps": [
|
||||
{ "point": "plan:pre", "ref": { "skill": "ui-phase" }, "produces": ["UI-SPEC.md"], "consumes": ["CONTEXT.md"], "when": "workflow.ui_phase", "onError": "skip" },
|
||||
{ "point": "verify:post", "ref": { "skill": "ui-review" }, "produces": ["UI-REVIEW.md"], "consumes": ["UI-SPEC.md"], "when": "workflow.ui_review", "onError": "skip" }
|
||||
{
|
||||
"point": "plan:pre",
|
||||
"ref": {
|
||||
"skill": "ui-phase"
|
||||
},
|
||||
"produces": [
|
||||
"UI-SPEC.md"
|
||||
],
|
||||
"consumes": [
|
||||
"CONTEXT.md"
|
||||
],
|
||||
"when": "workflow.ui_phase",
|
||||
"onError": "skip"
|
||||
},
|
||||
{
|
||||
"point": "verify:post",
|
||||
"ref": {
|
||||
"skill": "ui-review"
|
||||
},
|
||||
"produces": [
|
||||
"UI-REVIEW.md"
|
||||
],
|
||||
"consumes": [
|
||||
"UI-SPEC.md"
|
||||
],
|
||||
"when": "workflow.ui_review",
|
||||
"onError": "skip"
|
||||
}
|
||||
],
|
||||
"contributions": [],
|
||||
"gates": [
|
||||
{ "point": "plan:pre", "check": { "query": "ui.plan-gate" }, "when": "workflow.ui_safety_gate", "blocking": true, "onError": "halt" },
|
||||
{ "point": "execute:wave:post", "check": { "query": "ui.safety-gate" }, "when": "workflow.ui_safety_gate", "blocking": true, "onError": "halt" }
|
||||
{
|
||||
"point": "plan:pre",
|
||||
"check": {
|
||||
"query": "ui.plan-gate"
|
||||
},
|
||||
"when": "workflow.ui_safety_gate",
|
||||
"blocking": true,
|
||||
"onError": "halt"
|
||||
},
|
||||
{
|
||||
"point": "execute:wave:post",
|
||||
"check": {
|
||||
"query": "ui.safety-gate"
|
||||
},
|
||||
"when": "workflow.ui_safety_gate",
|
||||
"blocking": true,
|
||||
"onError": "halt"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
{
|
||||
"id": "windsurf",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Windsurf",
|
||||
"description": "Windsurf (Codeium) — nested under ~/.codeium/windsurf; skills-only artifact layout; no hook surface; no hook events; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home-nested",
|
||||
"name": "windsurf",
|
||||
"parent": ".codeium",
|
||||
"env": ["WINDSURF_CONFIG_DIR"]
|
||||
"env": [
|
||||
"WINDSURF_CONFIG_DIR"
|
||||
]
|
||||
},
|
||||
"configFormat": "none",
|
||||
"artifactLayout": {
|
||||
|
||||
@@ -28,6 +28,8 @@ Set the version in your manifest before every release:
|
||||
}
|
||||
```
|
||||
|
||||
> **First-party capabilities are versioned automatically.** The native capabilities shipped inside GSD (`capabilities/<id>/capability.json`) are stamped in lockstep with the GSD package version at release time by `scripts/sync-manifest-versions.cjs` — their `version` always equals the GSD version, so per-capability semver and `compatVersions` only carry independent signal for **third-party** capabilities. As an author of a third-party capability, you own your own version line; the lockstep rule does not apply to you.
|
||||
|
||||
### Decide when to raise `engines.gsd`
|
||||
|
||||
The `engines.gsd` range expresses which GSD host versions your capability is compatible with. GSD enforces this as a hard gate at install time and again at load time.
|
||||
@@ -137,5 +139,5 @@ If the new version of a capability requires a GSD version newer than what you ha
|
||||
|
||||
- [How to remove or disable a capability](remove-a-capability.md)
|
||||
- [Develop a Capability for GSD 1.5+](develop-a-capability.md)
|
||||
- [Capability manifest reference](../reference/capability-matrix.md)
|
||||
- [Capability manifest reference](../reference/capability-manifest.md)
|
||||
- [Turn a capability off (and keep it off)](turn-a-capability-off.md)
|
||||
|
||||
@@ -10,10 +10,14 @@ const capabilities = {
|
||||
"ai-integration": {
|
||||
"id": "ai-integration",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "AI design contract",
|
||||
"description": "AI-SPEC design contract workflow for phases that build AI systems; owns the AI integration command, agents, and workflow.ai_integration_phase activation key.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
@@ -59,10 +63,14 @@ const capabilities = {
|
||||
"antigravity": {
|
||||
"id": "antigravity",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Antigravity",
|
||||
"description": "Google Antigravity IDE — nested under ~/.gemini/antigravity; probed across 1.x and 2.x layouts; Gemini hook event dialect; nested skill layout; tier-1 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home-nested",
|
||||
@@ -114,10 +122,14 @@ const capabilities = {
|
||||
"audit": {
|
||||
"id": "audit",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Audit",
|
||||
"description": "Open-artifact audit and UAT-gap audit for milestone close gates; exposes `gsd-tools audit-uat` (cross-phase UAT outstanding items) and `gsd-tools audit-open` (structured open-artifact scan across debug, tasks, threads, todos, seeds, UAT, verification, context-questions).",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
@@ -147,10 +159,14 @@ const capabilities = {
|
||||
"augment": {
|
||||
"id": "augment",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Augment Code",
|
||||
"description": "Augment Code CLI — commands + nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -212,10 +228,14 @@ const capabilities = {
|
||||
"claude": {
|
||||
"id": "claude",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Claude Code",
|
||||
"description": "Anthropic Claude Code — primary development runtime; tier-1 support with full hook surface and skills-based global install.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -274,10 +294,14 @@ const capabilities = {
|
||||
"cline": {
|
||||
"id": "cline",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Cline",
|
||||
"description": "Cline (VS Code extension) — global-only nested-skill layout; cline-rules hook surface (.clinerules); no hook events emitted; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -313,10 +337,14 @@ const capabilities = {
|
||||
"code-review": {
|
||||
"id": "code-review",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Code review",
|
||||
"description": "Source-file code review and review-fix workflow support for completed execution work.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
@@ -370,10 +398,14 @@ const capabilities = {
|
||||
"codebuddy": {
|
||||
"id": "codebuddy",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "CodeBuddy",
|
||||
"description": "CodeBuddy (Tencent) — converted commands + skills artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -435,10 +467,14 @@ const capabilities = {
|
||||
"codex": {
|
||||
"id": "codex",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "OpenAI Codex CLI",
|
||||
"description": "OpenAI Codex CLI — shell-var command style; per-agent sandbox tiers; config.toml + hooks.json hook surface; tier-1 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -484,10 +520,14 @@ const capabilities = {
|
||||
"copilot": {
|
||||
"id": "copilot",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "GitHub Copilot",
|
||||
"description": "GitHub Copilot (VS Code) — markdown config format; copilot-inline hook surface; no hook events emitted; flat skill nesting (unconfirmed recursive loader); tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -533,10 +573,14 @@ const capabilities = {
|
||||
"cursor": {
|
||||
"id": "cursor",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Cursor",
|
||||
"description": "Cursor IDE — skills + converted commands artifact layout; hooks.json surface; Claude hook event dialect; recursive skill loader (flat nesting); tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -598,10 +642,14 @@ const capabilities = {
|
||||
"drift": {
|
||||
"id": "drift",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Drift detection gates",
|
||||
"description": "Post-execution drift detection gates that run after each wave completes. Provides two gates at execute:wave:post: a blocking schema drift gate (detects schema files changed without a database push) and a non-blocking codebase drift gate (detects structural additions not reflected in STRUCTURE.md).",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
@@ -658,10 +706,14 @@ const capabilities = {
|
||||
"gap-analysis": {
|
||||
"id": "gap-analysis",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Post-planning gap analysis",
|
||||
"description": "Proactive, non-blocking post-planning coverage report. After all PLAN.md files are generated, cross-references every REQ-ID and D-ID from REQUIREMENTS.md and CONTEXT.md against plan bodies. Emits a Source | Item | Status table. Does not block phase advancement.",
|
||||
"tier": "standard",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
@@ -695,10 +747,14 @@ const capabilities = {
|
||||
"gemini": {
|
||||
"id": "gemini",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Gemini CLI",
|
||||
"description": "Google Gemini CLI — commands-only artifact layout (TOML); Gemini hook event dialect; settings-json hook surface; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -748,10 +804,14 @@ const capabilities = {
|
||||
"graphify": {
|
||||
"id": "graphify",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Knowledge graph",
|
||||
"description": "Build, query, and inspect the project knowledge graph in `.planning/graphs/`; exposes graphify CLI subcommands (build, query, status, diff) and the /gsd-graphify skill.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
@@ -785,10 +845,14 @@ const capabilities = {
|
||||
"hermes": {
|
||||
"id": "hermes",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Hermes Agent",
|
||||
"description": "Hermes Agent (NousResearch) — skills nest under skills/gsd/ category bucket; nested skill layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -834,10 +898,14 @@ const capabilities = {
|
||||
"intel": {
|
||||
"id": "intel",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Codebase intelligence",
|
||||
"description": "Code-intelligence store for codebase querying, diff, snapshot, and API-surface extraction; exposes `gsd-tools intel` subcommands (query, status, update, diff, snapshot, patch-meta, validate, extract-exports, api-surface) and backs `/gsd-map-codebase` and `gsd-intel-updater`.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
@@ -882,10 +950,14 @@ const capabilities = {
|
||||
"kilo": {
|
||||
"id": "kilo",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Kilo Code",
|
||||
"description": "Kilo Code — XDG-based config dir; global skills at ~/.kilo/skills (separate from XDG config); flat command/ + skills artifact layout; no lifecycle hook registration; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "xdg",
|
||||
@@ -953,10 +1025,14 @@ const capabilities = {
|
||||
"kimi": {
|
||||
"id": "kimi",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Kimi CLI",
|
||||
"description": "Kimi CLI (Moonshot AI) — generic agents root at ~/.config/agents; skills + kimi-agents artifact layout; no hook surface; no hook events; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "generic-agents-root",
|
||||
@@ -1005,10 +1081,14 @@ const capabilities = {
|
||||
"mempalace": {
|
||||
"id": "mempalace",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "MemPalace memory",
|
||||
"description": "Cross-session, cross-project memory: deliberate recall before discuss/plan and verbatim capture + temporal-KG sync at phase boundaries, via the MemPalace MCP server and CLI.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
@@ -1175,10 +1255,14 @@ const capabilities = {
|
||||
"nyquist": {
|
||||
"id": "nyquist",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Nyquist validation",
|
||||
"description": "Validation coverage audit that maps executed work back to tests and manual-only evidence.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
@@ -1221,10 +1305,14 @@ const capabilities = {
|
||||
"opencode": {
|
||||
"id": "opencode",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "OpenCode",
|
||||
"description": "OpenCode — XDG-based config dir; flat command/ + skills artifact layout; settings-json config format; no lifecycle hook registration; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "xdg",
|
||||
@@ -1287,12 +1375,16 @@ const capabilities = {
|
||||
"pattern-mapper": {
|
||||
"id": "pattern-mapper",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Pattern mapping",
|
||||
"description": "Optional codebase-pattern mapping before planning; owns the pattern mapper agent and workflow.pattern_mapper activation key.",
|
||||
"tier": "full",
|
||||
"requires": [
|
||||
"research"
|
||||
],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
@@ -1337,10 +1429,14 @@ const capabilities = {
|
||||
"profile-pipeline": {
|
||||
"id": "profile-pipeline",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Developer profiling pipeline",
|
||||
"description": "Developer behavioral profiling from Claude Code session history; scans session JSONL files, extracts and samples user messages, and generates profile artifacts (USER-PROFILE.md, dev-preferences.md, CLAUDE.md sections). Exposes eight `gsd-tools` commands: scan-sessions, extract-messages, profile-sample (pipeline phase) and write-profile, profile-questionnaire, generate-dev-preferences, generate-claude-profile, generate-claude-md (output phase). Backs the /gsd-profile-user skill and gsd-user-profiler agent.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
@@ -1410,10 +1506,14 @@ const capabilities = {
|
||||
"qwen": {
|
||||
"id": "qwen",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Qwen Code",
|
||||
"description": "Qwen Code (Alibaba) — nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -1463,10 +1563,14 @@ const capabilities = {
|
||||
"research": {
|
||||
"id": "research",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Phase research",
|
||||
"description": "Optional phase research before planning; owns the phase researcher agent and workflow.research activation key.",
|
||||
"tier": "standard",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
@@ -1511,10 +1615,14 @@ const capabilities = {
|
||||
"schema-gate": {
|
||||
"id": "schema-gate",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Schema push detection gate",
|
||||
"description": "Detects ORM schema-relevant files in the phase scope during planning and injects a mandatory [BLOCKING] schema push task into the plan. Prevents false-positive verification where build/types pass because TypeScript types come from config, not the live database.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
@@ -1553,10 +1661,14 @@ const capabilities = {
|
||||
"security": {
|
||||
"id": "security",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Security enforcement",
|
||||
"description": "Threat mitigation verification and ship-time security blocking for phases with security enforcement enabled.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
@@ -1648,10 +1760,14 @@ const capabilities = {
|
||||
"tdd": {
|
||||
"id": "tdd",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Test-driven development",
|
||||
"description": "Injects TDD heuristics into the planner and enforces RED/GREEN gate compliance on type:tdd plans after execution. Owns workflow.tdd_mode; the --tdd CLI flag is the ephemeral override.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
@@ -1697,10 +1813,14 @@ const capabilities = {
|
||||
"trae": {
|
||||
"id": "trae",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Trae IDE",
|
||||
"description": "Trae IDE — nested-skill artifact layout; no hook surface (profile-marker-only config); tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -1745,10 +1865,14 @@ const capabilities = {
|
||||
"ui": {
|
||||
"id": "ui",
|
||||
"role": "feature",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "UI design contracts",
|
||||
"description": "UI-SPEC design contract + retrospective UI audit for frontend phases.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtimeCompat": {
|
||||
"supported": [
|
||||
"*"
|
||||
@@ -1836,10 +1960,14 @@ const capabilities = {
|
||||
"windsurf": {
|
||||
"id": "windsurf",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Windsurf",
|
||||
"description": "Windsurf (Codeium) — nested under ~/.codeium/windsurf; skills-only artifact layout; no hook surface; no hook events; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home-nested",
|
||||
@@ -2592,10 +2720,14 @@ const runtimes = {
|
||||
"antigravity": {
|
||||
"id": "antigravity",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Antigravity",
|
||||
"description": "Google Antigravity IDE — nested under ~/.gemini/antigravity; probed across 1.x and 2.x layouts; Gemini hook event dialect; nested skill layout; tier-1 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home-nested",
|
||||
@@ -2647,10 +2779,14 @@ const runtimes = {
|
||||
"augment": {
|
||||
"id": "augment",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Augment Code",
|
||||
"description": "Augment Code CLI — commands + nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -2712,10 +2848,14 @@ const runtimes = {
|
||||
"claude": {
|
||||
"id": "claude",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Claude Code",
|
||||
"description": "Anthropic Claude Code — primary development runtime; tier-1 support with full hook surface and skills-based global install.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -2774,10 +2914,14 @@ const runtimes = {
|
||||
"cline": {
|
||||
"id": "cline",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Cline",
|
||||
"description": "Cline (VS Code extension) — global-only nested-skill layout; cline-rules hook surface (.clinerules); no hook events emitted; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -2813,10 +2957,14 @@ const runtimes = {
|
||||
"codebuddy": {
|
||||
"id": "codebuddy",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "CodeBuddy",
|
||||
"description": "CodeBuddy (Tencent) — converted commands + skills artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -2878,10 +3026,14 @@ const runtimes = {
|
||||
"codex": {
|
||||
"id": "codex",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "OpenAI Codex CLI",
|
||||
"description": "OpenAI Codex CLI — shell-var command style; per-agent sandbox tiers; config.toml + hooks.json hook surface; tier-1 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -2927,10 +3079,14 @@ const runtimes = {
|
||||
"copilot": {
|
||||
"id": "copilot",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "GitHub Copilot",
|
||||
"description": "GitHub Copilot (VS Code) — markdown config format; copilot-inline hook surface; no hook events emitted; flat skill nesting (unconfirmed recursive loader); tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -2976,10 +3132,14 @@ const runtimes = {
|
||||
"cursor": {
|
||||
"id": "cursor",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Cursor",
|
||||
"description": "Cursor IDE — skills + converted commands artifact layout; hooks.json surface; Claude hook event dialect; recursive skill loader (flat nesting); tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -3041,10 +3201,14 @@ const runtimes = {
|
||||
"gemini": {
|
||||
"id": "gemini",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Gemini CLI",
|
||||
"description": "Google Gemini CLI — commands-only artifact layout (TOML); Gemini hook event dialect; settings-json hook surface; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -3094,10 +3258,14 @@ const runtimes = {
|
||||
"hermes": {
|
||||
"id": "hermes",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Hermes Agent",
|
||||
"description": "Hermes Agent (NousResearch) — skills nest under skills/gsd/ category bucket; nested skill layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -3143,10 +3311,14 @@ const runtimes = {
|
||||
"kilo": {
|
||||
"id": "kilo",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Kilo Code",
|
||||
"description": "Kilo Code — XDG-based config dir; global skills at ~/.kilo/skills (separate from XDG config); flat command/ + skills artifact layout; no lifecycle hook registration; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "xdg",
|
||||
@@ -3214,10 +3386,14 @@ const runtimes = {
|
||||
"kimi": {
|
||||
"id": "kimi",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Kimi CLI",
|
||||
"description": "Kimi CLI (Moonshot AI) — generic agents root at ~/.config/agents; skills + kimi-agents artifact layout; no hook surface; no hook events; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "generic-agents-root",
|
||||
@@ -3266,10 +3442,14 @@ const runtimes = {
|
||||
"opencode": {
|
||||
"id": "opencode",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "OpenCode",
|
||||
"description": "OpenCode — XDG-based config dir; flat command/ + skills artifact layout; settings-json config format; no lifecycle hook registration; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "xdg",
|
||||
@@ -3332,10 +3512,14 @@ const runtimes = {
|
||||
"qwen": {
|
||||
"id": "qwen",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Qwen Code",
|
||||
"description": "Qwen Code (Alibaba) — nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -3385,10 +3569,14 @@ const runtimes = {
|
||||
"trae": {
|
||||
"id": "trae",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Trae IDE",
|
||||
"description": "Trae IDE — nested-skill artifact layout; no hook surface (profile-marker-only config); tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home",
|
||||
@@ -3433,10 +3621,14 @@ const runtimes = {
|
||||
"windsurf": {
|
||||
"id": "windsurf",
|
||||
"role": "runtime",
|
||||
"version": "1.5.1-dev.0",
|
||||
"title": "Windsurf",
|
||||
"description": "Windsurf (Codeium) — nested under ~/.codeium/windsurf; skills-only artifact layout; no hook surface; no hook events; tier-2 support.",
|
||||
"tier": "core",
|
||||
"requires": [],
|
||||
"engines": {
|
||||
"gsd": ">=1.6.0"
|
||||
},
|
||||
"runtime": {
|
||||
"configHome": {
|
||||
"kind": "dot-home-nested",
|
||||
|
||||
@@ -236,6 +236,96 @@ const VALID_TIERS = new Set(['core', 'standard', 'full']);
|
||||
const VALID_ON_ERROR = new Set(['skip', 'halt']);
|
||||
const RUNTIME_COMPAT_WILDCARD = '*';
|
||||
|
||||
// ── ADR-1244 D1: versioned-manifest envelope ─────────────────────────────────
|
||||
// Official strict SemVer 2.0.0 grammar (https://semver.org). Rejects partials
|
||||
// ("1.0"), prefixes ("v1.0.0"), leading-zero segments ("01.2.3"), numeric
|
||||
// prerelease identifiers with leading zeros ("1.2.3-01"), empty identifiers
|
||||
// ("1.2.3-..") and — critically — prerelease/build identifiers containing
|
||||
// anything outside [0-9A-Za-z-] (so a version can never smuggle shell
|
||||
// metacharacters, spaces or unicode into a downstream `git tag v<version>` or
|
||||
// path). Accepts "1.2.3-dev.0", "1.2.3-rc.1", "1.2.3+build.5".
|
||||
const SEMVER_RE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
|
||||
// Permissive *shape* check for a semver range (engines.gsd / compatVersions
|
||||
// values). Range SATISFACTION is enforced by the runtime overlay (ADR-1244 D2);
|
||||
// here we only reject empty/garbage and shell metacharacters.
|
||||
const SEMVER_RANGE_RE = /^[0-9A-Za-z.\-+ |<>=~^*()]+$/;
|
||||
// Subresource-integrity hash: "sha512-" + base64 of a 64-byte digest (86 base64
|
||||
// chars + "==" padding). Exact length so malformed pins ("sha512-abc") fail.
|
||||
const SHA512_INTEGRITY_RE = /^sha512-[A-Za-z0-9+/]{86}==$/;
|
||||
|
||||
// A syntactically plausible semver range (shape-only — see SEMVER_RANGE_RE).
|
||||
// Requires a digit or a bare wildcard so pure-alpha garbage ("abcx", "()x") is
|
||||
// rejected; full range satisfaction is the runtime overlay's job (ADR-1244 D2).
|
||||
function isPlausibleRange(s) {
|
||||
if (typeof s !== 'string') return false;
|
||||
const t = s.trim();
|
||||
if (t.length === 0 || !SEMVER_RANGE_RE.test(s)) return false;
|
||||
return /\d/.test(t) || t === '*' || t === 'x' || t === 'X';
|
||||
}
|
||||
|
||||
/**
|
||||
* ADR-1244 D1: validate the versioned-manifest envelope.
|
||||
* - version REQUIRED semver string (the registry rejects a manifest
|
||||
* without one).
|
||||
* - engines optional object; engines.gsd optional semver-range string.
|
||||
* - compatVersions optional object mapping a capability version (semver) to a
|
||||
* gsd version range.
|
||||
* - integrity optional "sha512-<base64>" string.
|
||||
* - provenance optional { sourceRepo, commit } strings.
|
||||
*
|
||||
* Shape only — range satisfaction and integrity verification are enforced by
|
||||
* the source resolver / runtime overlay (ADR-1244 D2/D3).
|
||||
*
|
||||
* @param {object} cap The parsed JSON object.
|
||||
* @returns {string[]} Array of error strings; empty = valid.
|
||||
*/
|
||||
function validateVersionEnvelope(cap) {
|
||||
const errors = [];
|
||||
|
||||
if (typeof cap.version !== 'string' || !SEMVER_RE.test(cap.version)) {
|
||||
errors.push('version must be a semver string (e.g. "1.2.3"); got: ' + JSON.stringify(cap.version));
|
||||
}
|
||||
|
||||
if (cap.engines !== undefined) {
|
||||
if (typeof cap.engines !== 'object' || cap.engines === null || Array.isArray(cap.engines)) {
|
||||
errors.push('engines must be an object (e.g. { "gsd": ">=1.6.0" })');
|
||||
} else if (cap.engines.gsd !== undefined && !isPlausibleRange(cap.engines.gsd)) {
|
||||
errors.push('engines.gsd must be a semver range string; got: ' + JSON.stringify(cap.engines.gsd));
|
||||
}
|
||||
}
|
||||
|
||||
if (cap.compatVersions !== undefined) {
|
||||
if (typeof cap.compatVersions !== 'object' || cap.compatVersions === null || Array.isArray(cap.compatVersions)) {
|
||||
errors.push('compatVersions must be an object mapping capability versions to gsd version ranges');
|
||||
} else {
|
||||
for (const [k, v] of Object.entries(cap.compatVersions)) {
|
||||
if (!SEMVER_RE.test(k)) errors.push('compatVersions key "' + k + '" must be a semver string');
|
||||
if (!isPlausibleRange(v)) errors.push('compatVersions["' + k + '"] must be a semver range string');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cap.integrity !== undefined && (typeof cap.integrity !== 'string' || !SHA512_INTEGRITY_RE.test(cap.integrity))) {
|
||||
errors.push('integrity must be a "sha512-<base64>" string');
|
||||
}
|
||||
|
||||
if (cap.provenance !== undefined) {
|
||||
const p = cap.provenance;
|
||||
if (typeof p !== 'object' || p === null || Array.isArray(p)) {
|
||||
errors.push('provenance must be an object { sourceRepo, commit }');
|
||||
} else {
|
||||
if (typeof p.sourceRepo !== 'string' || p.sourceRepo.length === 0) {
|
||||
errors.push('provenance.sourceRepo must be a non-empty string');
|
||||
}
|
||||
if (typeof p.commit !== 'string' || p.commit.length === 0) {
|
||||
errors.push('provenance.commit must be a non-empty string');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a single capability declaration.
|
||||
*
|
||||
@@ -285,6 +375,9 @@ function validateCapability(cap, folderId) {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Versioned-manifest envelope (ADR-1244 D1) ──────────────────────────────
|
||||
errors.push(...validateVersionEnvelope(cap));
|
||||
|
||||
// ── Role-specific body ────────────────────────────────────────────────────
|
||||
|
||||
if (cap.role === 'feature') {
|
||||
@@ -2580,6 +2673,11 @@ function main() {
|
||||
|
||||
module.exports = {
|
||||
validateCapability,
|
||||
// ADR-1244 D1: versioned-manifest envelope validation (reused by the runtime overlay, D2)
|
||||
validateVersionEnvelope,
|
||||
SEMVER_RE,
|
||||
SEMVER_RANGE_RE,
|
||||
SHA512_INTEGRITY_RE,
|
||||
validateAgainstContract,
|
||||
validateConsumesGlobal,
|
||||
validateCrossCapability,
|
||||
|
||||
@@ -68,6 +68,66 @@ function findDrift(opts) {
|
||||
return drift;
|
||||
}
|
||||
|
||||
// ─── ADR-1244 D6: native capability manifests ────────────────────────────────
|
||||
//
|
||||
// Native capabilities (capabilities/<id>/capability.json) carry a `version`
|
||||
// stamped in lockstep with the package version at release. Unlike
|
||||
// VERSIONED_MANIFESTS (fixed paths), capabilities are discovered by glob so a
|
||||
// new capability is auto-covered without editing this file. The version-sync
|
||||
// regression guard (issue #844) treats every swept capability manifest as
|
||||
// registered.
|
||||
|
||||
// Discover capabilities/<id>/capability.json under `root`, sorted for stable
|
||||
// staging order. Returns [] when there is no capabilities/ directory.
|
||||
function listCapabilityManifests(opts) {
|
||||
const root = (opts && opts.root) || ROOT;
|
||||
const dir = path.join(root, 'capabilities');
|
||||
let entries;
|
||||
try {
|
||||
entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
return entries
|
||||
.filter((e) => e.isDirectory())
|
||||
// Forward-slash rel paths (NOT path.join) so they match `git ls-files`
|
||||
// output, git pathspecs, and the forward-slash VERSIONED_MANIFESTS on every
|
||||
// platform — path.join would emit backslashes on Windows and break the
|
||||
// issue-844 regression guard's ALLOWED-set comparison.
|
||||
.map((e) => 'capabilities/' + e.name + '/capability.json')
|
||||
.filter((rel) => fs.existsSync(path.join(root, rel)))
|
||||
.sort();
|
||||
}
|
||||
|
||||
// Stamp `version` into each native capability manifest. Returns changed rel paths.
|
||||
function syncCapabilityVersions(opts) {
|
||||
const root = (opts && opts.root) || ROOT;
|
||||
const v = (opts && opts.version) != null ? opts.version : getPackageVersion(root);
|
||||
const changed = [];
|
||||
for (const rel of listCapabilityManifests({ root })) {
|
||||
const abs = path.join(root, rel);
|
||||
const manifest = readJson(abs);
|
||||
if (manifest.version !== v) {
|
||||
manifest.version = v;
|
||||
fs.writeFileSync(abs, JSON.stringify(manifest, null, 2) + '\n');
|
||||
changed.push(rel);
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
// Native capability manifests whose version != package version.
|
||||
function findCapabilityDrift(opts) {
|
||||
const root = (opts && opts.root) || ROOT;
|
||||
const v = (opts && opts.version) != null ? opts.version : getPackageVersion(root);
|
||||
const drift = [];
|
||||
for (const rel of listCapabilityManifests({ root })) {
|
||||
const found = readJson(path.join(root, rel)).version;
|
||||
if (found !== v) drift.push({ manifest: rel, found, expected: v });
|
||||
}
|
||||
return drift;
|
||||
}
|
||||
|
||||
// Best-effort outside git; fail-closed inside a work tree so a release never
|
||||
// ships a stale manifest that the working-tree test already accepted.
|
||||
function stageManifests(opts) {
|
||||
@@ -83,21 +143,32 @@ function stageManifests(opts) {
|
||||
console.warn('sync-manifest-versions: not a git work tree; skipping staging.');
|
||||
return;
|
||||
}
|
||||
const toStage = [...VERSIONED_MANIFESTS, ...listCapabilityManifests({ root })];
|
||||
try {
|
||||
execFileSync('git', ['add', '--', ...VERSIONED_MANIFESTS], { cwd: root, stdio: ['ignore', 'ignore', 'pipe'] });
|
||||
execFileSync('git', ['add', '--', ...toStage], { cwd: root, stdio: ['ignore', 'ignore', 'pipe'] });
|
||||
} catch (err) {
|
||||
const detail = err && err.stderr ? err.stderr.toString().trim() : (err && err.message) || 'unknown error';
|
||||
throw new Error(`sync-manifest-versions: failed to git-add manifests inside a work tree: ${detail}`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { VERSIONED_MANIFESTS, syncManifestVersions, findDrift, getPackageVersion, stageManifests };
|
||||
module.exports = {
|
||||
VERSIONED_MANIFESTS,
|
||||
syncManifestVersions,
|
||||
findDrift,
|
||||
getPackageVersion,
|
||||
stageManifests,
|
||||
// ADR-1244 D6: native capability version sweep
|
||||
listCapabilityManifests,
|
||||
syncCapabilityVersions,
|
||||
findCapabilityDrift,
|
||||
};
|
||||
|
||||
if (require.main === module) {
|
||||
const args = process.argv.slice(2);
|
||||
const version = getPackageVersion();
|
||||
if (args.includes('--check')) {
|
||||
const drift = findDrift({ version });
|
||||
const drift = [...findDrift({ version }), ...findCapabilityDrift({ version })];
|
||||
if (drift.length) {
|
||||
for (const d of drift) {
|
||||
console.error('Manifest ' + d.manifest + ' version ' + d.found + ' != package.json ' + d.expected);
|
||||
@@ -105,10 +176,11 @@ if (require.main === module) {
|
||||
console.error('Run `node scripts/sync-manifest-versions.cjs` to fix.');
|
||||
process.exitCode = 1;
|
||||
} else {
|
||||
console.log('All ' + VERSIONED_MANIFESTS.length + ' versioned manifests in sync at ' + version + '.');
|
||||
const total = VERSIONED_MANIFESTS.length + listCapabilityManifests().length;
|
||||
console.log('All ' + total + ' versioned manifests in sync at ' + version + '.');
|
||||
}
|
||||
} else {
|
||||
const changed = syncManifestVersions({ version });
|
||||
const changed = [...syncManifestVersions({ version }), ...syncCapabilityVersions({ version })];
|
||||
if (changed.length) {
|
||||
console.log('Stamped ' + version + ' into: ' + changed.join(', '));
|
||||
} else {
|
||||
|
||||
295
tests/capability-manifest-version.test.cjs
Normal file
295
tests/capability-manifest-version.test.cjs
Normal file
@@ -0,0 +1,295 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Phase 1 (ADR-1244 / issue #1430): versioned capability manifest.
|
||||
*
|
||||
* The build-time validator in scripts/gen-capability-registry.cjs must:
|
||||
* - REQUIRE a semver `version` on every capability (the registry rejects a
|
||||
* manifest without one — ADR-1244 D1).
|
||||
* - Shape-validate the optional ecosystem envelope fields `engines`,
|
||||
* `compatVersions`, `integrity`, `provenance` when present.
|
||||
*
|
||||
* Every native capabilities/<id>/capability.json must carry a valid `version`
|
||||
* and `engines.gsd` (the conformance / parity requirement: the build fails when
|
||||
* a native manifest lacks a version).
|
||||
*
|
||||
* These are behavioral tests against the exported validator + generator
|
||||
* pipeline — no source-grep. They mirror the harness in
|
||||
* tests/capability-registry.test.cjs (makeTempCapDir + loadAndValidate +
|
||||
* buildRegistry).
|
||||
*/
|
||||
|
||||
const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '..');
|
||||
const helpers = require(path.join(__dirname, 'helpers.cjs'));
|
||||
const {
|
||||
validateCapability,
|
||||
loadAndValidate,
|
||||
buildRegistry,
|
||||
SEMVER_RE,
|
||||
} = require(path.join(ROOT, 'scripts', 'gen-capability-registry.cjs'));
|
||||
|
||||
const PKG_VERSION = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8')).version;
|
||||
const CAPABILITIES_DIR = path.join(ROOT, 'capabilities');
|
||||
|
||||
// Single source of truth: the validator's own strict-semver regex.
|
||||
const SEMVER = SEMVER_RE;
|
||||
|
||||
// ─── Minimal, otherwise-valid fixtures ───────────────────────────────────────
|
||||
|
||||
function featureCap(overrides) {
|
||||
return {
|
||||
id: 'demo',
|
||||
role: 'feature',
|
||||
version: '1.2.3',
|
||||
title: 'Demo',
|
||||
description: 'A demo capability.',
|
||||
tier: 'standard',
|
||||
requires: [],
|
||||
engines: { gsd: '>=1.6.0' },
|
||||
runtimeCompat: { supported: ['*'], unsupported: [] },
|
||||
skills: [],
|
||||
agents: [],
|
||||
hooks: [],
|
||||
config: {},
|
||||
steps: [],
|
||||
contributions: [],
|
||||
gates: [],
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function runtimeCap(overrides) {
|
||||
return {
|
||||
id: 'demo-rt',
|
||||
role: 'runtime',
|
||||
version: '1.2.3',
|
||||
title: 'Demo RT',
|
||||
description: 'A demo runtime.',
|
||||
tier: 'standard',
|
||||
requires: [],
|
||||
engines: { gsd: '>=1.6.0' },
|
||||
runtime: {
|
||||
configHome: { kind: 'dot-home', name: '.demo', env: [] },
|
||||
configFormat: 'settings-json',
|
||||
artifactLayout: { global: [], local: [] },
|
||||
commandStyle: 'slash-hyphen',
|
||||
hooksSurface: 'settings-json',
|
||||
sandboxTier: 'none',
|
||||
supportTier: 2,
|
||||
installSurface: 'settings-json',
|
||||
writesSharedSettings: false,
|
||||
permissionWriter: null,
|
||||
extendedHookEvents: [],
|
||||
},
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function makeTempCapDir(capabilities) {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cap-ver-test-'));
|
||||
for (const [id, cap] of Object.entries(capabilities)) {
|
||||
const subDir = path.join(tmpDir, id);
|
||||
fs.mkdirSync(subDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(subDir, 'capability.json'), JSON.stringify(cap), 'utf8');
|
||||
}
|
||||
return tmpDir;
|
||||
}
|
||||
|
||||
// ─── Sanity: the base fixtures are valid as-is ───────────────────────────────
|
||||
|
||||
describe('version envelope — base fixtures are valid', () => {
|
||||
test('a feature cap with a valid version passes validation', () => {
|
||||
assert.deepEqual(validateCapability(featureCap(), 'demo'), []);
|
||||
});
|
||||
test('a runtime cap with a valid version passes validation', () => {
|
||||
assert.deepEqual(validateCapability(runtimeCap(), 'demo-rt'), []);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── version is required + semver ────────────────────────────────────────────
|
||||
|
||||
describe('version is required and must be semver', () => {
|
||||
test('missing version is rejected (feature)', () => {
|
||||
const { version: _v, ...cap } = featureCap();
|
||||
const errors = validateCapability(cap, 'demo');
|
||||
assert.ok(errors.some((e) => e.includes('version')), `expected a version error, got: ${JSON.stringify(errors)}`);
|
||||
});
|
||||
|
||||
test('missing version is rejected (runtime)', () => {
|
||||
const { version: _v, ...cap } = runtimeCap();
|
||||
const errors = validateCapability(cap, 'demo-rt');
|
||||
assert.ok(errors.some((e) => e.includes('version')), `expected a version error, got: ${JSON.stringify(errors)}`);
|
||||
});
|
||||
|
||||
test('empty-string version is rejected', () => {
|
||||
const errors = validateCapability(featureCap({ version: '' }), 'demo');
|
||||
assert.ok(errors.some((e) => e.includes('version')));
|
||||
});
|
||||
|
||||
test('whitespace-only version is rejected', () => {
|
||||
const errors = validateCapability(featureCap({ version: ' ' }), 'demo');
|
||||
assert.ok(errors.some((e) => e.includes('version')));
|
||||
});
|
||||
|
||||
test('non-semver versions are rejected', () => {
|
||||
for (const bad of ['1.0', 'v1.0.0', '1.0.0.0', '1', 'latest', '1.x', '1.0.0 ', '01.2.3']) {
|
||||
const errors = validateCapability(featureCap({ version: bad }), 'demo');
|
||||
assert.ok(errors.some((e) => e.includes('version')), `expected "${bad}" to be rejected`);
|
||||
}
|
||||
});
|
||||
|
||||
test('malformed/hostile prerelease & build identifiers are rejected (strict semver)', () => {
|
||||
// The prerelease/build suffix must be dot-separated [0-9A-Za-z-] identifiers
|
||||
// with no leading-zero numerics and no empty segments — so a version can
|
||||
// never smuggle shell metacharacters, spaces or unicode downstream.
|
||||
for (const bad of ['1.2.3-01', '1.2.3-..', '1.2.3-', '1.2.3+', '1.2.3-foo bar', '1.2.3-$(whoami)', '1.2.3-`id`', '1.2.3-😈', '1.2.3+build meta']) {
|
||||
const errors = validateCapability(featureCap({ version: bad }), 'demo');
|
||||
assert.ok(errors.some((e) => e.includes('version')), `expected hostile suffix "${bad}" to be rejected`);
|
||||
}
|
||||
});
|
||||
|
||||
test('non-string version is rejected', () => {
|
||||
for (const bad of [123, null, {}, ['1.0.0']]) {
|
||||
const errors = validateCapability(featureCap({ version: bad }), 'demo');
|
||||
assert.ok(errors.some((e) => e.includes('version')), `expected ${JSON.stringify(bad)} to be rejected`);
|
||||
}
|
||||
});
|
||||
|
||||
test('valid semver versions (incl. prerelease/build) pass', () => {
|
||||
for (const ok of ['1.0.0', '0.0.1', '10.20.30', '1.2.3-dev.0', '1.2.3-rc.1', '1.2.3+build.5', PKG_VERSION]) {
|
||||
const errors = validateCapability(featureCap({ version: ok }), 'demo');
|
||||
assert.deepEqual(errors, [], `expected "${ok}" to pass, got: ${JSON.stringify(errors)}`);
|
||||
}
|
||||
});
|
||||
|
||||
test('hostile version strings are rejected (shell metachars, newline, unicode)', () => {
|
||||
for (const bad of ['1.0.0; rm -rf /', '1.0.0\n2.0.0', '1.0.0$(whoami)', '१.२.३', '1.0.0`id`']) {
|
||||
const errors = validateCapability(featureCap({ version: bad }), 'demo');
|
||||
assert.ok(errors.some((e) => e.includes('version')), `expected hostile "${bad}" to be rejected`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ─── engines (optional; shape-validated) ─────────────────────────────────────
|
||||
|
||||
describe('engines is optional but shape-validated when present', () => {
|
||||
test('omitting engines is valid', () => {
|
||||
const { engines: _e, ...cap } = featureCap();
|
||||
assert.deepEqual(validateCapability(cap, 'demo'), []);
|
||||
});
|
||||
|
||||
test('engines must be an object', () => {
|
||||
for (const bad of ['>=1.6.0', 123, ['gsd'], null]) {
|
||||
const errors = validateCapability(featureCap({ engines: bad }), 'demo');
|
||||
assert.ok(errors.some((e) => e.includes('engines')), `expected engines=${JSON.stringify(bad)} rejected`);
|
||||
}
|
||||
});
|
||||
|
||||
test('engines.gsd must be a non-empty range string', () => {
|
||||
for (const bad of ['', ' ', 123, {}, 'not a range!!', '>=1.0.0; rm -rf', 'abcx', '()x']) {
|
||||
const errors = validateCapability(featureCap({ engines: { gsd: bad } }), 'demo');
|
||||
assert.ok(errors.some((e) => e.includes('engines')), `expected engines.gsd=${JSON.stringify(bad)} rejected`);
|
||||
}
|
||||
});
|
||||
|
||||
test('valid engines.gsd ranges pass', () => {
|
||||
for (const ok of ['>=1.6.0', '>=1.6.0 <3.0.0', '^1.0.0', '~1.2.0', '1.x', '*', '>=1.6.0 || >=2.0.0']) {
|
||||
const errors = validateCapability(featureCap({ engines: { gsd: ok } }), 'demo');
|
||||
assert.deepEqual(errors, [], `expected range "${ok}" to pass, got: ${JSON.stringify(errors)}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ─── compatVersions / integrity / provenance (optional; shape-validated) ──────
|
||||
|
||||
describe('optional ecosystem envelope fields are shape-validated', () => {
|
||||
test('compatVersions must be an object of semver→range strings', () => {
|
||||
assert.deepEqual(validateCapability(featureCap({ compatVersions: { '1.0.0': '>=1.6.0' } }), 'demo'), []);
|
||||
for (const bad of ['x', 123, { '1.0.0': 5 }, { 'not-semver': '>=1.6.0' }]) {
|
||||
const errors = validateCapability(featureCap({ compatVersions: bad }), 'demo');
|
||||
assert.ok(errors.some((e) => e.includes('compatVersions')), `expected compatVersions=${JSON.stringify(bad)} rejected`);
|
||||
}
|
||||
});
|
||||
|
||||
test('integrity must be sha512-<base64>', () => {
|
||||
const good = 'sha512-' + 'a'.repeat(86) + '==';
|
||||
assert.deepEqual(validateCapability(featureCap({ integrity: good }), 'demo'), []);
|
||||
for (const bad of ['abc', 'sha256-deadbeef', 'sha512-', 'sha512-abc', 'sha512-' + 'a'.repeat(40) + '==', 123, 'sha512-not base64!!']) {
|
||||
const errors = validateCapability(featureCap({ integrity: bad }), 'demo');
|
||||
assert.ok(errors.some((e) => e.includes('integrity')), `expected integrity=${JSON.stringify(bad)} rejected`);
|
||||
}
|
||||
});
|
||||
|
||||
test('provenance must be { sourceRepo, commit } strings', () => {
|
||||
assert.deepEqual(validateCapability(featureCap({ provenance: { sourceRepo: 'https://x/y', commit: 'abc123' } }), 'demo'), []);
|
||||
for (const bad of ['x', 123, { sourceRepo: 5, commit: 'c' }, { sourceRepo: 'r' }, { commit: 'c' }]) {
|
||||
const errors = validateCapability(featureCap({ provenance: bad }), 'demo');
|
||||
assert.ok(errors.some((e) => e.includes('provenance')), `expected provenance=${JSON.stringify(bad)} rejected`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Registry pass-through ────────────────────────────────────────────────────
|
||||
|
||||
describe('generated registry preserves version + engines', () => {
|
||||
test('buildRegistry carries version and engines onto the capability object', (t) => {
|
||||
const capDir = makeTempCapDir({ demo: featureCap({ id: 'demo', version: '2.5.0', engines: { gsd: '>=1.6.0 <2.0.0' } }) });
|
||||
t.after(() => helpers.cleanup(capDir));
|
||||
|
||||
const { capMap, errors } = loadAndValidate(new Set(), capDir);
|
||||
assert.deepEqual(errors, [], `loadAndValidate errors: ${JSON.stringify(errors)}`);
|
||||
const registry = buildRegistry(capMap);
|
||||
assert.equal(registry.capabilities.demo.version, '2.5.0');
|
||||
assert.equal(registry.capabilities.demo.engines.gsd, '>=1.6.0 <2.0.0');
|
||||
});
|
||||
|
||||
test('loadAndValidate rejects a capability dir whose manifest lacks a version', (t) => {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cap-ver-noversion-'));
|
||||
t.after(() => helpers.cleanup(tmpDir));
|
||||
const sub = path.join(tmpDir, 'demo');
|
||||
fs.mkdirSync(sub, { recursive: true });
|
||||
const { version: _v, ...noVersion } = featureCap({ id: 'demo' });
|
||||
fs.writeFileSync(path.join(sub, 'capability.json'), JSON.stringify(noVersion), 'utf8');
|
||||
|
||||
const { errors } = loadAndValidate(new Set(), tmpDir);
|
||||
assert.ok(errors.some((e) => e.includes('version')), `expected a version error, got: ${JSON.stringify(errors)}`);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Native manifest conformance (the ADR-1244 parity requirement) ────────────
|
||||
|
||||
describe('every native capability.json carries a valid version + engines.gsd', () => {
|
||||
const ids = fs
|
||||
.readdirSync(CAPABILITIES_DIR, { withFileTypes: true })
|
||||
.filter((d) => d.isDirectory())
|
||||
.map((d) => d.name);
|
||||
|
||||
test('there are native capabilities to check', () => {
|
||||
assert.ok(ids.length >= 30, `expected the full native capability set, found ${ids.length}`);
|
||||
});
|
||||
|
||||
for (const id of ids) {
|
||||
test(`capabilities/${id}/capability.json has a semver version`, () => {
|
||||
const cap = JSON.parse(fs.readFileSync(path.join(CAPABILITIES_DIR, id, 'capability.json'), 'utf8'));
|
||||
assert.equal(typeof cap.version, 'string', `${id}: version must be a string`);
|
||||
assert.ok(SEMVER.test(cap.version), `${id}: version "${cap.version}" must be semver`);
|
||||
});
|
||||
|
||||
test(`capabilities/${id}/capability.json declares engines.gsd`, () => {
|
||||
const cap = JSON.parse(fs.readFileSync(path.join(CAPABILITIES_DIR, id, 'capability.json'), 'utf8'));
|
||||
assert.ok(cap.engines && typeof cap.engines.gsd === 'string' && cap.engines.gsd.length > 0,
|
||||
`${id}: engines.gsd must be a non-empty string`);
|
||||
});
|
||||
|
||||
test(`capabilities/${id}/capability.json passes validateCapability`, () => {
|
||||
const cap = JSON.parse(fs.readFileSync(path.join(CAPABILITIES_DIR, id, 'capability.json'), 'utf8'));
|
||||
assert.deepEqual(validateCapability(cap, id), [], `${id}: native manifest must validate`);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1470,6 +1470,7 @@ describe('S1: fragment.path traversal guard', () => {
|
||||
JSON.stringify({
|
||||
id: 'planning-advice',
|
||||
role: 'feature',
|
||||
version: '1.0.0',
|
||||
title: 'Planning advice',
|
||||
description: 'Synthetic fixture for fragment path materialization.',
|
||||
tier: 'full',
|
||||
@@ -1515,6 +1516,7 @@ describe('S1: fragment.path traversal guard', () => {
|
||||
JSON.stringify({
|
||||
id: 'research',
|
||||
role: 'feature',
|
||||
version: '1.0.0',
|
||||
title: 'Research',
|
||||
description: 'Synthetic fixture for step fragment materialization.',
|
||||
tier: 'standard',
|
||||
@@ -1702,7 +1704,7 @@ describe('C3: role:runtime body validation', () => {
|
||||
// configHome is now an object (Decision 1), artifactLayout is { global, local } (Decision 3),
|
||||
// commandStyle is closed enum (Decision 4), hooksSurface is closed enum (Decision 5).
|
||||
const VALID_RUNTIME_CAP = {
|
||||
id: 'cursor', role: 'runtime', title: 'Cursor', description: 'Cursor IDE runtime',
|
||||
id: 'cursor', role: 'runtime', version: '1.0.0', title: 'Cursor', description: 'Cursor IDE runtime',
|
||||
tier: 'standard', requires: [],
|
||||
runtime: {
|
||||
configHome: { kind: 'dot-home', name: '.cursor', env: ['CURSOR_CONFIG_DIR'] },
|
||||
@@ -2886,6 +2888,7 @@ function makeCommandCap(id, commands) {
|
||||
return {
|
||||
id,
|
||||
role: 'feature',
|
||||
version: '1.0.0',
|
||||
title: 'Test cap ' + id,
|
||||
description: 'Synthetic capability for ADR-959 command tests.',
|
||||
tier: 'full',
|
||||
@@ -3085,6 +3088,7 @@ function makeRuntimeCap(overrides) {
|
||||
return {
|
||||
id: 'test-rt',
|
||||
role: 'runtime',
|
||||
version: '1.0.0',
|
||||
title: 'Test Runtime',
|
||||
description: 'A synthetic runtime capability for testing.',
|
||||
tier: 'core',
|
||||
|
||||
@@ -27,6 +27,8 @@ const {
|
||||
syncManifestVersions,
|
||||
getPackageVersion,
|
||||
stageManifests,
|
||||
listCapabilityManifests,
|
||||
syncCapabilityVersions,
|
||||
} = require(path.join(ROOT, 'scripts', 'sync-manifest-versions.cjs'));
|
||||
|
||||
// ─── A: RED→GREEN repro via temp fixture ─────────────────────────────────────
|
||||
@@ -172,12 +174,91 @@ describe('B: real manifests match package.json version', () => {
|
||||
}
|
||||
});
|
||||
|
||||
// ─── B2: native capability manifests track package.json version (ADR-1244 D6) ─
|
||||
describe('B2: native capability manifests match package.json version', () => {
|
||||
|
||||
const pkgVersion = getPackageVersion(ROOT);
|
||||
const capManifests = listCapabilityManifests({ root: ROOT });
|
||||
|
||||
test('there is at least one native capability manifest', () => {
|
||||
assert.ok(capManifests.length >= 30, `expected the native capability set, found ${capManifests.length}`);
|
||||
});
|
||||
|
||||
for (const rel of capManifests) {
|
||||
test(`${rel} version === ${pkgVersion}`, () => {
|
||||
const m = JSON.parse(fs.readFileSync(path.join(ROOT, rel), 'utf8'));
|
||||
assert.equal(
|
||||
m.version,
|
||||
pkgVersion,
|
||||
`${rel} version (${m.version}) must match package.json version (${pkgVersion}). ` +
|
||||
'Run `node scripts/sync-manifest-versions.cjs` to fix.'
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ─── B3: syncCapabilityVersions stamps + is idempotent (temp fixture) ─────────
|
||||
describe('B3: syncCapabilityVersions — temp fixture', () => {
|
||||
|
||||
test('stamps stale capability manifests to package version, then is idempotent', () => {
|
||||
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-844-cap-'));
|
||||
try {
|
||||
fs.writeFileSync(
|
||||
path.join(tmpRoot, 'package.json'),
|
||||
JSON.stringify({ name: 'x', version: '9.9.9-test.0' }, null, 2) + '\n'
|
||||
);
|
||||
// Two stale capability manifests.
|
||||
for (const id of ['alpha', 'beta']) {
|
||||
const dir = path.join(tmpRoot, 'capabilities', id);
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(dir, 'capability.json'),
|
||||
JSON.stringify({ id, role: 'feature', version: '0.0.0', title: id }, null, 2) + '\n'
|
||||
);
|
||||
}
|
||||
|
||||
const found = listCapabilityManifests({ root: tmpRoot });
|
||||
assert.equal(found.length, 2, 'should discover both capability manifests');
|
||||
|
||||
const changed = syncCapabilityVersions({ root: tmpRoot });
|
||||
assert.equal(changed.length, 2, 'both manifests should be stamped on first run');
|
||||
for (const rel of found) {
|
||||
const m = JSON.parse(fs.readFileSync(path.join(tmpRoot, rel), 'utf8'));
|
||||
assert.equal(m.version, '9.9.9-test.0', `${rel} should be stamped`);
|
||||
assert.equal(m.title, m.id, `${rel} non-version fields preserved`);
|
||||
}
|
||||
|
||||
// Idempotent second run.
|
||||
assert.deepEqual(syncCapabilityVersions({ root: tmpRoot }), [], 'second run is a no-op');
|
||||
} finally {
|
||||
helpers.cleanup(tmpRoot);
|
||||
}
|
||||
});
|
||||
|
||||
test('listCapabilityManifests returns [] when there is no capabilities/ dir', () => {
|
||||
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-844-nocaps-'));
|
||||
try {
|
||||
assert.deepEqual(listCapabilityManifests({ root: tmpRoot }), []);
|
||||
} finally {
|
||||
helpers.cleanup(tmpRoot);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ─── C: Regression guard — all version-bearing JSON files are registered ──────
|
||||
describe('C: regression guard — version-bearing JSON files must be registered', () => {
|
||||
|
||||
// package.json is the version source; package-lock.json is npm-managed.
|
||||
// Both inherently track the version without the sync script.
|
||||
const ALLOWED = new Set([...VERSIONED_MANIFESTS, 'package.json', 'package-lock.json']);
|
||||
// Native capability manifests (capabilities/<id>/capability.json) are
|
||||
// version-swept by syncCapabilityVersions (ADR-1244 D6) — discovered by glob,
|
||||
// so every one is "registered" without an explicit entry here.
|
||||
const ALLOWED = new Set([
|
||||
...VERSIONED_MANIFESTS,
|
||||
...listCapabilityManifests({ root: ROOT }),
|
||||
'package.json',
|
||||
'package-lock.json',
|
||||
]);
|
||||
|
||||
// Semver-ish: matches X.Y.Z with optional pre-release/build metadata.
|
||||
const SEMVER = /^\d+\.\d+\.\d+(?:[-+].+)?$/;
|
||||
|
||||
Reference in New Issue
Block a user