sdtk-kit 0.3.2 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/README.md +40 -3
  2. package/assets/manifest/toolkit-bundle.manifest.json +87 -12
  3. package/assets/manifest/toolkit-bundle.sha256.txt +19 -4
  4. package/assets/toolkit/toolkit/AGENTS.md +20 -16
  5. package/assets/toolkit/toolkit/install.ps1 +122 -7
  6. package/assets/toolkit/toolkit/runtimes/claude/CLAUDE_TEMPLATE.md +32 -10
  7. package/assets/toolkit/toolkit/scripts/install-claude-skills.ps1 +129 -0
  8. package/assets/toolkit/toolkit/scripts/install-codex-skills.ps1 +8 -0
  9. package/assets/toolkit/toolkit/scripts/uninstall-claude-skills.ps1 +139 -0
  10. package/assets/toolkit/toolkit/skills-claude/api-design-spec/SKILL.md +76 -0
  11. package/assets/toolkit/toolkit/skills-claude/api-doc/SKILL.md +46 -0
  12. package/assets/toolkit/toolkit/skills-claude/arch/SKILL.md +70 -0
  13. package/assets/toolkit/toolkit/skills-claude/ba/SKILL.md +49 -0
  14. package/assets/toolkit/toolkit/skills-claude/design-layout/SKILL.md +25 -0
  15. package/assets/toolkit/toolkit/skills-claude/dev/SKILL.md +45 -0
  16. package/assets/toolkit/toolkit/skills-claude/dev-backend/SKILL.md +20 -0
  17. package/assets/toolkit/toolkit/skills-claude/dev-frontend/SKILL.md +18 -0
  18. package/assets/toolkit/toolkit/skills-claude/orchestrator/SKILL.md +63 -0
  19. package/assets/toolkit/toolkit/skills-claude/pm/SKILL.md +52 -0
  20. package/assets/toolkit/toolkit/skills-claude/qa/SKILL.md +47 -0
  21. package/assets/toolkit/toolkit/skills-claude/screen-design-spec/SKILL.md +68 -0
  22. package/assets/toolkit/toolkit/skills-claude/test-case-spec/SKILL.md +63 -0
  23. package/package.json +2 -2
  24. package/src/commands/help.js +30 -4
  25. package/src/commands/init.js +25 -1
  26. package/src/commands/runtime.js +217 -0
  27. package/src/index.js +4 -1
  28. package/src/lib/scope.js +68 -0
