sdtk-kit 0.3.3 → 0.3.5

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.
@@ -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
+ };