@@ -0,0 +1,47 @@
1
+ ---
2
+ name: qa
3
+ description: QA workflow for SDTK. Use when you need to validate an implemented feature against BA/PRD/Backlog, run quality gates, document defects, and produce a QA_RELEASE_REPORT with a release decision.
4
+ ---
5
+
6
+ ## SDTK Pipeline Rules (always apply)
7
+ 1. Pipeline: PM Initiation → BA Analysis → PM Planning → Architecture Design → Development + Review → QA Validation
8
+ 2. After completing phase work: update SHARED_PLANNING.md + QUALITY_CHECKLIST.md
9
+ 3. If unclear: log OQ-xx in artifact, escalate to PM (PM asks user if needed)
10
+ 4. Traceability: REQ → BR/UC/AC → design → backlog → code/tests → QA
11
+ 5. Code review must be COMPLETE before QA phase can start
12
+ 6. Do not skip phases. If inputs missing, ask focused questions.
13
+
14
+ ## Prerequisites (verify before proceeding)
15
+ Read QUALITY_CHECKLIST.md and verify:
16
+ - Phase 4 DEV gate must show all items [x] Done (including code review completion).
17
+
18
+ If prerequisites NOT met: report which gate is missing, suggest user run `/dev` first to complete code review.
19
+
20
+ ## Current Context
21
+ - Config: !`node -e "try{process.stdout.write(require('fs').readFileSync('sdtk.config.json','utf8'))}catch{process.stdout.write('{}')}"`
22
+ - Pipeline: !`node -e "try{process.stdout.write(require('fs').readFileSync('SHARED_PLANNING.md','utf8'))}catch{process.stdout.write('Not initialized')}"`
23
+ - Gates: !`node -e "try{process.stdout.write(require('fs').readFileSync('QUALITY_CHECKLIST.md','utf8'))}catch{process.stdout.write('Not initialized')}"`
24
+ - State: !`node -e "try{process.stdout.write(require('fs').readFileSync('.sdtk/orchestration-state.json','utf8'))}catch{process.stdout.write('{}')}"`
25
+
26
+ ## Input
27
+ $ARGUMENTS
28
+
29
+ If no arguments provided, read current feature context from SHARED_PLANNING.md.
30
+
31
+ # SDTK QA (Testing + Release Decision)
32
+
33
+ ## Output
34
+ - `docs/qa/QA_RELEASE_REPORT_[FEATURE_KEY].md`
35
+ - Optional (when detailed test design spec is requested):
36
+ - `docs/qa/[FEATURE_KEY]_TEST_CASE.md` via `/test-case-spec`
37
+
38
+ ## Process
39
+ 1. Validate prerequisites: DEV phase completed + code review PASS.
40
+ 2. Create test strategy mapped to UC/AC.
41
+ 3. If test-case specification artifact is required, invoke `/test-case-spec` and align counts/coverage with release testing scope.
42
+ 4. If acceptance criteria / expected behavior is unclear: record OQ-xx in QA report and escalate to PM (suggest user run `/pm` if still missing info).
43
+ 5. Execute/record results (unit/integration/e2e as applicable).
44
+ 6. Record defects with severity and status.
45
+ 7. Decide: APPROVED vs REJECTED (with reasons).
46
+ 8. Update shared state + Phase 5 checklist.
47
+ 9. Handoff: suggest user run `/pm` to finalize release status if approved.
@@ -0,0 +1,68 @@
1
+ ---
2
+ name: screen-design-spec
3
+ description: Create/update screen flow-action specifications from requirement sources (Excel/Figma/spec docs), including screen flow PlantUML, UI item/action tables, API mapping tables, and screen-to-API traceability.
4
+ ---
5
+
6
+ ## Required Inputs (read before proceeding)
7
+ Read the following artifacts for the current feature:
8
+ 1. `docs/specs/BA_SPEC_*.md` — business rules, use cases
9
+ 2. `docs/architecture/ARCH_DESIGN_*.md` — system components
10
+ 3. `docs/api/[FEATURE_KEY]_ENDPOINTS.md` — API endpoints (when available)
11
+
12
+ ## Input
13
+ $ARGUMENTS
14
+
15
+ # SDTK Screen Flow Action Spec
16
+
17
+ ## Outputs
18
+ - `docs/specs/[FEATURE_KEY]_FLOW_ACTION_SPEC.md`
19
+ - Optional supporting docs:
20
+ - `docs/specs/[FEATURE_KEY]_FIGMA_LAYOUT.md`
21
+ - `docs/specs/[FEATURE_KEY]_DESIGN_SPEC_FROM_EXCEL.md`
22
+ - `docs/specs/assets/[feature_snake]/screens/*`
23
+
24
+ ## Required Inputs
25
+ - Feature key/name
26
+ - Primary requirement sources (BA spec, architecture, customer design docs)
27
+ - Screen source references (Figma URLs and/or requirement screenshots)
28
+ - API endpoint source (`docs/api/[FEATURE_KEY]_ENDPOINTS.md`) when available
29
+
30
+ ## Process
31
+ 1. Read requirement sources and identify in-scope screens, dialogs, and transitions.
32
+ 2. Read and apply rules from `.claude/skills/references/FLOW_ACTION_SPEC_CREATION_RULES.md`.
33
+ 3. Build/update section `Feature overview` and `Screen flow action` (PlantUML).
34
+ 4. For each screen/dialog:
35
+ - Add metadata (screen ID, source link)
36
+ - Add screen image reference
37
+ - Add UI item/action table with `No` column
38
+ - Add API mapping table (trigger -> API -> data usage)
39
+ 5. Build/update `System processing flow` from use-case and process sources.
40
+ 6. Build/update `Open questions` for unresolved behavior/API/data points.
41
+ 7. Build/update `Screen - API Mapping` summary section.
42
+ 8. Validate:
43
+ - global numbering consistency (no screen-level reset)
44
+ - no broken image paths
45
+ - PlantUML renderability
46
+ - consistency with API endpoints spec
47
+ - EN artifact hygiene (no VI leftovers, no mojibake, no merged heading lines)
48
+ - for legacy specs with per-screen reset numbering: run renumber migration first
49
+ 9. Update document history and handoff to ARCH/DEV.
50
+
51
+ ## Rule References
52
+ - Core rules: `.claude/skills/references/FLOW_ACTION_SPEC_CREATION_RULES.md`
53
+ - Numbering reference: `.claude/skills/references/numbering-rules.md`
54
+ - Figma reference: `.claude/skills/references/figma-mcp.md`
55
+ - Excel image export reference: `.claude/skills/references/excel-image-export.md`
56
+
57
+ ## Validation Script
58
+ - `scripts/validate_flow_action_spec_numbering.py`
59
+ - Checks duplicate/resetted numbering in action tables.
60
+ - Checks encoding/markdown hygiene.
61
+ - For EN artifacts, run with `--en-check`:
62
+ - `python scripts/validate_flow_action_spec_numbering.py --spec "docs/specs/[FEATURE_KEY]_FLOW_ACTION_SPEC.md" --en-check`
63
+ - `scripts/renumber_flow_action_spec_global.py`
64
+ - Auto-migrates action table `No` fields to global numbering.
65
+ - Dry-run:
66
+ - `python scripts/renumber_flow_action_spec_global.py --spec "docs/specs/[FEATURE_KEY]_FLOW_ACTION_SPEC.md"`
67
+ - Apply:
68
+ - `python scripts/renumber_flow_action_spec_global.py --spec "docs/specs/[FEATURE_KEY]_FLOW_ACTION_SPEC.md" --write`
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: test-case-spec
3
+ description: Generate screen-based QA test-case markdown in Excel-aligned layout (Statistic + per-screen UTC/ITC worksheets). Use when QA needs reusable test design artifacts before or during execution.
4
+ ---
5
+
6
+ ## Required Inputs (read before proceeding)
7
+ Read the following artifacts for the current feature:
8
+ 1. `docs/specs/[FEATURE_KEY]_FLOW_ACTION_SPEC.md` — screen/flow references
9
+ 2. `docs/specs/BA_SPEC_[FEATURE_KEY].md` — business rules, use cases
10
+ 3. `docs/api/[FEATURE_KEY]_ENDPOINTS.md` — API reference
11
+ 4. `docs/specs/Q&A.md` or `docs/en/specs/Q&A.md` — clarification source (when available)
12
+
13
+ ## Input
14
+ $ARGUMENTS
15
+
16
+ # SDTK Test Case Spec
17
+
18
+ ## Outputs
19
+ - `docs/qa/[FEATURE_KEY]_TEST_CASE.md`
20
+ - Optional project variant:
21
+ - `docs/en/qa/[FEATURE_KEY]_TEST_CASE.md` (only when project uses `docs/en/**`)
22
+
23
+ ## Core Rules
24
+ 1. Apply `.claude/skills/references/TEST_CASE_CREATION_RULES.md` first.
25
+ 2. Keep section order fixed (Statistic -> Abbreviations -> Scope -> References -> Environment -> Coverage -> Screen-based UTC/ITC -> OQ -> STC/UAT note).
26
+ 3. Use screen-first layout to mirror worksheet structure:
27
+ - each screen can have `[SCREEN_KEY]_UTC` and `[SCREEN_KEY]_ITC`.
28
+ 4. Keep one unified 18-column schema for UTC/ITC rows.
29
+ 5. Keep stable case IDs; do not renumber only because of regrouping.
30
+ 6. Track unresolved decisions via `OQ-xx`.
31
+
32
+ ## Procedure
33
+ 1. Resolve output path:
34
+ - default `docs/qa/[FEATURE_KEY]_TEST_CASE.md`
35
+ - use `docs/en/qa/...` only if the repo already follows `docs/en/**`.
36
+ 2. Build/refresh `Statistic Summary (Excel-aligned)`:
37
+ - include UT total, IT total, grand total.
38
+ 3. Build `Feature Coverage Matrix` from flow/action and API docs.
39
+ 4. Split UTC/ITC by screen sections (`screen-first`), not by test type only.
40
+ 5. Fill conflict/permission/error-path cases from Q&A decisions and API contracts.
41
+ 6. Record unresolved items in section `Open Questions`.
42
+ 7. Validate:
43
+ - counts in summary match table rows
44
+ - no placeholder tokens like `??`
45
+ - out-of-scope boundaries are explicit
46
+
47
+ ## Role Integration
48
+ - Primary owner: `/qa`.
49
+ - `/qa` uses this skill to design/update reusable test-case specs.
50
+ - `/qa` still owns release decision artifact (`QA_RELEASE_REPORT_*`).
51
+
52
+ ## Notes
53
+ - This skill is for test-case specification artifacts, not test execution logs.
54
+ - For release approval and defect triage, continue with `/qa`.
55
+
56
+ ## Validator Script
57
+ - `scripts/validate_test_case_spec.py`
58
+
59
+ ### Typical command
60
+ ```bash
61
+ python scripts/validate_test_case_spec.py \
62
+ --file "docs/qa/[FEATURE_KEY]_TEST_CASE.md"
63
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sdtk-kit",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "SDTK CLI toolkit for deterministic software documentation workflows",
5
5
  "bin": {
6
6
  "sdtk": "./bin/sdtk.js"
@@ -36,7 +36,7 @@
36
36
  "url": "https://github.com/codexsdtk/sdtk-toolkit.git",
37
37
  "directory": "distribution/sdtk-cli"
38
38
  },
39
- "homepage": "https://sdtk.dev",
39
+ "homepage": "https://agenttoolkits.dev",
40
40
  "bugs": {
41
41
  "url": "https://github.com/codexsdtk/sdtk-toolkit/issues"
42
42
  },
@@ -9,15 +9,36 @@ function cmdHelp() {
9
9
  "",
10
10
  "Commands:",
11
11
  " init Initialize SDTK workspace (runtime adapter + config)",
12
+ " runtime Manage runtime asset installation (install, uninstall, status)",
12
13
  " auth Manage GitHub authentication and entitlement",
13
14
  " generate Scaffold feature documentation from templates (17 files)",
14
15
  "",
15
16
  "Init options:",
16
- " --runtime <codex|claude> Runtime adapter (default: codex)",
17
+ " --runtime <codex|claude> Runtime adapter (default: codex)",
18
+ " --runtime-scope <project|user> Install scope (default: project for claude, user for codex)",
19
+ " --project-path <path> Target project directory (default: cwd)",
20
+ " --force Overwrite existing files",
21
+ " --skip-runtime-assets Skip runtime asset installation",
22
+ " --skip-skills (deprecated, use --skip-runtime-assets)",
23
+ " --verbose Show detailed PowerShell script output",
24
+ "",
25
+ "Runtime subcommands:",
26
+ " sdtk runtime install --runtime <codex|claude> [--scope <project|user>]",
27
+ " sdtk runtime uninstall --runtime <codex|claude> [--scope <project|user>] [--all]",
28
+ " sdtk runtime status --runtime <codex|claude>",
29
+ "",
30
+ "Runtime options:",
31
+ " --runtime <codex|claude> Target runtime (required)",
32
+ " --scope <project|user> Install scope (default: project for claude, user for codex)",
17
33
  " --project-path <path> Target project directory (default: cwd)",
18
- " --force Overwrite existing files",
19
- " --skip-skills Skip Codex skill installation",
20
- " --verbose Show detailed PowerShell script output",
34
+ " --force Overwrite existing skills",
35
+ " --all Uninstall all managed skills",
36
+ " --verbose Show detailed script output",
37
+ "",
38
+ "Scope behavior:",
39
+ " project Skills are installed inside the repo (Claude only: .claude/skills/)",
40
+ " user Skills are installed in the runtime home (~/.claude/skills/ or ~/.codex/skills/)",
41
+ " Note: Codex supports user scope only. Project scope is not available for Codex.",
21
42
  "",
22
43
  "Auth options:",
23
44
  " --token <value> Store GitHub PAT",
@@ -60,6 +81,11 @@ function cmdHelp() {
60
81
  " sdtk auth --verify",
61
82
  ' sdtk generate --feature-key USER_PROFILE --feature-name "User Profile"',
62
83
  " sdtk generate --feature-key ORDER_MGMT --feature-name \"Order Management\" --validate-only",
84
+ " sdtk init --runtime claude --runtime-scope user",
85
+ " sdtk runtime install --runtime claude --scope project",
86
+ " sdtk runtime install --runtime codex --scope user",
87
+ " sdtk runtime status --runtime claude",
88
+ " sdtk runtime uninstall --runtime claude --scope project --all",
63
89
  "",
64
90
  " # Override entitlement repo (bash/zsh):",
65
91
  " export SDTK_ENTITLEMENT_REPO=owner/repo",
@@ -5,12 +5,15 @@ const { parseFlags, validateChoice } = require("../lib/args");
5
5
  const { verify, resolvePayloadFile } = require("../lib/toolkit-payload");
6
6
  const { runScript } = require("../lib/powershell");
7
7
  const { ValidationError } = require("../lib/errors");
8
+ const { VALID_SCOPES, defaultScope, isProjectScopeSupported } = require("../lib/scope");
8
9
 
9
10
  const FLAG_DEFS = {
10
11
  runtime: { type: "string" },
11
12
  "project-path": { type: "string" },
13
+ "runtime-scope": { type: "string" },
12
14
  force: { type: "boolean" },
13
15
  "skip-skills": { type: "boolean" },
16
+ "skip-runtime-assets": { type: "boolean" },
14
17
  verbose: { type: "boolean" },
15
18
  };
16
19
 
@@ -23,6 +26,25 @@ async function cmdInit(args) {
23
26
  const runtime = flags.runtime || "codex";
24
27
  validateChoice(runtime, VALID_RUNTIMES, "runtime");
25
28
 
29
+ // Resolve scope
30
+ const scope = flags["runtime-scope"] || defaultScope(runtime);
31
+ validateChoice(scope, VALID_SCOPES, "runtime-scope");
32
+
33
+ // Gate C0: Codex does not support project-local skills
34
+ if (scope === "project" && !isProjectScopeSupported(runtime)) {
35
+ throw new ValidationError(
36
+ "Codex does not support project-local skills. Use --runtime-scope user instead."
37
+ );
38
+ }
39
+
40
+ // Handle deprecated --skip-skills
41
+ const skipAssets = flags["skip-runtime-assets"] || flags["skip-skills"];
42
+ if (flags["skip-skills"] && !flags["skip-runtime-assets"]) {
43
+ console.warn(
44
+ "Warning: --skip-skills is deprecated. Use --skip-runtime-assets instead."
45
+ );
46
+ }
47
+
26
48
  // Resolve project path
27
49
  const projectPath = flags["project-path"]
28
50
  ? path.resolve(flags["project-path"])
@@ -38,13 +60,15 @@ async function cmdInit(args) {
38
60
  const params = {
39
61
  ProjectPath: projectPath,
40
62
  Runtime: runtime,
63
+ Scope: scope,
41
64
  };
42
65
  if (flags.force) params.Force = true;
43
- if (flags["skip-skills"]) params.SkipSkills = true;
66
+ if (skipAssets) params.SkipRuntimeAssets = true;
44
67
  if (!flags.verbose) params.Quiet = true;
45
68
 
46
69
  console.log(`Initializing SDTK workspace...`);
47
70
  console.log(` Runtime: ${runtime}`);
71
+ console.log(` Scope: ${scope}`);
48
72
  console.log(` Project: ${projectPath}`);
49
73
  console.log("");
50
74
 
@@ -0,0 +1,217 @@
1
+ "use strict";
2
+
3
+ const path = require("path");
4
+ const fs = require("fs");
5
+ const { parseFlags, requireFlag, validateChoice } = require("../lib/args");
6
+ const { verify, resolvePayloadFile } = require("../lib/toolkit-payload");
7
+ const { runScript } = require("../lib/powershell");
8
+ const { ValidationError } = require("../lib/errors");
9
+ const {
10
+ VALID_SCOPES,
11
+ defaultScope,
12
+ isProjectScopeSupported,
13
+ resolveSkillsDir,
14
+ managedSkillNames,
15
+ } = require("../lib/scope");
16
+
17
+ const FLAG_DEFS = {
18
+ runtime: { type: "string" },
19
+ scope: { type: "string" },
20
+ "project-path": { type: "string" },
21
+ force: { type: "boolean" },
22
+ all: { type: "boolean" },
23
+ verbose: { type: "boolean" },
24
+ };
25
+
26
+ const VALID_RUNTIMES = ["codex", "claude"];
27
+ const SUBCOMMANDS = ["install", "uninstall", "status"];
28
+
29
+ function validateScopeForRuntime(runtime, scope) {
30
+ if (scope === "project" && !isProjectScopeSupported(runtime)) {
31
+ throw new ValidationError(
32
+ `Codex does not support project-local skills. Use --scope user instead.`
33
+ );
34
+ }
35
+ }
36
+
37
+ async function cmdRuntimeInstall(flags) {
38
+ const runtime = requireFlag(flags, "runtime", "runtime");
39
+ validateChoice(runtime, VALID_RUNTIMES, "runtime");
40
+
41
+ const scope = flags.scope || defaultScope(runtime);
42
+ validateChoice(scope, VALID_SCOPES, "scope");
43
+ validateScopeForRuntime(runtime, scope);
44
+
45
+ const projectPath = flags["project-path"]
46
+ ? path.resolve(flags["project-path"])
47
+ : process.cwd();
48
+
49
+ verify();
50
+
51
+ const scriptName =
52
+ runtime === "claude"
53
+ ? "toolkit/scripts/install-claude-skills.ps1"
54
+ : "toolkit/scripts/install-codex-skills.ps1";
55
+ const script = resolvePayloadFile(scriptName);
56
+
57
+ const params = { Scope: scope };
58
+ if (runtime === "claude") {
59
+ params.ProjectPath = projectPath;
60
+ }
61
+ if (flags.force) params.Force = true;
62
+
63
+ console.log(`Installing ${runtime} runtime assets...`);
64
+ console.log(` Runtime: ${runtime}`);
65
+ console.log(` Scope: ${scope}`);
66
+ if (scope === "project") {
67
+ console.log(` Project: ${projectPath}`);
68
+ }
69
+ console.log("");
70
+
71
+ const result = await runScript(script, params, {
72
+ silent: !flags.verbose,
73
+ });
74
+
75
+ if (result.exitCode !== 0) {
76
+ if (result.stderr) console.error(result.stderr);
77
+ throw new ValidationError(
78
+ `Runtime install failed (exit code ${result.exitCode}).`
79
+ );
80
+ }
81
+
82
+ console.log("");
83
+ console.log(`${runtime} runtime assets installed successfully.`);
84
+ return 0;
85
+ }
86
+
87
+ async function cmdRuntimeUninstall(flags) {
88
+ const runtime = requireFlag(flags, "runtime", "runtime");
89
+ validateChoice(runtime, VALID_RUNTIMES, "runtime");
90
+
91
+ const scope = flags.scope || defaultScope(runtime);
92
+ validateChoice(scope, VALID_SCOPES, "scope");
93
+ validateScopeForRuntime(runtime, scope);
94
+
95
+ const projectPath = flags["project-path"]
96
+ ? path.resolve(flags["project-path"])
97
+ : process.cwd();
98
+
99
+ verify();
100
+
101
+ const scriptName =
102
+ runtime === "claude"
103
+ ? "toolkit/scripts/uninstall-claude-skills.ps1"
104
+ : "toolkit/scripts/uninstall-codex-skills.ps1";
105
+ const script = resolvePayloadFile(scriptName);
106
+
107
+ const params = {};
108
+ if (runtime === "claude") {
109
+ params.Scope = scope;
110
+ params.ProjectPath = projectPath;
111
+ }
112
+ if (flags.all) params.All = true;
113
+
114
+ console.log(`Uninstalling ${runtime} runtime assets...`);
115
+ console.log(` Runtime: ${runtime}`);
116
+ console.log(` Scope: ${scope}`);
117
+ console.log("");
118
+
119
+ const result = await runScript(script, params, {
120
+ silent: !flags.verbose,
121
+ });
122
+
123
+ if (result.exitCode !== 0) {
124
+ if (result.stderr) console.error(result.stderr);
125
+ throw new ValidationError(
126
+ `Runtime uninstall failed (exit code ${result.exitCode}).`
127
+ );
128
+ }
129
+
130
+ console.log("");
131
+ console.log(`${runtime} runtime assets uninstalled successfully.`);
132
+ return 0;
133
+ }
134
+
135
+ function cmdRuntimeStatus(flags) {
136
+ const runtime = requireFlag(flags, "runtime", "runtime");
137
+ validateChoice(runtime, VALID_RUNTIMES, "runtime");
138
+
139
+ const projectPath = flags["project-path"]
140
+ ? path.resolve(flags["project-path"])
141
+ : process.cwd();
142
+
143
+ console.log(`Runtime: ${runtime}`);
144
+ console.log("");
145
+
146
+ // Check both scopes
147
+ const scopes = runtime === "claude" ? ["project", "user"] : ["user"];
148
+
149
+ for (const scope of scopes) {
150
+ const skillsDir = resolveSkillsDir(runtime, scope, projectPath);
151
+ const exists = fs.existsSync(skillsDir);
152
+ let skillCount = 0;
153
+
154
+ const managed = managedSkillNames(runtime);
155
+ let installedNames = [];
156
+
157
+ if (exists) {
158
+ try {
159
+ const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
160
+ installedNames = entries
161
+ .filter((e) => e.isDirectory() && managed.includes(e.name))
162
+ .map((e) => e.name);
163
+ skillCount = installedNames.length;
164
+ } catch {
165
+ // Directory not readable
166
+ }
167
+ }
168
+
169
+ const allInstalled = skillCount === managed.length;
170
+
171
+ console.log(` Scope: ${scope}`);
172
+ console.log(` Path: ${skillsDir}`);
173
+ const statusLabel = !exists
174
+ ? "not installed"
175
+ : allInstalled
176
+ ? `installed (${skillCount}/${managed.length} SDTK skills)`
177
+ : `partial (${skillCount}/${managed.length} SDTK skills)`;
178
+ console.log(` Status: ${statusLabel}`);
179
+ if (exists && skillCount < managed.length) {
180
+ const missing = managed.filter((n) => !installedNames.includes(n));
181
+ console.log(` Missing: ${missing.join(", ")}`);
182
+ }
183
+ console.log("");
184
+ }
185
+
186
+ return 0;
187
+ }
188
+
189
+ async function cmdRuntime(args) {
190
+ const { flags, positional } = parseFlags(args, FLAG_DEFS);
191
+
192
+ const subcommand = positional[0];
193
+ if (!subcommand) {
194
+ throw new ValidationError(
195
+ `Missing subcommand. Usage: sdtk runtime <${SUBCOMMANDS.join("|")}> [options]`
196
+ );
197
+ }
198
+
199
+ if (!SUBCOMMANDS.includes(subcommand)) {
200
+ throw new ValidationError(
201
+ `Unknown subcommand: "${subcommand}". Must be one of: ${SUBCOMMANDS.join(", ")}`
202
+ );
203
+ }
204
+
205
+ switch (subcommand) {
206
+ case "install":
207
+ return cmdRuntimeInstall(flags);
208
+ case "uninstall":
209
+ return cmdRuntimeUninstall(flags);
210
+ case "status":
211
+ return cmdRuntimeStatus(flags);
212
+ }
213
+ }
214
+
215
+ module.exports = {
216
+ cmdRuntime,
217
+ };
package/src/index.js CHANGED
@@ -4,6 +4,7 @@ const { cmdHelp } = require("./commands/help");
4
4
  const { cmdInit } = require("./commands/init");
5
5
  const { cmdAuth } = require("./commands/auth");
6
6
  const { cmdGenerate } = require("./commands/generate");
7
+ const { cmdRuntime } = require("./commands/runtime");
7
8
  const { ValidationError, CliError } = require("./lib/errors");
8
9
 
9
10
  function getVersion() {
@@ -25,7 +26,7 @@ function parseCommand(argv) {
25
26
  return { command: first, args: rest };
26
27
  }
27
28
 
28
- const COMMANDS = new Set(["help", "version", "init", "auth", "generate"]);
29
+ const COMMANDS = new Set(["help", "version", "init", "auth", "generate", "runtime"]);
29
30
 
30
31
  async function run(argv) {
31
32
  const { command, args } = parseCommand(argv);
@@ -48,6 +49,8 @@ async function run(argv) {
48
49
  return cmdAuth(args);
49
50
  case "generate":
50
51
  return cmdGenerate(args);
52
+ case "runtime":
53
+ return cmdRuntime(args);
51
54
  }
52
55
  }
53
56
 
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+
3
+ const path = require("path");
4
+ const os = require("os");
5
+
6
+ const VALID_SCOPES = ["project", "user"];
7
+
8
+ /**
9
+ * Returns the default scope for a given runtime.
10
+ * Claude defaults to project-local, Codex defaults to user/global.
11
+ */
12
+ function defaultScope(runtime) {
13
+ return runtime === "claude" ? "project" : "user";
14
+ }
15
+
16
+ /**
17
+ * Returns true if the given runtime supports project-local scope.
18
+ * Gate C0: Codex does not support project-local skills.
19
+ */
20
+ function isProjectScopeSupported(runtime) {
21
+ return runtime === "claude";
22
+ }
23
+
24
+ /**
25
+ * Resolves the skills directory for a given runtime, scope, and project path.
26
+ */
27
+ function resolveSkillsDir(runtime, scope, projectPath) {
28
+ if (runtime === "claude") {
29
+ if (scope === "user") {
30
+ return path.join(os.homedir(), ".claude", "skills");
31
+ }
32
+ return path.join(projectPath || process.cwd(), ".claude", "skills");
33
+ }
34
+
35
+ // Codex: always user/global scope
36
+ const codexHome = process.env.CODEX_HOME || path.join(os.homedir(), ".codex");
37
+ return path.join(codexHome, "skills");
38
+ }
39
+
40
+ /**
41
+ * SDTK-managed skill directory names per runtime.
42
+ * Used by runtime status to distinguish SDTK skills from unrelated user skills.
43
+ */
44
+ const MANAGED_CODEX_SKILLS = [
45
+ "sdtk-api-design-spec", "sdtk-api-doc", "sdtk-arch", "sdtk-ba",
46
+ "sdtk-design-layout", "sdtk-dev", "sdtk-dev-backend", "sdtk-dev-frontend",
47
+ "sdtk-orchestrator", "sdtk-pm", "sdtk-qa", "sdtk-screen-design-spec",
48
+ "sdtk-test-case-spec",
49
+ ];
50
+
51
+ const MANAGED_CLAUDE_SKILLS = [
52
+ "api-design-spec", "api-doc", "arch", "ba",
53
+ "design-layout", "dev", "dev-backend", "dev-frontend",
54
+ "orchestrator", "pm", "qa", "screen-design-spec",
55
+ "test-case-spec",
56
+ ];
57
+
58
+ function managedSkillNames(runtime) {
59
+ return runtime === "claude" ? MANAGED_CLAUDE_SKILLS : MANAGED_CODEX_SKILLS;
60
+ }
61
+
62
+ module.exports = {
63
+ VALID_SCOPES,
64
+ defaultScope,
65
+ isProjectScopeSupported,
66
+ resolveSkillsDir,
67
+ managedSkillNames,
68
+ };