sumulige-claude 1.2.0 → 1.3.0
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.
- package/.claude/.kickoff-hint.txt +3 -2
- package/.claude/AGENTS.md +6 -6
- package/.claude/CLAUDE.md +138 -0
- package/.claude/README.md +234 -43
- package/.claude/USAGE.md +175 -0
- package/.claude/boris-optimizations.md +167 -0
- package/.claude/commands/fix.md +83 -0
- package/.claude/commands/plan.md +88 -0
- package/.claude/commands/refactor.md +102 -0
- package/.claude/commands/todos.md +6 -41
- package/.claude/hooks/code-formatter.cjs +2 -7
- package/.claude/hooks/conversation-logger.cjs +222 -0
- package/.claude/hooks/multi-session.cjs +3 -9
- package/.claude/hooks/pre-push.cjs +3 -2
- package/.claude/hooks/project-kickoff.cjs +198 -20
- package/.claude/hooks/rag-skill-loader.cjs +0 -7
- package/.claude/hooks/session-restore.cjs +0 -0
- package/.claude/hooks/session-save.cjs +0 -0
- package/.claude/hooks/thinking-silent.cjs +3 -9
- package/.claude/hooks/todo-manager.cjs +142 -269
- package/.claude/hooks/verify-work.cjs +4 -10
- package/.claude/rag/skill-index.json +147 -8
- package/.claude/rules/coding-style.md +119 -0
- package/.claude/rules/hooks.md +288 -0
- package/.claude/rules/performance.md +78 -0
- package/.claude/rules/security.md +56 -0
- package/.claude/rules/testing.md +89 -0
- package/.claude/settings.json +115 -0
- package/.claude/settings.local.json +19 -1
- package/.claude/skills/SKILLS.md +145 -0
- package/.claude/skills/design-brain/SKILL.md +190 -0
- package/.claude/skills/design-brain/metadata.yaml +26 -0
- package/.claude/skills/examples/README.md +47 -0
- package/.claude/skills/examples/basic-task.md +67 -0
- package/.claude/skills/examples/bug-fix-workflow.md +92 -0
- package/.claude/skills/examples/feature-development.md +81 -0
- package/.claude/skills/manus-kickoff/SKILL.md +128 -0
- package/.claude/skills/manus-kickoff/examples/basic.md +84 -0
- package/.claude/skills/manus-kickoff/metadata.yaml +33 -0
- package/.claude/skills/manus-kickoff/templates/PROJECT_KICKOFF.md +89 -0
- package/.claude/skills/manus-kickoff/templates/PROJECT_PROPOSAL.md +227 -0
- package/.claude/skills/manus-kickoff/templates/TASK_PLAN.md +121 -0
- package/.claude/skills/quality-guard/SKILL.md +138 -0
- package/.claude/skills/quality-guard/metadata.yaml +27 -0
- package/.claude/skills/quick-fix/SKILL.md +138 -0
- package/.claude/skills/quick-fix/metadata.yaml +26 -0
- package/.claude/skills/test-master/SKILL.md +186 -0
- package/.claude/skills/test-master/metadata.yaml +29 -0
- package/.claude/templates/PROJECT_KICKOFF.md +89 -0
- package/.claude/templates/PROJECT_PROPOSAL.md +227 -0
- package/.claude/templates/TASK_PLAN.md +121 -0
- package/.claude-plugin/marketplace.json +2 -2
- package/AGENTS.md +49 -7
- package/CHANGELOG.md +56 -2
- package/CLAUDE-template.md +114 -0
- package/README.md +73 -1
- package/config/official-skills.json +2 -2
- package/development/knowledge-base/.index.clean.json +1 -0
- package/jest.config.js +3 -1
- package/lib/commands.js +1626 -1207
- package/lib/marketplace.js +1 -0
- package/package.json +1 -1
- package/project-paradigm.md +313 -0
- package/prompts/how-to-find.md +163 -0
- package/tests/commands.test.js +940 -17
- package/tests/config-schema.test.js +425 -0
- package/tests/marketplace.test.js +330 -214
- package/tests/sync-external.test.js +214 -0
- package/tests/update-registry.test.js +251 -0
- package/tests/utils.test.js +12 -8
- package/tests/web-search.test.js +392 -0
- package/thinkinglens-silent.md +138 -0
- package/.claude/skills/api-tester/SKILL.md +0 -90
- package/.claude/skills/api-tester/examples/basic.md +0 -3
- package/.claude/skills/api-tester/metadata.yaml +0 -30
- package/.claude/skills/api-tester/templates/default.md +0 -3
- package/.claude/skills/template/SKILL.md +0 -6
package/lib/commands.js
CHANGED
|
@@ -4,15 +4,22 @@
|
|
|
4
4
|
* Extracted from cli.js for better organization
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const fs = require(
|
|
8
|
-
const path = require(
|
|
9
|
-
const { execSync } = require(
|
|
10
|
-
const {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
const fs = require("fs");
|
|
8
|
+
const path = require("path");
|
|
9
|
+
const { execSync } = require("child_process");
|
|
10
|
+
const {
|
|
11
|
+
loadConfig,
|
|
12
|
+
CONFIG_DIR,
|
|
13
|
+
CONFIG_FILE,
|
|
14
|
+
SKILLS_DIR,
|
|
15
|
+
ensureDir,
|
|
16
|
+
saveConfig,
|
|
17
|
+
} = require("./config");
|
|
18
|
+
const { copyRecursive, toTitleCase, CopyMode } = require("./utils");
|
|
19
|
+
const { runMigrations, TEMPLATE_VERSION } = require("./migrations");
|
|
20
|
+
const { checkUpdate, getCurrentVersion } = require("./version-check");
|
|
21
|
+
|
|
22
|
+
const TEMPLATE_DIR = path.join(__dirname, "../template");
|
|
16
23
|
|
|
17
24
|
// ============================================================================
|
|
18
25
|
// Helper Functions
|
|
@@ -30,7 +37,11 @@ function countFiles(dirPath) {
|
|
|
30
37
|
const fullPath = path.join(dirPath, entry.name);
|
|
31
38
|
if (entry.isDirectory()) {
|
|
32
39
|
// Skip certain directories
|
|
33
|
-
if (
|
|
40
|
+
if (
|
|
41
|
+
entry.name !== "node_modules" &&
|
|
42
|
+
entry.name !== ".git" &&
|
|
43
|
+
entry.name !== "sessions"
|
|
44
|
+
) {
|
|
34
45
|
count += countFiles(fullPath);
|
|
35
46
|
}
|
|
36
47
|
} else {
|
|
@@ -76,8 +87,15 @@ function copySingleFile(srcPath, destPath, mode, backupDir, displayName) {
|
|
|
76
87
|
case CopyMode.BACKUP:
|
|
77
88
|
default:
|
|
78
89
|
// Backup then overwrite
|
|
79
|
-
const timestamp = new Date()
|
|
80
|
-
|
|
90
|
+
const timestamp = new Date()
|
|
91
|
+
.toISOString()
|
|
92
|
+
.replace(/[:.]/g, "-")
|
|
93
|
+
.split("T")[0];
|
|
94
|
+
const backupFileName =
|
|
95
|
+
path.basename(destPath).replace(/\.[^.]+$/, "") +
|
|
96
|
+
"." +
|
|
97
|
+
timestamp +
|
|
98
|
+
".bak";
|
|
81
99
|
const backupPath = path.join(backupDir, backupFileName);
|
|
82
100
|
|
|
83
101
|
fs.mkdirSync(backupDir, { recursive: true });
|
|
@@ -96,7 +114,7 @@ function copySingleFile(srcPath, destPath, mode, backupDir, displayName) {
|
|
|
96
114
|
* @param {string} filePath - File path
|
|
97
115
|
*/
|
|
98
116
|
function setExecutablePermission(filePath) {
|
|
99
|
-
if (filePath.endsWith(
|
|
117
|
+
if (filePath.endsWith(".sh") || filePath.endsWith(".cjs")) {
|
|
100
118
|
try {
|
|
101
119
|
fs.chmodSync(filePath, 0o755);
|
|
102
120
|
} catch (e) {
|
|
@@ -112,13 +130,13 @@ function setExecutablePermission(filePath) {
|
|
|
112
130
|
const commands = {
|
|
113
131
|
// -------------------------------------------------------------------------
|
|
114
132
|
init: (...args) => {
|
|
115
|
-
const isInteractive = args.includes(
|
|
133
|
+
const isInteractive = args.includes("--interactive") || args.includes("-i");
|
|
116
134
|
|
|
117
135
|
if (isInteractive) {
|
|
118
|
-
return commands[
|
|
136
|
+
return commands["init:interactive"]();
|
|
119
137
|
}
|
|
120
138
|
|
|
121
|
-
console.log(
|
|
139
|
+
console.log("🚀 Initializing Sumulige Claude...");
|
|
122
140
|
|
|
123
141
|
// Create config directory
|
|
124
142
|
ensureDir(CONFIG_DIR);
|
|
@@ -126,36 +144,38 @@ const commands = {
|
|
|
126
144
|
// Create config file
|
|
127
145
|
if (!fs.existsSync(CONFIG_FILE)) {
|
|
128
146
|
saveConfig(loadConfig());
|
|
129
|
-
console.log(
|
|
147
|
+
console.log("✅ Created config:", CONFIG_FILE);
|
|
130
148
|
} else {
|
|
131
|
-
console.log(
|
|
149
|
+
console.log("ℹ️ Config already exists:", CONFIG_FILE);
|
|
132
150
|
}
|
|
133
151
|
|
|
134
152
|
// Create skills directory
|
|
135
153
|
ensureDir(SKILLS_DIR);
|
|
136
|
-
console.log(
|
|
154
|
+
console.log("✅ Created skills directory:", SKILLS_DIR);
|
|
137
155
|
|
|
138
156
|
// Install openskills if not installed
|
|
139
157
|
try {
|
|
140
|
-
execSync(
|
|
141
|
-
console.log(
|
|
158
|
+
execSync("openskills --version", { stdio: "ignore" });
|
|
159
|
+
console.log("✅ OpenSkills already installed");
|
|
142
160
|
} catch {
|
|
143
|
-
console.log(
|
|
161
|
+
console.log("📦 Installing OpenSkills...");
|
|
144
162
|
try {
|
|
145
|
-
execSync(
|
|
146
|
-
console.log(
|
|
163
|
+
execSync("npm i -g openskills", { stdio: "inherit" });
|
|
164
|
+
console.log("✅ OpenSkills installed");
|
|
147
165
|
} catch (e) {
|
|
148
|
-
console.log(
|
|
166
|
+
console.log(
|
|
167
|
+
"⚠️ Failed to install OpenSkills. Run: npm i -g openskills",
|
|
168
|
+
);
|
|
149
169
|
}
|
|
150
170
|
}
|
|
151
171
|
|
|
152
|
-
console.log(
|
|
153
|
-
console.log(
|
|
154
|
-
console.log(
|
|
155
|
-
console.log(
|
|
156
|
-
console.log(
|
|
157
|
-
console.log(
|
|
158
|
-
console.log(
|
|
172
|
+
console.log("");
|
|
173
|
+
console.log("🎉 Sumulige Claude initialized!");
|
|
174
|
+
console.log("");
|
|
175
|
+
console.log("Next steps:");
|
|
176
|
+
console.log(" smc sync # Sync to current project");
|
|
177
|
+
console.log(" smc skills:official # View available skills");
|
|
178
|
+
console.log(" smc init --interactive # Interactive setup");
|
|
159
179
|
},
|
|
160
180
|
|
|
161
181
|
// -------------------------------------------------------------------------
|
|
@@ -170,155 +190,160 @@ const commands = {
|
|
|
170
190
|
},
|
|
171
191
|
|
|
172
192
|
// -------------------------------------------------------------------------
|
|
173
|
-
|
|
174
|
-
const readline = require(
|
|
193
|
+
"init:interactive": () => {
|
|
194
|
+
const readline = require("readline");
|
|
175
195
|
const COLORS = {
|
|
176
|
-
reset:
|
|
177
|
-
green:
|
|
178
|
-
blue:
|
|
179
|
-
yellow:
|
|
180
|
-
gray:
|
|
181
|
-
cyan:
|
|
196
|
+
reset: "\x1b[0m",
|
|
197
|
+
green: "\x1b[32m",
|
|
198
|
+
blue: "\x1b[34m",
|
|
199
|
+
yellow: "\x1b[33m",
|
|
200
|
+
gray: "\x1b[90m",
|
|
201
|
+
cyan: "\x1b[36m",
|
|
182
202
|
};
|
|
183
203
|
|
|
184
|
-
const log = (msg, color =
|
|
204
|
+
const log = (msg, color = "reset") => {
|
|
185
205
|
console.log(`${COLORS[color]}${msg}${COLORS.reset}`);
|
|
186
206
|
};
|
|
187
207
|
|
|
188
208
|
const rl = readline.createInterface({
|
|
189
209
|
input: process.stdin,
|
|
190
|
-
output: process.stdout
|
|
210
|
+
output: process.stdout,
|
|
191
211
|
});
|
|
192
212
|
|
|
193
213
|
const question = (prompt) => {
|
|
194
|
-
return new Promise(resolve => {
|
|
214
|
+
return new Promise((resolve) => {
|
|
195
215
|
rl.question(prompt, resolve);
|
|
196
216
|
});
|
|
197
217
|
};
|
|
198
218
|
|
|
199
219
|
(async () => {
|
|
200
|
-
log(
|
|
201
|
-
log(
|
|
202
|
-
log(
|
|
203
|
-
log(
|
|
220
|
+
log("", "gray");
|
|
221
|
+
log("🎯 SMC Interactive Setup", "blue");
|
|
222
|
+
log("=====================================", "gray");
|
|
223
|
+
log("", "gray");
|
|
204
224
|
|
|
205
225
|
// Step 1: Create config
|
|
206
|
-
log(
|
|
226
|
+
log("Step 1: Configuration", "cyan");
|
|
207
227
|
ensureDir(CONFIG_DIR);
|
|
208
228
|
if (!fs.existsSync(CONFIG_FILE)) {
|
|
209
229
|
saveConfig(loadConfig());
|
|
210
|
-
log(
|
|
230
|
+
log("✅ Created config", "green");
|
|
211
231
|
} else {
|
|
212
|
-
log(
|
|
232
|
+
log("✅ Config exists", "green");
|
|
213
233
|
}
|
|
214
|
-
log(
|
|
234
|
+
log("", "gray");
|
|
215
235
|
|
|
216
236
|
// Step 2: Install OpenSkills
|
|
217
|
-
log(
|
|
237
|
+
log("Step 2: OpenSkills", "cyan");
|
|
218
238
|
try {
|
|
219
|
-
execSync(
|
|
220
|
-
log(
|
|
239
|
+
execSync("openskills --version", { stdio: "ignore" });
|
|
240
|
+
log("✅ OpenSkills already installed", "green");
|
|
221
241
|
} catch {
|
|
222
|
-
log(
|
|
242
|
+
log("Installing OpenSkills...", "gray");
|
|
223
243
|
try {
|
|
224
|
-
execSync(
|
|
225
|
-
log(
|
|
244
|
+
execSync("npm i -g openskills", { stdio: "pipe" });
|
|
245
|
+
log("✅ OpenSkills installed", "green");
|
|
226
246
|
} catch (e) {
|
|
227
|
-
log(
|
|
247
|
+
log("⚠️ Failed to install OpenSkills", "yellow");
|
|
228
248
|
}
|
|
229
249
|
}
|
|
230
|
-
log(
|
|
250
|
+
log("", "gray");
|
|
231
251
|
|
|
232
252
|
// Step 3: Select skills to install
|
|
233
|
-
log(
|
|
234
|
-
log(
|
|
235
|
-
log(
|
|
236
|
-
log(
|
|
237
|
-
log(
|
|
238
|
-
log(
|
|
239
|
-
log(
|
|
240
|
-
log(
|
|
241
|
-
log(
|
|
242
|
-
log(
|
|
243
|
-
|
|
244
|
-
const skillChoice = await question(
|
|
253
|
+
log("Step 3: Choose Skills to Install", "cyan");
|
|
254
|
+
log("", "gray");
|
|
255
|
+
log("Available skill groups:", "gray");
|
|
256
|
+
log(" 1. 📄 Documents (docx, pdf, pptx, xlsx)", "gray");
|
|
257
|
+
log(" 2. 🎨 Creative (frontend-design, algorithmic-art)", "gray");
|
|
258
|
+
log(" 3. 🛠️ Development (mcp-builder, webapp-testing)", "gray");
|
|
259
|
+
log(" 4. 📋 Workflow (doc-coauthoring, internal-comms)", "gray");
|
|
260
|
+
log(" 5. ✨ All recommended skills", "gray");
|
|
261
|
+
log(" 6. ⏭️ Skip skill installation", "gray");
|
|
262
|
+
log("", "gray");
|
|
263
|
+
|
|
264
|
+
const skillChoice = await question("Select (1-6) [default: 6]: ");
|
|
245
265
|
|
|
246
266
|
const installSkills = async (source) => {
|
|
247
267
|
try {
|
|
248
|
-
execSync(`openskills install ${source} -y`, { stdio:
|
|
249
|
-
execSync(
|
|
250
|
-
log(
|
|
268
|
+
execSync(`openskills install ${source} -y`, { stdio: "pipe" });
|
|
269
|
+
execSync("openskills sync -y", { stdio: "pipe" });
|
|
270
|
+
log("✅ Skills installed", "green");
|
|
251
271
|
} catch (e) {
|
|
252
|
-
log(
|
|
272
|
+
log("⚠️ Skill installation failed", "yellow");
|
|
253
273
|
}
|
|
254
274
|
};
|
|
255
275
|
|
|
256
276
|
switch (skillChoice.trim()) {
|
|
257
|
-
case
|
|
258
|
-
log(
|
|
259
|
-
await installSkills(
|
|
277
|
+
case "1":
|
|
278
|
+
log("Installing document skills...", "gray");
|
|
279
|
+
await installSkills("anthropics/skills");
|
|
260
280
|
break;
|
|
261
|
-
case
|
|
262
|
-
log(
|
|
263
|
-
await installSkills(
|
|
281
|
+
case "2":
|
|
282
|
+
log("Installing creative skills...", "gray");
|
|
283
|
+
await installSkills("anthropics/skills");
|
|
264
284
|
break;
|
|
265
|
-
case
|
|
266
|
-
log(
|
|
267
|
-
await installSkills(
|
|
285
|
+
case "3":
|
|
286
|
+
log("Installing development skills...", "gray");
|
|
287
|
+
await installSkills("anthropics/skills");
|
|
268
288
|
break;
|
|
269
|
-
case
|
|
270
|
-
log(
|
|
271
|
-
await installSkills(
|
|
289
|
+
case "4":
|
|
290
|
+
log("Installing workflow skills...", "gray");
|
|
291
|
+
await installSkills("anthropics/skills");
|
|
272
292
|
break;
|
|
273
|
-
case
|
|
274
|
-
log(
|
|
275
|
-
await installSkills(
|
|
293
|
+
case "5":
|
|
294
|
+
log("Installing all recommended skills...", "gray");
|
|
295
|
+
await installSkills("anthropics/skills");
|
|
276
296
|
break;
|
|
277
297
|
default:
|
|
278
|
-
log(
|
|
298
|
+
log("⏭️ Skipped skill installation", "yellow");
|
|
279
299
|
break;
|
|
280
300
|
}
|
|
281
|
-
log(
|
|
301
|
+
log("", "gray");
|
|
282
302
|
|
|
283
303
|
// Step 4: Sync to current project
|
|
284
|
-
log(
|
|
285
|
-
const shouldSync = await question(
|
|
286
|
-
|
|
304
|
+
log("Step 4: Sync to Current Project", "cyan");
|
|
305
|
+
const shouldSync = await question(
|
|
306
|
+
"Sync .claude/ to current directory? [Y/n]: ",
|
|
307
|
+
);
|
|
308
|
+
if (shouldSync.toLowerCase() !== "n") {
|
|
287
309
|
try {
|
|
288
|
-
execSync(
|
|
310
|
+
execSync("smc sync", { stdio: "inherit" });
|
|
289
311
|
} catch (e) {
|
|
290
|
-
log(
|
|
312
|
+
log(
|
|
313
|
+
"⚠️ Sync failed (this is normal if not in a project directory)",
|
|
314
|
+
"yellow",
|
|
315
|
+
);
|
|
291
316
|
}
|
|
292
317
|
} else {
|
|
293
|
-
log(
|
|
318
|
+
log("⏭️ Skipped sync", "yellow");
|
|
294
319
|
}
|
|
295
|
-
log(
|
|
320
|
+
log("", "gray");
|
|
296
321
|
|
|
297
322
|
rl.close();
|
|
298
323
|
|
|
299
|
-
log(
|
|
300
|
-
log(
|
|
301
|
-
log(
|
|
302
|
-
log(
|
|
303
|
-
log(
|
|
304
|
-
log(
|
|
305
|
-
log(
|
|
306
|
-
log(
|
|
324
|
+
log("=====================================", "gray");
|
|
325
|
+
log("🎉 Setup complete!", "green");
|
|
326
|
+
log("", "gray");
|
|
327
|
+
log("Useful commands:", "gray");
|
|
328
|
+
log(" smc status Show configuration status", "gray");
|
|
329
|
+
log(" smc skills:official List available skills", "gray");
|
|
330
|
+
log(" smc doctor Check system health", "gray");
|
|
331
|
+
log("", "gray");
|
|
307
332
|
})();
|
|
308
333
|
},
|
|
309
334
|
|
|
310
335
|
// -------------------------------------------------------------------------
|
|
311
336
|
sync: async (...args) => {
|
|
312
|
-
const forceCheckUpdate = args.includes(
|
|
337
|
+
const forceCheckUpdate = args.includes("--check-update");
|
|
313
338
|
|
|
314
|
-
console.log(
|
|
315
|
-
console.log(
|
|
339
|
+
console.log("🔄 Syncing Sumulige Claude to current project...");
|
|
340
|
+
console.log("");
|
|
316
341
|
|
|
317
342
|
const projectDir = process.cwd();
|
|
318
|
-
const projectConfigDir = path.join(projectDir,
|
|
319
|
-
const agentsFile = path.join(projectConfigDir,
|
|
320
|
-
const readmeFile = path.join(projectConfigDir,
|
|
321
|
-
const templateReadme = path.join(TEMPLATE_DIR,
|
|
343
|
+
const projectConfigDir = path.join(projectDir, ".claude");
|
|
344
|
+
const agentsFile = path.join(projectConfigDir, "AGENTS.md");
|
|
345
|
+
const readmeFile = path.join(projectConfigDir, "README.md");
|
|
346
|
+
const templateReadme = path.join(TEMPLATE_DIR, ".claude", "README.md");
|
|
322
347
|
|
|
323
348
|
// Create .claude directory
|
|
324
349
|
ensureDir(projectConfigDir);
|
|
@@ -327,13 +352,13 @@ const commands = {
|
|
|
327
352
|
const result = runMigrations(projectDir);
|
|
328
353
|
if (result.migrations.length > 0) {
|
|
329
354
|
console.log(`📦 Migrating project template → v${TEMPLATE_VERSION}`);
|
|
330
|
-
result.migrations.forEach(m => {
|
|
355
|
+
result.migrations.forEach((m) => {
|
|
331
356
|
console.log(` ✅ ${m.description}`);
|
|
332
357
|
});
|
|
333
|
-
console.log(
|
|
358
|
+
console.log("");
|
|
334
359
|
}
|
|
335
360
|
|
|
336
|
-
console.log(
|
|
361
|
+
console.log("✅ Created .claude directory");
|
|
337
362
|
|
|
338
363
|
// Sync config
|
|
339
364
|
const config = loadConfig();
|
|
@@ -341,17 +366,19 @@ const commands = {
|
|
|
341
366
|
// Generate AGENTS.md
|
|
342
367
|
const agentsMd = generateAgentsMd(config);
|
|
343
368
|
fs.writeFileSync(agentsFile, agentsMd);
|
|
344
|
-
console.log(
|
|
369
|
+
console.log("✅ Created AGENTS.md");
|
|
345
370
|
|
|
346
371
|
// Silently sync README.md if template updated
|
|
347
372
|
if (fs.existsSync(templateReadme)) {
|
|
348
|
-
const templateContent = fs.readFileSync(templateReadme,
|
|
373
|
+
const templateContent = fs.readFileSync(templateReadme, "utf-8");
|
|
349
374
|
let needsUpdate = true;
|
|
350
375
|
|
|
351
376
|
if (fs.existsSync(readmeFile)) {
|
|
352
|
-
const existingContent = fs.readFileSync(readmeFile,
|
|
353
|
-
const templateVersion =
|
|
354
|
-
|
|
377
|
+
const existingContent = fs.readFileSync(readmeFile, "utf-8");
|
|
378
|
+
const templateVersion =
|
|
379
|
+
templateContent.match(/@version:\s*(\d+\.\d+\.\d+)/)?.[1] || "0.0.0";
|
|
380
|
+
const existingVersion =
|
|
381
|
+
existingContent.match(/@version:\s*(\d+\.\d+\.\d+)/)?.[1] || "0.0.0";
|
|
355
382
|
needsUpdate = templateVersion !== existingVersion;
|
|
356
383
|
}
|
|
357
384
|
|
|
@@ -361,8 +388,8 @@ const commands = {
|
|
|
361
388
|
}
|
|
362
389
|
|
|
363
390
|
// Sync todos directory structure
|
|
364
|
-
const todosTemplateDir = path.join(TEMPLATE_DIR,
|
|
365
|
-
const todosProjectDir = path.join(projectDir,
|
|
391
|
+
const todosTemplateDir = path.join(TEMPLATE_DIR, "development", "todos");
|
|
392
|
+
const todosProjectDir = path.join(projectDir, "development", "todos");
|
|
366
393
|
|
|
367
394
|
if (fs.existsSync(todosTemplateDir)) {
|
|
368
395
|
copyRecursive(todosTemplateDir, todosProjectDir);
|
|
@@ -370,14 +397,14 @@ const commands = {
|
|
|
370
397
|
|
|
371
398
|
// Sync skills
|
|
372
399
|
try {
|
|
373
|
-
execSync(
|
|
374
|
-
console.log(
|
|
400
|
+
execSync("openskills sync -y", { stdio: "pipe" });
|
|
401
|
+
console.log("✅ Synced skills");
|
|
375
402
|
} catch (e) {
|
|
376
|
-
console.log(
|
|
403
|
+
console.log("⚠️ Failed to sync skills");
|
|
377
404
|
}
|
|
378
405
|
|
|
379
|
-
console.log(
|
|
380
|
-
console.log(
|
|
406
|
+
console.log("");
|
|
407
|
+
console.log("✅ Sync complete!");
|
|
381
408
|
|
|
382
409
|
// Check for updates (sync is already a network operation)
|
|
383
410
|
await checkUpdate({ force: forceCheckUpdate, silent: false });
|
|
@@ -385,168 +412,211 @@ const commands = {
|
|
|
385
412
|
|
|
386
413
|
// -------------------------------------------------------------------------
|
|
387
414
|
migrate: () => {
|
|
388
|
-
console.log(
|
|
389
|
-
console.log(
|
|
415
|
+
console.log("🔧 Migrating project to latest format...");
|
|
416
|
+
console.log("");
|
|
390
417
|
|
|
391
418
|
const projectDir = process.cwd();
|
|
392
|
-
const settingsFile = path.join(projectDir,
|
|
419
|
+
const settingsFile = path.join(projectDir, ".claude", "settings.json");
|
|
393
420
|
|
|
394
421
|
if (!fs.existsSync(settingsFile)) {
|
|
395
|
-
console.log(
|
|
396
|
-
console.log(
|
|
422
|
+
console.log("⚠️ No settings.json found in .claude/");
|
|
423
|
+
console.log(" Run: smc template");
|
|
397
424
|
return;
|
|
398
425
|
}
|
|
399
426
|
|
|
400
427
|
let settings;
|
|
401
428
|
try {
|
|
402
|
-
settings = JSON.parse(fs.readFileSync(settingsFile,
|
|
429
|
+
settings = JSON.parse(fs.readFileSync(settingsFile, "utf-8"));
|
|
403
430
|
} catch (e) {
|
|
404
|
-
console.log(
|
|
431
|
+
console.log("❌ Invalid JSON in settings.json");
|
|
405
432
|
return;
|
|
406
433
|
}
|
|
407
434
|
|
|
408
435
|
// 检测旧格式
|
|
409
|
-
const isOldFormat =
|
|
436
|
+
const isOldFormat =
|
|
437
|
+
settings.matcher ||
|
|
438
|
+
(settings.hooks && typeof settings.hooks === "object");
|
|
410
439
|
|
|
411
440
|
if (!isOldFormat) {
|
|
412
|
-
console.log(
|
|
441
|
+
console.log("✅ Already using latest format");
|
|
413
442
|
return;
|
|
414
443
|
}
|
|
415
444
|
|
|
416
445
|
// 读取新模板
|
|
417
|
-
const templateSettings = path.join(
|
|
446
|
+
const templateSettings = path.join(
|
|
447
|
+
TEMPLATE_DIR,
|
|
448
|
+
".claude",
|
|
449
|
+
"settings.json",
|
|
450
|
+
);
|
|
418
451
|
if (!fs.existsSync(templateSettings)) {
|
|
419
|
-
console.log(
|
|
452
|
+
console.log("❌ Template settings.json not found");
|
|
420
453
|
return;
|
|
421
454
|
}
|
|
422
455
|
|
|
423
|
-
const newSettings = fs.readFileSync(templateSettings,
|
|
456
|
+
const newSettings = fs.readFileSync(templateSettings, "utf-8");
|
|
424
457
|
fs.writeFileSync(settingsFile, newSettings);
|
|
425
458
|
|
|
426
|
-
console.log(
|
|
427
|
-
console.log(
|
|
428
|
-
console.log(
|
|
459
|
+
console.log("✅ Migrated settings.json to latest format");
|
|
460
|
+
console.log("");
|
|
461
|
+
console.log("Changes:");
|
|
429
462
|
console.log(' - Old format: {"matcher": "...", "hooks": [...]}');
|
|
430
|
-
console.log(
|
|
431
|
-
|
|
432
|
-
|
|
463
|
+
console.log(
|
|
464
|
+
' - New format: {"UserPromptSubmit": [...], "PostToolUse": [...]}',
|
|
465
|
+
);
|
|
466
|
+
console.log("");
|
|
467
|
+
console.log("ℹ️ Hooks will now work correctly!");
|
|
433
468
|
},
|
|
434
469
|
|
|
435
470
|
// -------------------------------------------------------------------------
|
|
436
471
|
agent: (task) => {
|
|
437
472
|
if (!task) {
|
|
438
|
-
console.log(
|
|
439
|
-
console.log(
|
|
473
|
+
console.log("Usage: sumulige-claude agent <task>");
|
|
474
|
+
console.log("");
|
|
440
475
|
console.log('Example: sumulige-claude agent "Build a React dashboard"');
|
|
441
476
|
return;
|
|
442
477
|
}
|
|
443
478
|
|
|
444
479
|
const config = loadConfig();
|
|
445
|
-
console.log(
|
|
446
|
-
console.log(
|
|
447
|
-
console.log(
|
|
448
|
-
console.log(
|
|
449
|
-
console.log(
|
|
480
|
+
console.log("🤖 Starting Agent Orchestration...");
|
|
481
|
+
console.log("");
|
|
482
|
+
console.log("Task:", task);
|
|
483
|
+
console.log("");
|
|
484
|
+
console.log("Available Agents:");
|
|
450
485
|
Object.entries(config.agents).forEach(([name, agent]) => {
|
|
451
486
|
const model = agent.model || config.model;
|
|
452
487
|
console.log(` - ${name}: ${model} (${agent.role})`);
|
|
453
488
|
});
|
|
454
|
-
console.log(
|
|
455
|
-
console.log(
|
|
489
|
+
console.log("");
|
|
490
|
+
console.log(
|
|
491
|
+
"💡 In Claude Code, use /skill <name> to invoke specific agent capabilities",
|
|
492
|
+
);
|
|
456
493
|
},
|
|
457
494
|
|
|
458
495
|
// -------------------------------------------------------------------------
|
|
459
496
|
status: () => {
|
|
460
497
|
const config = loadConfig();
|
|
461
|
-
console.log(
|
|
462
|
-
console.log(
|
|
463
|
-
console.log(
|
|
464
|
-
console.log(
|
|
465
|
-
console.log(
|
|
498
|
+
console.log("📊 Sumulige Claude Status");
|
|
499
|
+
console.log("");
|
|
500
|
+
console.log("Config:", CONFIG_FILE);
|
|
501
|
+
console.log("");
|
|
502
|
+
console.log("Agents:");
|
|
466
503
|
Object.entries(config.agents).forEach(([name, agent]) => {
|
|
467
504
|
const model = agent.model || config.model;
|
|
468
505
|
console.log(` ${name.padEnd(12)} ${model.padEnd(20)} (${agent.role})`);
|
|
469
506
|
});
|
|
470
|
-
console.log(
|
|
471
|
-
console.log(
|
|
472
|
-
console.log(
|
|
473
|
-
console.log(
|
|
474
|
-
|
|
507
|
+
console.log("");
|
|
508
|
+
console.log("Skills:", config.skills.join(", "));
|
|
509
|
+
console.log("");
|
|
510
|
+
console.log(
|
|
511
|
+
"ThinkingLens:",
|
|
512
|
+
config.thinkingLens.enabled ? "✅ Enabled" : "❌ Disabled",
|
|
513
|
+
);
|
|
514
|
+
console.log("");
|
|
475
515
|
|
|
476
516
|
// Show project todos status
|
|
477
517
|
const projectDir = process.cwd();
|
|
478
|
-
const todosIndex = path.join(
|
|
518
|
+
const todosIndex = path.join(
|
|
519
|
+
projectDir,
|
|
520
|
+
"development",
|
|
521
|
+
"todos",
|
|
522
|
+
"INDEX.md",
|
|
523
|
+
);
|
|
479
524
|
|
|
480
525
|
if (fs.existsSync(todosIndex)) {
|
|
481
|
-
const content = fs.readFileSync(todosIndex,
|
|
526
|
+
const content = fs.readFileSync(todosIndex, "utf-8");
|
|
482
527
|
|
|
483
528
|
const totalMatch = content.match(/Total:\s+`([^`]+)`\s+(\d+)%/);
|
|
484
|
-
const p0Match = content.match(
|
|
485
|
-
|
|
486
|
-
|
|
529
|
+
const p0Match = content.match(
|
|
530
|
+
/P0[^`]*`([^`]+)`\s+(\d+)%\s+\((\d+)\/(\d+)\)/,
|
|
531
|
+
);
|
|
532
|
+
const p1Match = content.match(
|
|
533
|
+
/P1[^`]*`([^`]+)`\s+(\d+)%\s+\((\d+)\/(\d+)\)/,
|
|
534
|
+
);
|
|
535
|
+
const p2Match = content.match(
|
|
536
|
+
/P2[^`]*`([^`]+)`\s+(\d+)%\s+\((\d+)\/(\d+)\)/,
|
|
537
|
+
);
|
|
487
538
|
|
|
488
|
-
const activeMatch = content.match(
|
|
489
|
-
|
|
490
|
-
|
|
539
|
+
const activeMatch = content.match(
|
|
540
|
+
/\|\s+🚧 进行中[^|]*\|\s+`active\/`\s+\|\s+(\d+)/,
|
|
541
|
+
);
|
|
542
|
+
const completedMatch = content.match(
|
|
543
|
+
/\|\s+✅ 已完成[^|]*\|\s+`completed\/`\s+\|\s+(\d+)/,
|
|
544
|
+
);
|
|
545
|
+
const backlogMatch = content.match(
|
|
546
|
+
/\|\s+📋 待办[^|]*\|\s+`backlog\/`\s+\|\s+(\d+)/,
|
|
547
|
+
);
|
|
491
548
|
|
|
492
|
-
console.log(
|
|
493
|
-
console.log(
|
|
549
|
+
console.log("📋 Project Tasks:");
|
|
550
|
+
console.log("");
|
|
494
551
|
if (totalMatch) {
|
|
495
552
|
console.log(` Total: ${totalMatch[1]} ${totalMatch[2]}%`);
|
|
496
553
|
}
|
|
497
554
|
if (p0Match) {
|
|
498
|
-
console.log(
|
|
555
|
+
console.log(
|
|
556
|
+
` P0: ${p0Match[1]} ${p0Match[2]}% (${p0Match[3]}/${p0Match[4]})`,
|
|
557
|
+
);
|
|
499
558
|
}
|
|
500
559
|
if (p1Match) {
|
|
501
|
-
console.log(
|
|
560
|
+
console.log(
|
|
561
|
+
` P1: ${p1Match[1]} ${p1Match[2]}% (${p1Match[3]}/${p1Match[4]})`,
|
|
562
|
+
);
|
|
502
563
|
}
|
|
503
564
|
if (p2Match) {
|
|
504
|
-
console.log(
|
|
565
|
+
console.log(
|
|
566
|
+
` P2: ${p2Match[1]} ${p2Match[2]}% (${p2Match[3]}/${p2Match[4]})`,
|
|
567
|
+
);
|
|
505
568
|
}
|
|
506
|
-
console.log(
|
|
569
|
+
console.log("");
|
|
507
570
|
console.log(` 🚧 Active: ${activeMatch ? activeMatch[1] : 0}`);
|
|
508
571
|
console.log(` ✅ Completed: ${completedMatch ? completedMatch[1] : 0}`);
|
|
509
572
|
console.log(` 📋 Backlog: ${backlogMatch ? backlogMatch[1] : 0}`);
|
|
510
|
-
console.log(
|
|
573
|
+
console.log("");
|
|
511
574
|
console.log(` View: cat development/todos/INDEX.md`);
|
|
512
575
|
} else {
|
|
513
|
-
console.log(
|
|
514
|
-
console.log(
|
|
576
|
+
console.log("📋 Project Tasks: (not initialized)");
|
|
577
|
+
console.log(" Run: node .claude/hooks/todo-manager.cjs --force");
|
|
515
578
|
}
|
|
516
579
|
},
|
|
517
580
|
|
|
518
581
|
// -------------------------------------------------------------------------
|
|
519
|
-
|
|
582
|
+
"skill:list": () => {
|
|
520
583
|
try {
|
|
521
|
-
const result = execSync(
|
|
584
|
+
const result = execSync("openskills list", { encoding: "utf-8" });
|
|
522
585
|
console.log(result);
|
|
523
586
|
} catch (e) {
|
|
524
|
-
console.log(
|
|
587
|
+
console.log("⚠️ OpenSkills not installed. Run: npm i -g openskills");
|
|
525
588
|
}
|
|
526
589
|
},
|
|
527
590
|
|
|
528
591
|
// -------------------------------------------------------------------------
|
|
529
|
-
|
|
592
|
+
"skill:create": (skillName) => {
|
|
530
593
|
if (!skillName) {
|
|
531
|
-
console.log(
|
|
532
|
-
console.log(
|
|
533
|
-
console.log(
|
|
534
|
-
console.log(
|
|
535
|
-
console.log(
|
|
536
|
-
console.log(
|
|
594
|
+
console.log("Usage: sumulige-claude skill:create <skill-name>");
|
|
595
|
+
console.log("");
|
|
596
|
+
console.log("Example: sumulige-claude skill:create api-tester");
|
|
597
|
+
console.log("");
|
|
598
|
+
console.log("The skill will be created at:");
|
|
599
|
+
console.log(" .claude/skills/<skill-name>/");
|
|
537
600
|
return;
|
|
538
601
|
}
|
|
539
602
|
|
|
540
603
|
// Validate skill name (kebab-case)
|
|
541
604
|
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(skillName)) {
|
|
542
|
-
console.log(
|
|
605
|
+
console.log(
|
|
606
|
+
"❌ Invalid skill name. Use kebab-case (e.g., api-tester, code-reviewer)",
|
|
607
|
+
);
|
|
543
608
|
return;
|
|
544
609
|
}
|
|
545
610
|
|
|
546
611
|
const projectDir = process.cwd();
|
|
547
|
-
const skillsDir = path.join(projectDir,
|
|
612
|
+
const skillsDir = path.join(projectDir, ".claude", "skills");
|
|
548
613
|
const skillDir = path.join(skillsDir, skillName);
|
|
549
|
-
const templateDir = path.join(
|
|
614
|
+
const templateDir = path.join(
|
|
615
|
+
TEMPLATE_DIR,
|
|
616
|
+
".claude",
|
|
617
|
+
"skills",
|
|
618
|
+
"template",
|
|
619
|
+
);
|
|
550
620
|
|
|
551
621
|
// Check if skill already exists
|
|
552
622
|
if (fs.existsSync(skillDir)) {
|
|
@@ -555,75 +625,80 @@ const commands = {
|
|
|
555
625
|
}
|
|
556
626
|
|
|
557
627
|
console.log(`📝 Creating skill: ${skillName}`);
|
|
558
|
-
console.log(
|
|
628
|
+
console.log("");
|
|
559
629
|
|
|
560
630
|
// Create skill directory structure
|
|
561
|
-
fs.mkdirSync(path.join(skillDir,
|
|
562
|
-
fs.mkdirSync(path.join(skillDir,
|
|
563
|
-
console.log(
|
|
631
|
+
fs.mkdirSync(path.join(skillDir, "templates"), { recursive: true });
|
|
632
|
+
fs.mkdirSync(path.join(skillDir, "examples"), { recursive: true });
|
|
633
|
+
console.log("✅ Created directory structure");
|
|
564
634
|
|
|
565
635
|
// Copy template files
|
|
566
636
|
if (fs.existsSync(templateDir)) {
|
|
567
|
-
const skillTemplate = fs.readFileSync(
|
|
568
|
-
|
|
637
|
+
const skillTemplate = fs.readFileSync(
|
|
638
|
+
path.join(templateDir, "SKILL.md"),
|
|
639
|
+
"utf-8",
|
|
640
|
+
);
|
|
641
|
+
const metadataTemplate = fs.readFileSync(
|
|
642
|
+
path.join(templateDir, "metadata.yaml"),
|
|
643
|
+
"utf-8",
|
|
644
|
+
);
|
|
569
645
|
|
|
570
646
|
// Replace placeholders
|
|
571
|
-
const date = new Date().toISOString().split(
|
|
647
|
+
const date = new Date().toISOString().split("T")[0];
|
|
572
648
|
let skillContent = skillTemplate
|
|
573
|
-
.replace(/Skill Name/g, toTitleCase(skillName.replace(/-/g,
|
|
649
|
+
.replace(/Skill Name/g, toTitleCase(skillName.replace(/-/g, " ")))
|
|
574
650
|
.replace(/{current-date}/g, date)
|
|
575
651
|
.replace(/skill-name/g, skillName);
|
|
576
652
|
|
|
577
|
-
let metadataContent = metadataTemplate
|
|
578
|
-
.replace(/skill-name/g, skillName);
|
|
653
|
+
let metadataContent = metadataTemplate.replace(/skill-name/g, skillName);
|
|
579
654
|
|
|
580
|
-
fs.writeFileSync(path.join(skillDir,
|
|
581
|
-
fs.writeFileSync(path.join(skillDir,
|
|
582
|
-
console.log(
|
|
655
|
+
fs.writeFileSync(path.join(skillDir, "SKILL.md"), skillContent);
|
|
656
|
+
fs.writeFileSync(path.join(skillDir, "metadata.yaml"), metadataContent);
|
|
657
|
+
console.log("✅ Created SKILL.md and metadata.yaml");
|
|
583
658
|
}
|
|
584
659
|
|
|
585
660
|
// Create example templates
|
|
586
661
|
fs.writeFileSync(
|
|
587
|
-
path.join(skillDir,
|
|
588
|
-
`# Default Template for ${skillName}\n\nReplace this with your actual template.\n
|
|
662
|
+
path.join(skillDir, "templates", "default.md"),
|
|
663
|
+
`# Default Template for ${skillName}\n\nReplace this with your actual template.\n`,
|
|
589
664
|
);
|
|
590
665
|
fs.writeFileSync(
|
|
591
|
-
path.join(skillDir,
|
|
592
|
-
`# Basic Example for ${skillName}\n\nReplace this with your actual example.\n
|
|
666
|
+
path.join(skillDir, "examples", "basic.md"),
|
|
667
|
+
`# Basic Example for ${skillName}\n\nReplace this with your actual example.\n`,
|
|
593
668
|
);
|
|
594
|
-
console.log(
|
|
669
|
+
console.log("✅ Created templates and examples");
|
|
595
670
|
|
|
596
671
|
// Update RAG index
|
|
597
|
-
const ragDir = path.join(projectDir,
|
|
598
|
-
const ragIndexFile = path.join(ragDir,
|
|
672
|
+
const ragDir = path.join(projectDir, ".claude", "rag");
|
|
673
|
+
const ragIndexFile = path.join(ragDir, "skill-index.json");
|
|
599
674
|
let ragIndex = { skills: [], auto_load: { enabled: true } };
|
|
600
675
|
|
|
601
676
|
ensureDir(ragDir);
|
|
602
677
|
|
|
603
678
|
if (fs.existsSync(ragIndexFile)) {
|
|
604
679
|
try {
|
|
605
|
-
ragIndex = JSON.parse(fs.readFileSync(ragIndexFile,
|
|
606
|
-
} catch (e) {
|
|
680
|
+
ragIndex = JSON.parse(fs.readFileSync(ragIndexFile, "utf-8"));
|
|
681
|
+
} catch (e) {}
|
|
607
682
|
}
|
|
608
683
|
|
|
609
684
|
// Add new skill to index
|
|
610
685
|
const newSkill = {
|
|
611
686
|
name: skillName,
|
|
612
687
|
description: `TODO: Add description for ${skillName}`,
|
|
613
|
-
keywords: [skillName.replace(/-/g,
|
|
614
|
-
path: `.claude/skills/${skillName}/SKILL.md
|
|
688
|
+
keywords: [skillName.replace(/-/g, " ")],
|
|
689
|
+
path: `.claude/skills/${skillName}/SKILL.md`,
|
|
615
690
|
};
|
|
616
691
|
|
|
617
692
|
// Avoid duplicates
|
|
618
|
-
if (!ragIndex.skills.some(s => s.name === skillName)) {
|
|
693
|
+
if (!ragIndex.skills.some((s) => s.name === skillName)) {
|
|
619
694
|
ragIndex.skills.push(newSkill);
|
|
620
695
|
fs.writeFileSync(ragIndexFile, JSON.stringify(ragIndex, null, 2));
|
|
621
|
-
console.log(
|
|
696
|
+
console.log("✅ Updated RAG skill index");
|
|
622
697
|
}
|
|
623
698
|
|
|
624
|
-
console.log(
|
|
625
|
-
console.log(
|
|
626
|
-
console.log(
|
|
699
|
+
console.log("");
|
|
700
|
+
console.log("✅ Skill created successfully!");
|
|
701
|
+
console.log("");
|
|
627
702
|
console.log(`Next steps:`);
|
|
628
703
|
console.log(` 1. Edit .claude/skills/${skillName}/SKILL.md`);
|
|
629
704
|
console.log(` 2. Add your templates and examples`);
|
|
@@ -631,12 +706,12 @@ const commands = {
|
|
|
631
706
|
},
|
|
632
707
|
|
|
633
708
|
// -------------------------------------------------------------------------
|
|
634
|
-
|
|
709
|
+
"skill:check": (skillName) => {
|
|
635
710
|
const projectDir = process.cwd();
|
|
636
|
-
const skillsDir = path.join(projectDir,
|
|
711
|
+
const skillsDir = path.join(projectDir, ".claude", "skills");
|
|
637
712
|
|
|
638
|
-
console.log(
|
|
639
|
-
console.log(
|
|
713
|
+
console.log("🔍 Checking skill dependencies...");
|
|
714
|
+
console.log("");
|
|
640
715
|
|
|
641
716
|
const checkSkill = (name, visited = new Set()) => {
|
|
642
717
|
if (visited.has(name)) {
|
|
@@ -646,7 +721,7 @@ const commands = {
|
|
|
646
721
|
visited.add(name);
|
|
647
722
|
|
|
648
723
|
const skillDir = path.join(skillsDir, name);
|
|
649
|
-
const metadataFile = path.join(skillDir,
|
|
724
|
+
const metadataFile = path.join(skillDir, "metadata.yaml");
|
|
650
725
|
|
|
651
726
|
if (!fs.existsSync(skillDir)) {
|
|
652
727
|
console.log(`❌ Skill "${name}" not found`);
|
|
@@ -661,13 +736,13 @@ const commands = {
|
|
|
661
736
|
// Simple YAML parser (basic key: value format only)
|
|
662
737
|
const parseSimpleYaml = (content) => {
|
|
663
738
|
const result = {};
|
|
664
|
-
content.split(
|
|
739
|
+
content.split("\n").forEach((line) => {
|
|
665
740
|
const match = line.match(/^(\w+):\s*(.*)$/);
|
|
666
741
|
if (match) {
|
|
667
742
|
const value = match[2].trim();
|
|
668
|
-
if (value ===
|
|
743
|
+
if (value === "[]") {
|
|
669
744
|
result[match[1]] = [];
|
|
670
|
-
} else if (value.startsWith(
|
|
745
|
+
} else if (value.startsWith("[")) {
|
|
671
746
|
try {
|
|
672
747
|
result[match[1]] = JSON.parse(value.replace(/'/g, '"'));
|
|
673
748
|
} catch (e) {
|
|
@@ -681,7 +756,7 @@ const commands = {
|
|
|
681
756
|
return result;
|
|
682
757
|
};
|
|
683
758
|
|
|
684
|
-
const metadata = parseSimpleYaml(fs.readFileSync(metadataFile,
|
|
759
|
+
const metadata = parseSimpleYaml(fs.readFileSync(metadataFile, "utf-8"));
|
|
685
760
|
const deps = metadata.dependencies || [];
|
|
686
761
|
|
|
687
762
|
if (deps.length === 0) {
|
|
@@ -690,7 +765,7 @@ const commands = {
|
|
|
690
765
|
}
|
|
691
766
|
|
|
692
767
|
console.log(`📦 ${name} depends on:`);
|
|
693
|
-
deps.forEach(dep => {
|
|
768
|
+
deps.forEach((dep) => {
|
|
694
769
|
const depDir = path.join(skillsDir, dep);
|
|
695
770
|
if (fs.existsSync(depDir)) {
|
|
696
771
|
console.log(` ✅ ${dep}`);
|
|
@@ -706,30 +781,34 @@ const commands = {
|
|
|
706
781
|
} else {
|
|
707
782
|
// Check all skills
|
|
708
783
|
const allSkills = fs.existsSync(skillsDir)
|
|
709
|
-
? fs.readdirSync(skillsDir).filter(f => {
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
784
|
+
? fs.readdirSync(skillsDir).filter((f) => {
|
|
785
|
+
const dir = path.join(skillsDir, f);
|
|
786
|
+
return (
|
|
787
|
+
fs.statSync(dir).isDirectory() &&
|
|
788
|
+
f !== "template" &&
|
|
789
|
+
f !== "examples"
|
|
790
|
+
);
|
|
791
|
+
})
|
|
713
792
|
: [];
|
|
714
793
|
|
|
715
794
|
console.log(`Found ${allSkills.length} skills\n`);
|
|
716
|
-
allSkills.forEach(skill => checkSkill(skill));
|
|
795
|
+
allSkills.forEach((skill) => checkSkill(skill));
|
|
717
796
|
}
|
|
718
797
|
},
|
|
719
798
|
|
|
720
799
|
// -------------------------------------------------------------------------
|
|
721
|
-
|
|
800
|
+
"skill:install": (source) => {
|
|
722
801
|
if (!source) {
|
|
723
|
-
console.log(
|
|
724
|
-
console.log(
|
|
802
|
+
console.log("Usage: sumulige-claude skill:install <source>");
|
|
803
|
+
console.log("Example: sumulige-claude skill:install anthropics/skills");
|
|
725
804
|
return;
|
|
726
805
|
}
|
|
727
806
|
try {
|
|
728
|
-
execSync(`openskills install ${source} -y`, { stdio:
|
|
729
|
-
execSync(
|
|
730
|
-
console.log(
|
|
807
|
+
execSync(`openskills install ${source} -y`, { stdio: "inherit" });
|
|
808
|
+
execSync("openskills sync -y", { stdio: "pipe" });
|
|
809
|
+
console.log("✅ Skill installed and synced");
|
|
731
810
|
} catch (e) {
|
|
732
|
-
console.log(
|
|
811
|
+
console.log("❌ Failed to install skill");
|
|
733
812
|
}
|
|
734
813
|
},
|
|
735
814
|
|
|
@@ -737,142 +816,181 @@ const commands = {
|
|
|
737
816
|
template: (...args) => {
|
|
738
817
|
// Parse arguments
|
|
739
818
|
let targetPath = process.cwd();
|
|
740
|
-
let copyMode = CopyMode.BACKUP;
|
|
819
|
+
let copyMode = CopyMode.BACKUP; // Default: backup before overwrite
|
|
741
820
|
let showHelp = false;
|
|
742
821
|
|
|
743
822
|
for (const arg of args) {
|
|
744
|
-
if (arg ===
|
|
823
|
+
if (arg === "--safe") {
|
|
745
824
|
copyMode = CopyMode.SAFE;
|
|
746
|
-
} else if (arg ===
|
|
825
|
+
} else if (arg === "--force") {
|
|
747
826
|
copyMode = CopyMode.FORCE;
|
|
748
|
-
} else if (arg ===
|
|
827
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
749
828
|
showHelp = true;
|
|
750
|
-
} else if (!arg.startsWith(
|
|
829
|
+
} else if (!arg.startsWith("--")) {
|
|
751
830
|
targetPath = path.resolve(arg);
|
|
752
831
|
}
|
|
753
832
|
}
|
|
754
833
|
|
|
755
834
|
// Show help message
|
|
756
835
|
if (showHelp) {
|
|
757
|
-
console.log(
|
|
758
|
-
console.log(
|
|
759
|
-
console.log(
|
|
760
|
-
console.log(
|
|
761
|
-
console.log(
|
|
762
|
-
console.log(
|
|
763
|
-
console.log(
|
|
764
|
-
console.log(
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
console.log(
|
|
768
|
-
console.log(
|
|
769
|
-
console.log(
|
|
770
|
-
console.log(
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
console.log(
|
|
774
|
-
console.log(
|
|
775
|
-
console.log(
|
|
776
|
-
console.log(
|
|
777
|
-
console.log(
|
|
778
|
-
console.log(
|
|
779
|
-
console.log(
|
|
780
|
-
console.log(
|
|
781
|
-
console.log(
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
console.log(
|
|
785
|
-
console.log(
|
|
836
|
+
console.log("");
|
|
837
|
+
console.log("📋 smc template - Deploy Claude Code project template");
|
|
838
|
+
console.log("");
|
|
839
|
+
console.log("USAGE:");
|
|
840
|
+
console.log(" smc template [path] [options]");
|
|
841
|
+
console.log("");
|
|
842
|
+
console.log("ARGUMENTS:");
|
|
843
|
+
console.log(
|
|
844
|
+
" path Target directory (default: current directory)",
|
|
845
|
+
);
|
|
846
|
+
console.log("");
|
|
847
|
+
console.log("OPTIONS:");
|
|
848
|
+
console.log(" --safe Skip existing files (no overwrite)");
|
|
849
|
+
console.log(
|
|
850
|
+
" --force Overwrite without backup (use for new projects)",
|
|
851
|
+
);
|
|
852
|
+
console.log(" --help, -h Show this help message");
|
|
853
|
+
console.log("");
|
|
854
|
+
console.log("DEFAULT BEHAVIOR:");
|
|
855
|
+
console.log(" Backup existing files before overwriting.");
|
|
856
|
+
console.log(" Backups stored in: .claude/backup/");
|
|
857
|
+
console.log(" Backup format: filename.YYYY-MM-DD.bak");
|
|
858
|
+
console.log("");
|
|
859
|
+
console.log("EXAMPLES:");
|
|
860
|
+
console.log(
|
|
861
|
+
" smc template # Deploy to current dir (with backup)",
|
|
862
|
+
);
|
|
863
|
+
console.log(" smc template ./my-project # Deploy to specific dir");
|
|
864
|
+
console.log(" smc template --safe # Skip existing files");
|
|
865
|
+
console.log(" smc template --force # Overwrite everything");
|
|
866
|
+
console.log("");
|
|
867
|
+
console.log("COMPARISON:");
|
|
868
|
+
console.log(" smc template = Full deployment (overwrites with backup)");
|
|
869
|
+
console.log(" smc sync = Incremental update (only adds missing)");
|
|
870
|
+
console.log("");
|
|
786
871
|
return;
|
|
787
872
|
}
|
|
788
873
|
|
|
789
874
|
const targetDir = targetPath;
|
|
790
|
-
const backupDir = path.join(targetDir,
|
|
875
|
+
const backupDir = path.join(targetDir, ".claude", "backup");
|
|
791
876
|
const stats = { copied: 0, skipped: 0, backedup: 0, backups: [] };
|
|
792
877
|
|
|
793
|
-
console.log(
|
|
794
|
-
console.log(
|
|
795
|
-
console.log(
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
878
|
+
console.log("🚀 Initializing Claude Code project template...");
|
|
879
|
+
console.log(" Target:", targetDir);
|
|
880
|
+
console.log(
|
|
881
|
+
" Mode:",
|
|
882
|
+
copyMode === CopyMode.SAFE
|
|
883
|
+
? "SAFE (skip existing)"
|
|
884
|
+
: copyMode === CopyMode.FORCE
|
|
885
|
+
? "FORCE (overwrite)"
|
|
886
|
+
: "BACKUP (backup before overwrite)",
|
|
887
|
+
);
|
|
888
|
+
console.log("");
|
|
799
889
|
|
|
800
890
|
// Warn if existing .claude directory found
|
|
801
|
-
const existingClaudeDir = path.join(targetDir,
|
|
891
|
+
const existingClaudeDir = path.join(targetDir, ".claude");
|
|
802
892
|
if (fs.existsSync(existingClaudeDir)) {
|
|
803
893
|
if (copyMode === CopyMode.FORCE) {
|
|
804
|
-
console.log(
|
|
805
|
-
|
|
894
|
+
console.log(
|
|
895
|
+
"⚠️ Existing .claude/ directory found -- will be overwritten!",
|
|
896
|
+
);
|
|
897
|
+
console.log("");
|
|
806
898
|
} else if (copyMode === CopyMode.BACKUP) {
|
|
807
|
-
console.log(
|
|
808
|
-
console.log(
|
|
899
|
+
console.log("ℹ️ Existing files will be backed up to: .claude/backup/");
|
|
900
|
+
console.log("");
|
|
809
901
|
}
|
|
810
902
|
}
|
|
811
903
|
|
|
812
904
|
// Check template directory exists
|
|
813
905
|
if (!fs.existsSync(TEMPLATE_DIR)) {
|
|
814
|
-
console.log(
|
|
815
|
-
console.log(
|
|
906
|
+
console.log("❌ Template not found at:", TEMPLATE_DIR);
|
|
907
|
+
console.log(" Please reinstall sumulige-claude");
|
|
816
908
|
process.exit(1);
|
|
817
909
|
}
|
|
818
910
|
|
|
819
911
|
// Create directory structure
|
|
820
|
-
console.log(
|
|
912
|
+
console.log("📁 Creating directory structure...");
|
|
821
913
|
const dirs = [
|
|
822
|
-
path.join(targetDir,
|
|
823
|
-
path.join(targetDir,
|
|
824
|
-
path.join(targetDir,
|
|
825
|
-
path.join(targetDir,
|
|
826
|
-
path.join(targetDir,
|
|
827
|
-
path.join(targetDir,
|
|
828
|
-
backupDir
|
|
914
|
+
path.join(targetDir, ".claude"),
|
|
915
|
+
path.join(targetDir, "prompts"),
|
|
916
|
+
path.join(targetDir, "development/todos/active"),
|
|
917
|
+
path.join(targetDir, "development/todos/completed"),
|
|
918
|
+
path.join(targetDir, "development/todos/backlog"),
|
|
919
|
+
path.join(targetDir, "development/todos/archived"),
|
|
920
|
+
backupDir,
|
|
829
921
|
];
|
|
830
922
|
|
|
831
923
|
dirs.forEach(ensureDir);
|
|
832
|
-
console.log(
|
|
833
|
-
console.log(
|
|
924
|
+
console.log(" ✅ Directories created");
|
|
925
|
+
console.log("");
|
|
834
926
|
|
|
835
927
|
// Copy files
|
|
836
|
-
console.log(
|
|
928
|
+
console.log("📋 Copying template files...");
|
|
837
929
|
|
|
838
|
-
const claudeTemplateDir = path.join(TEMPLATE_DIR,
|
|
839
|
-
const targetClaudeDir = path.join(targetDir,
|
|
930
|
+
const claudeTemplateDir = path.join(TEMPLATE_DIR, ".claude");
|
|
931
|
+
const targetClaudeDir = path.join(targetDir, ".claude");
|
|
840
932
|
|
|
841
933
|
// Files to copy
|
|
842
934
|
const filesToCopy = [
|
|
843
|
-
{
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
935
|
+
{
|
|
936
|
+
src: "CLAUDE-template.md",
|
|
937
|
+
dest: "CLAUDE.md",
|
|
938
|
+
name: ".claude/CLAUDE.md",
|
|
939
|
+
},
|
|
940
|
+
{ src: "README.md", dest: "README.md", name: ".claude/README.md" },
|
|
941
|
+
{
|
|
942
|
+
src: "settings.json",
|
|
943
|
+
dest: "settings.json",
|
|
944
|
+
name: ".claude/settings.json",
|
|
945
|
+
},
|
|
946
|
+
{
|
|
947
|
+
src: "boris-optimizations.md",
|
|
948
|
+
dest: "boris-optimizations.md",
|
|
949
|
+
name: ".claude/boris-optimizations.md",
|
|
950
|
+
},
|
|
847
951
|
];
|
|
848
952
|
|
|
849
953
|
filesToCopy.forEach(({ src, dest, name }) => {
|
|
850
954
|
const srcPath = path.join(claudeTemplateDir, src);
|
|
851
955
|
const destPath = path.join(targetClaudeDir, dest);
|
|
852
956
|
if (fs.existsSync(srcPath)) {
|
|
853
|
-
const action = copySingleFile(
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
957
|
+
const action = copySingleFile(
|
|
958
|
+
srcPath,
|
|
959
|
+
destPath,
|
|
960
|
+
copyMode,
|
|
961
|
+
backupDir,
|
|
962
|
+
name,
|
|
963
|
+
);
|
|
964
|
+
if (action === "copied") stats.copied++;
|
|
965
|
+
else if (action === "skipped") stats.skipped++;
|
|
966
|
+
else if (action === "backedup") {
|
|
967
|
+
stats.copied++;
|
|
968
|
+
stats.backedup++;
|
|
969
|
+
}
|
|
857
970
|
}
|
|
858
971
|
});
|
|
859
972
|
|
|
860
973
|
// Directories to copy recursively
|
|
861
974
|
const dirsToCopy = [
|
|
862
|
-
{ src:
|
|
863
|
-
{ src:
|
|
864
|
-
{ src:
|
|
865
|
-
{ src:
|
|
866
|
-
{ src:
|
|
867
|
-
{ src:
|
|
975
|
+
{ src: "hooks", name: ".claude/hooks/" },
|
|
976
|
+
{ src: "commands", name: ".claude/commands/" },
|
|
977
|
+
{ src: "skills", name: ".claude/skills/" },
|
|
978
|
+
{ src: "templates", name: ".claude/templates/" },
|
|
979
|
+
{ src: "thinking-routes", name: ".claude/thinking-routes/" },
|
|
980
|
+
{ src: "rag", name: ".claude/rag/" },
|
|
868
981
|
];
|
|
869
982
|
|
|
870
983
|
dirsToCopy.forEach(({ src, name }) => {
|
|
871
984
|
const srcPath = path.join(claudeTemplateDir, src);
|
|
872
985
|
if (fs.existsSync(srcPath)) {
|
|
873
|
-
const result = copyRecursive(
|
|
986
|
+
const result = copyRecursive(
|
|
987
|
+
srcPath,
|
|
988
|
+
path.join(targetClaudeDir, src),
|
|
989
|
+
copyMode,
|
|
990
|
+
backupDir,
|
|
991
|
+
);
|
|
874
992
|
const fileCount = result.copied + result.skipped + result.backedup;
|
|
875
|
-
const suffix = result.skipped > 0 ? ` (${result.skipped} skipped)` :
|
|
993
|
+
const suffix = result.skipped > 0 ? ` (${result.skipped} skipped)` : "";
|
|
876
994
|
console.log(` ✅ ${name} (${fileCount} files${suffix})`);
|
|
877
995
|
stats.copied += result.copied;
|
|
878
996
|
stats.skipped += result.skipped;
|
|
@@ -881,11 +999,16 @@ const commands = {
|
|
|
881
999
|
});
|
|
882
1000
|
|
|
883
1001
|
// Copy prompts
|
|
884
|
-
const promptsDir = path.join(TEMPLATE_DIR,
|
|
1002
|
+
const promptsDir = path.join(TEMPLATE_DIR, "prompts");
|
|
885
1003
|
if (fs.existsSync(promptsDir)) {
|
|
886
|
-
const result = copyRecursive(
|
|
1004
|
+
const result = copyRecursive(
|
|
1005
|
+
promptsDir,
|
|
1006
|
+
path.join(targetDir, "prompts"),
|
|
1007
|
+
copyMode,
|
|
1008
|
+
backupDir,
|
|
1009
|
+
);
|
|
887
1010
|
const fileCount = result.copied + result.skipped + result.backedup;
|
|
888
|
-
const suffix = result.skipped > 0 ? ` (${result.skipped} skipped)` :
|
|
1011
|
+
const suffix = result.skipped > 0 ? ` (${result.skipped} skipped)` : "";
|
|
889
1012
|
console.log(` ✅ prompts/ (${fileCount} files${suffix})`);
|
|
890
1013
|
stats.copied += result.copied;
|
|
891
1014
|
stats.skipped += result.skipped;
|
|
@@ -893,11 +1016,16 @@ const commands = {
|
|
|
893
1016
|
}
|
|
894
1017
|
|
|
895
1018
|
// Copy todos
|
|
896
|
-
const todosDir = path.join(TEMPLATE_DIR,
|
|
1019
|
+
const todosDir = path.join(TEMPLATE_DIR, "development", "todos");
|
|
897
1020
|
if (fs.existsSync(todosDir)) {
|
|
898
|
-
const result = copyRecursive(
|
|
1021
|
+
const result = copyRecursive(
|
|
1022
|
+
todosDir,
|
|
1023
|
+
path.join(targetDir, "development", "todos"),
|
|
1024
|
+
copyMode,
|
|
1025
|
+
backupDir,
|
|
1026
|
+
);
|
|
899
1027
|
const fileCount = result.copied + result.skipped + result.backedup;
|
|
900
|
-
const suffix = result.skipped > 0 ? ` (${result.skipped} skipped)` :
|
|
1028
|
+
const suffix = result.skipped > 0 ? ` (${result.skipped} skipped)` : "";
|
|
901
1029
|
console.log(` ✅ development/todos/ (${fileCount} files${suffix})`);
|
|
902
1030
|
stats.copied += result.copied;
|
|
903
1031
|
stats.skipped += result.skipped;
|
|
@@ -905,41 +1033,54 @@ const commands = {
|
|
|
905
1033
|
}
|
|
906
1034
|
|
|
907
1035
|
// Root files
|
|
908
|
-
const rootFiles = [
|
|
909
|
-
|
|
1036
|
+
const rootFiles = [
|
|
1037
|
+
"project-paradigm.md",
|
|
1038
|
+
"thinkinglens-silent.md",
|
|
1039
|
+
"CLAUDE-template.md",
|
|
1040
|
+
];
|
|
1041
|
+
rootFiles.forEach((file) => {
|
|
910
1042
|
const src = path.join(TEMPLATE_DIR, file);
|
|
911
1043
|
const dest = path.join(targetDir, file);
|
|
912
1044
|
if (fs.existsSync(src)) {
|
|
913
1045
|
const action = copySingleFile(src, dest, copyMode, backupDir, file);
|
|
914
|
-
if (action ===
|
|
915
|
-
else if (action ===
|
|
916
|
-
else if (action ===
|
|
1046
|
+
if (action === "copied") stats.copied++;
|
|
1047
|
+
else if (action === "skipped") stats.skipped++;
|
|
1048
|
+
else if (action === "backedup") {
|
|
1049
|
+
stats.copied++;
|
|
1050
|
+
stats.backedup++;
|
|
1051
|
+
}
|
|
917
1052
|
}
|
|
918
1053
|
});
|
|
919
1054
|
|
|
920
1055
|
// Create memory files
|
|
921
|
-
console.log(
|
|
922
|
-
console.log(
|
|
923
|
-
if (!fs.existsSync(path.join(targetClaudeDir,
|
|
924
|
-
fs.writeFileSync(
|
|
925
|
-
|
|
1056
|
+
console.log("");
|
|
1057
|
+
console.log("📝 Creating memory files...");
|
|
1058
|
+
if (!fs.existsSync(path.join(targetClaudeDir, "MEMORY.md"))) {
|
|
1059
|
+
fs.writeFileSync(
|
|
1060
|
+
path.join(targetClaudeDir, "MEMORY.md"),
|
|
1061
|
+
"# Memory\n\n<!-- Project memory updated by AI -->\n",
|
|
1062
|
+
);
|
|
1063
|
+
console.log(" ✅ MEMORY.md");
|
|
926
1064
|
} else {
|
|
927
|
-
console.log(
|
|
1065
|
+
console.log(" ⊝ MEMORY.md (already exists)");
|
|
928
1066
|
}
|
|
929
|
-
if (!fs.existsSync(path.join(targetClaudeDir,
|
|
930
|
-
fs.writeFileSync(
|
|
931
|
-
|
|
1067
|
+
if (!fs.existsSync(path.join(targetClaudeDir, "PROJECT_LOG.md"))) {
|
|
1068
|
+
fs.writeFileSync(
|
|
1069
|
+
path.join(targetClaudeDir, "PROJECT_LOG.md"),
|
|
1070
|
+
"# Project Log\n\n<!-- Build history and decisions -->\n",
|
|
1071
|
+
);
|
|
1072
|
+
console.log(" ✅ PROJECT_LOG.md");
|
|
932
1073
|
} else {
|
|
933
|
-
console.log(
|
|
1074
|
+
console.log(" ⊝ PROJECT_LOG.md (already exists)");
|
|
934
1075
|
}
|
|
935
1076
|
|
|
936
1077
|
// Create ANCHORS.md
|
|
937
|
-
const anchorsPath = path.join(targetClaudeDir,
|
|
1078
|
+
const anchorsPath = path.join(targetClaudeDir, "ANCHORS.md");
|
|
938
1079
|
if (!fs.existsSync(anchorsPath)) {
|
|
939
1080
|
const anchorsContent = `# [Project Name] - Skill Anchors Index
|
|
940
1081
|
|
|
941
1082
|
> This file is auto-maintained by AI as a quick index for the skill system
|
|
942
|
-
> Last updated: ${new Date().toISOString().split(
|
|
1083
|
+
> Last updated: ${new Date().toISOString().split("T")[0]}
|
|
943
1084
|
|
|
944
1085
|
---
|
|
945
1086
|
|
|
@@ -976,19 +1117,19 @@ const commands = {
|
|
|
976
1117
|
|
|
977
1118
|
`;
|
|
978
1119
|
fs.writeFileSync(anchorsPath, anchorsContent);
|
|
979
|
-
console.log(
|
|
1120
|
+
console.log(" ✅ .claude/ANCHORS.md");
|
|
980
1121
|
} else {
|
|
981
|
-
console.log(
|
|
1122
|
+
console.log(" ⊝ .claude/ANCHORS.md (already exists)");
|
|
982
1123
|
}
|
|
983
1124
|
|
|
984
1125
|
// Write version file
|
|
985
|
-
const { setProjectVersion } = require(
|
|
1126
|
+
const { setProjectVersion } = require("./migrations");
|
|
986
1127
|
setProjectVersion(targetDir, TEMPLATE_VERSION);
|
|
987
1128
|
console.log(` ✅ .claude/.version (v${TEMPLATE_VERSION})`);
|
|
988
1129
|
|
|
989
1130
|
// Show summary
|
|
990
|
-
console.log(
|
|
991
|
-
console.log(
|
|
1131
|
+
console.log("");
|
|
1132
|
+
console.log("📊 Summary:");
|
|
992
1133
|
console.log(` ✅ Copied: ${stats.copied} files`);
|
|
993
1134
|
if (stats.skipped > 0) {
|
|
994
1135
|
console.log(` ⊝ Skipped: ${stats.skipped} files (already exist)`);
|
|
@@ -998,62 +1139,68 @@ const commands = {
|
|
|
998
1139
|
}
|
|
999
1140
|
|
|
1000
1141
|
// Initialize Sumulige Claude if installed
|
|
1001
|
-
console.log(
|
|
1002
|
-
console.log(
|
|
1142
|
+
console.log("");
|
|
1143
|
+
console.log("🤖 Initializing Sumulige Claude...");
|
|
1003
1144
|
try {
|
|
1004
|
-
execSync(
|
|
1005
|
-
console.log(
|
|
1145
|
+
execSync("sumulige-claude sync", { cwd: targetDir, stdio: "pipe" });
|
|
1146
|
+
console.log(" ✅ Sumulige Claude synced");
|
|
1006
1147
|
} catch (e) {
|
|
1007
|
-
console.log(
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
console.log('✅ Template initialization complete!');
|
|
1012
|
-
console.log('');
|
|
1013
|
-
console.log('📦 What was included:');
|
|
1014
|
-
console.log(' • AI autonomous memory system (ThinkingLens)');
|
|
1015
|
-
console.log(' • Slash commands (/commit, /test, /review, etc.)');
|
|
1016
|
-
console.log(' • Skills system with templates');
|
|
1017
|
-
console.log(' • RAG dynamic skill index');
|
|
1018
|
-
console.log(' • Hooks for automation');
|
|
1019
|
-
console.log(' • TODO management system v2.0 (R-D-T lifecycle)');
|
|
1020
|
-
console.log('');
|
|
1021
|
-
console.log('Next steps:');
|
|
1022
|
-
console.log(' 1. Edit .claude/CLAUDE.md with your project info');
|
|
1023
|
-
console.log(' 2. Run: claude # Start Claude Code');
|
|
1024
|
-
console.log(' 3. Try: /commit, /test, /review');
|
|
1025
|
-
console.log('');
|
|
1026
|
-
},
|
|
1148
|
+
console.log(
|
|
1149
|
+
" ⚠️ Sumulige Claude not available (run: npm i -g sumulige-claude)",
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1027
1152
|
|
|
1153
|
+
console.log("");
|
|
1154
|
+
console.log("✅ Template initialization complete!");
|
|
1155
|
+
console.log("");
|
|
1156
|
+
console.log("📦 What was included:");
|
|
1157
|
+
console.log(" • AI autonomous memory system (ThinkingLens)");
|
|
1158
|
+
console.log(" • Slash commands (/commit, /test, /review, etc.)");
|
|
1159
|
+
console.log(" • Skills system with templates");
|
|
1160
|
+
console.log(" • RAG dynamic skill index");
|
|
1161
|
+
console.log(" • Hooks for automation");
|
|
1162
|
+
console.log(" • TODO management system v2.0 (R-D-T lifecycle)");
|
|
1163
|
+
console.log("");
|
|
1164
|
+
console.log("Next steps:");
|
|
1165
|
+
console.log(" 1. Edit .claude/CLAUDE.md with your project info");
|
|
1166
|
+
console.log(" 2. Run: claude # Start Claude Code");
|
|
1167
|
+
console.log(" 3. Try: /commit, /test, /review");
|
|
1168
|
+
console.log("");
|
|
1169
|
+
},
|
|
1028
1170
|
|
|
1029
1171
|
// -------------------------------------------------------------------------
|
|
1030
1172
|
kickoff: () => {
|
|
1031
1173
|
const projectDir = process.cwd();
|
|
1032
|
-
const kickoffFile = path.join(projectDir,
|
|
1033
|
-
const hintFile = path.join(projectDir,
|
|
1174
|
+
const kickoffFile = path.join(projectDir, "PROJECT_KICKOFF.md");
|
|
1175
|
+
const hintFile = path.join(projectDir, ".claude", ".kickoff-hint.txt");
|
|
1034
1176
|
|
|
1035
|
-
console.log(
|
|
1036
|
-
console.log(
|
|
1177
|
+
console.log("🚀 Project Kickoff - Manus 风格项目启动");
|
|
1178
|
+
console.log("");
|
|
1037
1179
|
|
|
1038
1180
|
if (fs.existsSync(kickoffFile)) {
|
|
1039
|
-
console.log(
|
|
1040
|
-
console.log(
|
|
1041
|
-
console.log(
|
|
1042
|
-
console.log(
|
|
1043
|
-
console.log(
|
|
1044
|
-
console.log(
|
|
1045
|
-
console.log(
|
|
1181
|
+
console.log("ℹ️ 项目已经完成启动流程");
|
|
1182
|
+
console.log(" 文件:", kickoffFile);
|
|
1183
|
+
console.log("");
|
|
1184
|
+
console.log("如需重新规划,请先删除以下文件:");
|
|
1185
|
+
console.log(" - PROJECT_KICKOFF.md");
|
|
1186
|
+
console.log(" - TASK_PLAN.md");
|
|
1187
|
+
console.log(" - PROJECT_PROPOSAL.md");
|
|
1046
1188
|
return;
|
|
1047
1189
|
}
|
|
1048
1190
|
|
|
1049
1191
|
// Run kickoff hook
|
|
1050
|
-
const kickoffHook = path.join(
|
|
1192
|
+
const kickoffHook = path.join(
|
|
1193
|
+
projectDir,
|
|
1194
|
+
".claude",
|
|
1195
|
+
"hooks",
|
|
1196
|
+
"project-kickoff.cjs",
|
|
1197
|
+
);
|
|
1051
1198
|
if (fs.existsSync(kickoffHook)) {
|
|
1052
1199
|
try {
|
|
1053
1200
|
execSync(`node "${kickoffHook}"`, {
|
|
1054
1201
|
cwd: projectDir,
|
|
1055
1202
|
env: { ...process.env, CLAUDE_PROJECT_DIR: projectDir },
|
|
1056
|
-
stdio:
|
|
1203
|
+
stdio: "inherit",
|
|
1057
1204
|
});
|
|
1058
1205
|
} catch (e) {
|
|
1059
1206
|
// Hook may output and exit, this is normal
|
|
@@ -1061,85 +1208,91 @@ const commands = {
|
|
|
1061
1208
|
|
|
1062
1209
|
// Show hint file if exists
|
|
1063
1210
|
if (fs.existsSync(hintFile)) {
|
|
1064
|
-
const hint = fs.readFileSync(hintFile,
|
|
1211
|
+
const hint = fs.readFileSync(hintFile, "utf-8");
|
|
1065
1212
|
console.log(hint);
|
|
1066
1213
|
}
|
|
1067
1214
|
} else {
|
|
1068
|
-
console.log(
|
|
1069
|
-
console.log(
|
|
1070
|
-
console.log(
|
|
1215
|
+
console.log("⚠️ 启动 Hook 不存在");
|
|
1216
|
+
console.log(" 请先运行: sumulige-claude template");
|
|
1217
|
+
console.log(" 或: sumulige-claude sync");
|
|
1071
1218
|
}
|
|
1072
1219
|
},
|
|
1073
1220
|
|
|
1074
1221
|
// -------------------------------------------------------------------------
|
|
1075
1222
|
ultrathink: () => {
|
|
1076
1223
|
const COLORS = {
|
|
1077
|
-
reset:
|
|
1078
|
-
green:
|
|
1079
|
-
blue:
|
|
1080
|
-
yellow:
|
|
1081
|
-
gray:
|
|
1224
|
+
reset: "\x1b[0m",
|
|
1225
|
+
green: "\x1b[32m",
|
|
1226
|
+
blue: "\x1b[34m",
|
|
1227
|
+
yellow: "\x1b[33m",
|
|
1228
|
+
gray: "\x1b[90m",
|
|
1082
1229
|
};
|
|
1083
1230
|
|
|
1084
|
-
const log = (msg, color =
|
|
1231
|
+
const log = (msg, color = "reset") => {
|
|
1085
1232
|
console.log(`${COLORS[color]}${msg}${COLORS.reset}`);
|
|
1086
1233
|
};
|
|
1087
1234
|
|
|
1088
|
-
log(
|
|
1089
|
-
log(
|
|
1090
|
-
log(
|
|
1091
|
-
log(
|
|
1092
|
-
log(
|
|
1093
|
-
log(
|
|
1094
|
-
log(
|
|
1095
|
-
log(' Mention "ultrathink" or "深度思考" in conversation',
|
|
1096
|
-
log(
|
|
1097
|
-
log(
|
|
1098
|
-
log(
|
|
1235
|
+
log("", "gray");
|
|
1236
|
+
log("🧠 UltraThink Mode", "blue");
|
|
1237
|
+
log("=====================================", "gray");
|
|
1238
|
+
log("", "gray");
|
|
1239
|
+
log("✅ Deep thinking enabled", "green");
|
|
1240
|
+
log("", "gray");
|
|
1241
|
+
log("Usage:", "gray");
|
|
1242
|
+
log(' Mention "ultrathink" or "深度思考" in conversation', "gray");
|
|
1243
|
+
log("", "gray");
|
|
1244
|
+
log("=====================================", "gray");
|
|
1245
|
+
log("", "gray");
|
|
1099
1246
|
},
|
|
1100
1247
|
|
|
1101
1248
|
// -------------------------------------------------------------------------
|
|
1102
|
-
|
|
1103
|
-
const fs = require(
|
|
1104
|
-
const path = require(
|
|
1249
|
+
"skills:official": () => {
|
|
1250
|
+
const fs = require("fs");
|
|
1251
|
+
const path = require("path");
|
|
1105
1252
|
const COLORS = {
|
|
1106
|
-
reset:
|
|
1107
|
-
green:
|
|
1108
|
-
blue:
|
|
1109
|
-
yellow:
|
|
1110
|
-
gray:
|
|
1111
|
-
cyan:
|
|
1112
|
-
magenta:
|
|
1253
|
+
reset: "\x1b[0m",
|
|
1254
|
+
green: "\x1b[32m",
|
|
1255
|
+
blue: "\x1b[34m",
|
|
1256
|
+
yellow: "\x1b[33m",
|
|
1257
|
+
gray: "\x1b[90m",
|
|
1258
|
+
cyan: "\x1b[36m",
|
|
1259
|
+
magenta: "\x1b[35m",
|
|
1113
1260
|
};
|
|
1114
1261
|
|
|
1115
|
-
const log = (msg, color =
|
|
1262
|
+
const log = (msg, color = "reset") => {
|
|
1116
1263
|
console.log(`${COLORS[color]}${msg}${COLORS.reset}`);
|
|
1117
1264
|
};
|
|
1118
1265
|
|
|
1119
|
-
const officialSkillsFile = path.join(
|
|
1266
|
+
const officialSkillsFile = path.join(
|
|
1267
|
+
__dirname,
|
|
1268
|
+
"../config/official-skills.json",
|
|
1269
|
+
);
|
|
1120
1270
|
|
|
1121
1271
|
if (!fs.existsSync(officialSkillsFile)) {
|
|
1122
|
-
log(
|
|
1272
|
+
log("⚠ Official skills registry not found", "yellow");
|
|
1123
1273
|
return;
|
|
1124
1274
|
}
|
|
1125
1275
|
|
|
1126
|
-
const registry = JSON.parse(fs.readFileSync(officialSkillsFile,
|
|
1276
|
+
const registry = JSON.parse(fs.readFileSync(officialSkillsFile, "utf-8"));
|
|
1127
1277
|
|
|
1128
|
-
log(
|
|
1129
|
-
log(
|
|
1130
|
-
log(
|
|
1131
|
-
log(
|
|
1132
|
-
log(`Source: ${registry.source}`,
|
|
1133
|
-
log(`Updated: ${registry.last_updated}`,
|
|
1134
|
-
log(
|
|
1278
|
+
log("", "gray");
|
|
1279
|
+
log("📚 Official Skills (anthropics/skills)", "blue");
|
|
1280
|
+
log("=====================================", "gray");
|
|
1281
|
+
log("", "gray");
|
|
1282
|
+
log(`Source: ${registry.source}`, "gray");
|
|
1283
|
+
log(`Updated: ${registry.last_updated}`, "gray");
|
|
1284
|
+
log("", "gray");
|
|
1135
1285
|
|
|
1136
1286
|
// Check which skills are already installed
|
|
1137
|
-
const skillsDir = path.join(
|
|
1287
|
+
const skillsDir = path.join(
|
|
1288
|
+
process.env.HOME || process.env.USERPROFILE,
|
|
1289
|
+
".claude/skills",
|
|
1290
|
+
);
|
|
1138
1291
|
const installedSkills = fs.existsSync(skillsDir)
|
|
1139
|
-
? fs.readdirSync(skillsDir).filter(f => {
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1292
|
+
? fs.readdirSync(skillsDir).filter((f) => {
|
|
1293
|
+
const dir = path.join(skillsDir, f);
|
|
1294
|
+
return fs.statSync(dir).isDirectory();
|
|
1295
|
+
})
|
|
1143
1296
|
: [];
|
|
1144
1297
|
|
|
1145
1298
|
// Group by category
|
|
@@ -1158,192 +1311,203 @@ const commands = {
|
|
|
1158
1311
|
for (const [catName, cat] of Object.entries(byCategory)) {
|
|
1159
1312
|
if (cat.skills.length === 0) continue;
|
|
1160
1313
|
|
|
1161
|
-
log(`${cat.icon} ${catName}`,
|
|
1162
|
-
log(` ${cat.description}`,
|
|
1163
|
-
log(
|
|
1314
|
+
log(`${cat.icon} ${catName}`, "cyan");
|
|
1315
|
+
log(` ${cat.description}`, "gray");
|
|
1316
|
+
log("", "gray");
|
|
1164
1317
|
|
|
1165
1318
|
for (const skill of cat.skills) {
|
|
1166
|
-
const status = skill.isInstalled ?
|
|
1167
|
-
const rec = skill.recommended ?
|
|
1168
|
-
const color = skill.isInstalled ?
|
|
1319
|
+
const status = skill.isInstalled ? "✓" : " ";
|
|
1320
|
+
const rec = skill.recommended ? " [推荐]" : "";
|
|
1321
|
+
const color = skill.isInstalled ? "green" : "reset";
|
|
1169
1322
|
log(` [${status}] ${skill.name}${rec}`, color);
|
|
1170
|
-
log(` ${skill.description}`,
|
|
1323
|
+
log(` ${skill.description}`, "gray");
|
|
1171
1324
|
}
|
|
1172
|
-
log(
|
|
1325
|
+
log("", "gray");
|
|
1173
1326
|
}
|
|
1174
1327
|
|
|
1175
|
-
log(
|
|
1176
|
-
log(
|
|
1177
|
-
log(
|
|
1178
|
-
log(
|
|
1179
|
-
log(
|
|
1180
|
-
|
|
1328
|
+
log("=====================================", "gray");
|
|
1329
|
+
log("", "gray");
|
|
1330
|
+
log("Commands:", "gray");
|
|
1331
|
+
log(" smc skills:install-official <name> Install a skill", "gray");
|
|
1332
|
+
log(
|
|
1333
|
+
" smc skills:install-all Install all recommended",
|
|
1334
|
+
"gray",
|
|
1335
|
+
);
|
|
1336
|
+
log("", "gray");
|
|
1181
1337
|
},
|
|
1182
1338
|
|
|
1183
1339
|
// -------------------------------------------------------------------------
|
|
1184
|
-
|
|
1185
|
-
const fs = require(
|
|
1186
|
-
const path = require(
|
|
1187
|
-
const { execSync } = require(
|
|
1340
|
+
"skills:install-official": (skillName) => {
|
|
1341
|
+
const fs = require("fs");
|
|
1342
|
+
const path = require("path");
|
|
1343
|
+
const { execSync } = require("child_process");
|
|
1188
1344
|
const COLORS = {
|
|
1189
|
-
reset:
|
|
1190
|
-
green:
|
|
1191
|
-
blue:
|
|
1192
|
-
yellow:
|
|
1193
|
-
gray:
|
|
1194
|
-
red:
|
|
1345
|
+
reset: "\x1b[0m",
|
|
1346
|
+
green: "\x1b[32m",
|
|
1347
|
+
blue: "\x1b[34m",
|
|
1348
|
+
yellow: "\x1b[33m",
|
|
1349
|
+
gray: "\x1b[90m",
|
|
1350
|
+
red: "\x1b[31m",
|
|
1195
1351
|
};
|
|
1196
1352
|
|
|
1197
|
-
const log = (msg, color =
|
|
1353
|
+
const log = (msg, color = "reset") => {
|
|
1198
1354
|
console.log(`${COLORS[color]}${msg}${COLORS.reset}`);
|
|
1199
1355
|
};
|
|
1200
1356
|
|
|
1201
|
-
const officialSkillsFile = path.join(
|
|
1357
|
+
const officialSkillsFile = path.join(
|
|
1358
|
+
__dirname,
|
|
1359
|
+
"../config/official-skills.json",
|
|
1360
|
+
);
|
|
1202
1361
|
|
|
1203
1362
|
if (!fs.existsSync(officialSkillsFile)) {
|
|
1204
|
-
log(
|
|
1363
|
+
log("⚠ Official skills registry not found", "yellow");
|
|
1205
1364
|
return;
|
|
1206
1365
|
}
|
|
1207
1366
|
|
|
1208
|
-
const registry = JSON.parse(fs.readFileSync(officialSkillsFile,
|
|
1367
|
+
const registry = JSON.parse(fs.readFileSync(officialSkillsFile, "utf-8"));
|
|
1209
1368
|
|
|
1210
1369
|
// Check if openskills is installed
|
|
1211
1370
|
try {
|
|
1212
|
-
execSync(
|
|
1371
|
+
execSync("openskills --version", { stdio: "ignore" });
|
|
1213
1372
|
} catch {
|
|
1214
|
-
log(
|
|
1215
|
-
log(
|
|
1216
|
-
log(
|
|
1373
|
+
log("⚠ OpenSkills not installed", "yellow");
|
|
1374
|
+
log("", "gray");
|
|
1375
|
+
log("Installing OpenSkills...", "gray");
|
|
1217
1376
|
try {
|
|
1218
|
-
execSync(
|
|
1219
|
-
log(
|
|
1377
|
+
execSync("npm i -g openskills", { stdio: "inherit" });
|
|
1378
|
+
log("✅ OpenSkills installed", "green");
|
|
1220
1379
|
} catch (e) {
|
|
1221
|
-
log(
|
|
1222
|
-
log(
|
|
1380
|
+
log("❌ Failed to install OpenSkills", "red");
|
|
1381
|
+
log(" Run: npm i -g openskills", "gray");
|
|
1223
1382
|
return;
|
|
1224
1383
|
}
|
|
1225
1384
|
}
|
|
1226
1385
|
|
|
1227
1386
|
// Find the skill
|
|
1228
|
-
const skill = registry.skills.find(s => s.name === skillName);
|
|
1387
|
+
const skill = registry.skills.find((s) => s.name === skillName);
|
|
1229
1388
|
|
|
1230
1389
|
if (!skill) {
|
|
1231
|
-
log(`❌ Skill "${skillName}" not found in official registry`,
|
|
1232
|
-
log(
|
|
1233
|
-
log(
|
|
1234
|
-
log(
|
|
1390
|
+
log(`❌ Skill "${skillName}" not found in official registry`, "red");
|
|
1391
|
+
log("", "gray");
|
|
1392
|
+
log("Run: smc skills:official");
|
|
1393
|
+
log("to see available skills.", "gray");
|
|
1235
1394
|
return;
|
|
1236
1395
|
}
|
|
1237
1396
|
|
|
1238
|
-
log(`📦 Installing: ${skillName}`,
|
|
1239
|
-
log(
|
|
1240
|
-
log(`Source: ${skill.source}`,
|
|
1241
|
-
log(`License: ${skill.license}`,
|
|
1242
|
-
log(
|
|
1397
|
+
log(`📦 Installing: ${skillName}`, "blue");
|
|
1398
|
+
log("", "gray");
|
|
1399
|
+
log(`Source: ${skill.source}`, "gray");
|
|
1400
|
+
log(`License: ${skill.license}`, "gray");
|
|
1401
|
+
log("", "gray");
|
|
1243
1402
|
|
|
1244
1403
|
try {
|
|
1245
|
-
execSync(`openskills install ${skill.source} -y`, { stdio:
|
|
1246
|
-
execSync(
|
|
1247
|
-
log(
|
|
1248
|
-
log(
|
|
1249
|
-
log(
|
|
1250
|
-
log(
|
|
1404
|
+
execSync(`openskills install ${skill.source} -y`, { stdio: "inherit" });
|
|
1405
|
+
execSync("openskills sync -y", { stdio: "pipe" });
|
|
1406
|
+
log("", "gray");
|
|
1407
|
+
log("✅ Skill installed successfully", "green");
|
|
1408
|
+
log("", "gray");
|
|
1409
|
+
log("The skill is now available in your conversations.", "gray");
|
|
1251
1410
|
} catch (e) {
|
|
1252
|
-
log(
|
|
1411
|
+
log("❌ Installation failed", "red");
|
|
1253
1412
|
}
|
|
1254
1413
|
},
|
|
1255
1414
|
|
|
1256
1415
|
// -------------------------------------------------------------------------
|
|
1257
|
-
|
|
1258
|
-
const fs = require(
|
|
1259
|
-
const path = require(
|
|
1260
|
-
const { execSync } = require(
|
|
1416
|
+
"skills:install-all": () => {
|
|
1417
|
+
const fs = require("fs");
|
|
1418
|
+
const path = require("path");
|
|
1419
|
+
const { execSync } = require("child_process");
|
|
1261
1420
|
const COLORS = {
|
|
1262
|
-
reset:
|
|
1263
|
-
green:
|
|
1264
|
-
blue:
|
|
1265
|
-
yellow:
|
|
1266
|
-
gray:
|
|
1267
|
-
red:
|
|
1268
|
-
cyan:
|
|
1421
|
+
reset: "\x1b[0m",
|
|
1422
|
+
green: "\x1b[32m",
|
|
1423
|
+
blue: "\x1b[34m",
|
|
1424
|
+
yellow: "\x1b[33m",
|
|
1425
|
+
gray: "\x1b[90m",
|
|
1426
|
+
red: "\x1b[31m",
|
|
1427
|
+
cyan: "\x1b[36m",
|
|
1269
1428
|
};
|
|
1270
1429
|
|
|
1271
|
-
const log = (msg, color =
|
|
1430
|
+
const log = (msg, color = "reset") => {
|
|
1272
1431
|
console.log(`${COLORS[color]}${msg}${COLORS.reset}`);
|
|
1273
1432
|
};
|
|
1274
1433
|
|
|
1275
|
-
const officialSkillsFile = path.join(
|
|
1434
|
+
const officialSkillsFile = path.join(
|
|
1435
|
+
__dirname,
|
|
1436
|
+
"../config/official-skills.json",
|
|
1437
|
+
);
|
|
1276
1438
|
|
|
1277
1439
|
if (!fs.existsSync(officialSkillsFile)) {
|
|
1278
|
-
log(
|
|
1440
|
+
log("⚠ Official skills registry not found", "yellow");
|
|
1279
1441
|
return;
|
|
1280
1442
|
}
|
|
1281
1443
|
|
|
1282
|
-
const registry = JSON.parse(fs.readFileSync(officialSkillsFile,
|
|
1283
|
-
const recommended = registry.skills.filter(s => s.recommended);
|
|
1444
|
+
const registry = JSON.parse(fs.readFileSync(officialSkillsFile, "utf-8"));
|
|
1445
|
+
const recommended = registry.skills.filter((s) => s.recommended);
|
|
1284
1446
|
|
|
1285
|
-
log(
|
|
1286
|
-
log(
|
|
1287
|
-
log(
|
|
1288
|
-
log(
|
|
1289
|
-
log(`Installing ${recommended.length} skills...`,
|
|
1290
|
-
log(
|
|
1447
|
+
log("", "gray");
|
|
1448
|
+
log("📦 Installing All Recommended Skills", "blue");
|
|
1449
|
+
log("=====================================", "gray");
|
|
1450
|
+
log("", "gray");
|
|
1451
|
+
log(`Installing ${recommended.length} skills...`, "gray");
|
|
1452
|
+
log("", "gray");
|
|
1291
1453
|
|
|
1292
1454
|
// Check if openskills is installed
|
|
1293
1455
|
try {
|
|
1294
|
-
execSync(
|
|
1456
|
+
execSync("openskills --version", { stdio: "ignore" });
|
|
1295
1457
|
} catch {
|
|
1296
|
-
log(
|
|
1458
|
+
log("⚠ OpenSkills not installed. Installing...", "yellow");
|
|
1297
1459
|
try {
|
|
1298
|
-
execSync(
|
|
1299
|
-
log(
|
|
1300
|
-
log(
|
|
1460
|
+
execSync("npm i -g openskills", { stdio: "inherit" });
|
|
1461
|
+
log("✅ OpenSkills installed", "green");
|
|
1462
|
+
log("", "gray");
|
|
1301
1463
|
} catch (e) {
|
|
1302
|
-
log(
|
|
1464
|
+
log("❌ Failed to install OpenSkills", "red");
|
|
1303
1465
|
return;
|
|
1304
1466
|
}
|
|
1305
1467
|
}
|
|
1306
1468
|
|
|
1307
1469
|
// Install anthropics/skills (includes all skills)
|
|
1308
1470
|
try {
|
|
1309
|
-
log(`Installing ${registry.source}...`,
|
|
1310
|
-
execSync(`openskills install ${registry.source} -y`, {
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
log(
|
|
1315
|
-
log(
|
|
1316
|
-
|
|
1317
|
-
log(
|
|
1318
|
-
|
|
1471
|
+
log(`Installing ${registry.source}...`, "cyan");
|
|
1472
|
+
execSync(`openskills install ${registry.source} -y`, {
|
|
1473
|
+
stdio: "inherit",
|
|
1474
|
+
});
|
|
1475
|
+
execSync("openskills sync -y", { stdio: "pipe" });
|
|
1476
|
+
log("", "gray");
|
|
1477
|
+
log("✅ All skills installed successfully", "green");
|
|
1478
|
+
log("", "gray");
|
|
1479
|
+
log("Installed skills:", "gray");
|
|
1480
|
+
recommended.forEach((s) => log(` • ${s.name}`, "gray"));
|
|
1481
|
+
log("", "gray");
|
|
1482
|
+
log("These skills are now available in your conversations.", "gray");
|
|
1319
1483
|
} catch (e) {
|
|
1320
|
-
log(
|
|
1484
|
+
log("❌ Installation failed", "red");
|
|
1321
1485
|
}
|
|
1322
1486
|
},
|
|
1323
1487
|
|
|
1324
1488
|
// -------------------------------------------------------------------------
|
|
1325
1489
|
doctor: () => {
|
|
1326
|
-
const fs = require(
|
|
1327
|
-
const path = require(
|
|
1328
|
-
const { execSync } = require(
|
|
1490
|
+
const fs = require("fs");
|
|
1491
|
+
const path = require("path");
|
|
1492
|
+
const { execSync } = require("child_process");
|
|
1329
1493
|
const COLORS = {
|
|
1330
|
-
reset:
|
|
1331
|
-
green:
|
|
1332
|
-
blue:
|
|
1333
|
-
yellow:
|
|
1334
|
-
gray:
|
|
1335
|
-
red:
|
|
1336
|
-
cyan:
|
|
1494
|
+
reset: "\x1b[0m",
|
|
1495
|
+
green: "\x1b[32m",
|
|
1496
|
+
blue: "\x1b[34m",
|
|
1497
|
+
yellow: "\x1b[33m",
|
|
1498
|
+
gray: "\x1b[90m",
|
|
1499
|
+
red: "\x1b[31m",
|
|
1500
|
+
cyan: "\x1b[36m",
|
|
1337
1501
|
};
|
|
1338
1502
|
|
|
1339
|
-
const log = (msg, color =
|
|
1503
|
+
const log = (msg, color = "reset") => {
|
|
1340
1504
|
console.log(`${COLORS[color]}${msg}${COLORS.reset}`);
|
|
1341
1505
|
};
|
|
1342
1506
|
|
|
1343
|
-
log(
|
|
1344
|
-
log(
|
|
1345
|
-
log(
|
|
1346
|
-
log(
|
|
1507
|
+
log("", "gray");
|
|
1508
|
+
log("🏥 SMC Health Check", "blue");
|
|
1509
|
+
log("=====================================", "gray");
|
|
1510
|
+
log("", "gray");
|
|
1347
1511
|
|
|
1348
1512
|
const checks = [];
|
|
1349
1513
|
let passCount = 0;
|
|
@@ -1351,197 +1515,275 @@ const commands = {
|
|
|
1351
1515
|
let failCount = 0;
|
|
1352
1516
|
|
|
1353
1517
|
// Check 1: Global config
|
|
1354
|
-
const globalConfigDir = path.join(
|
|
1355
|
-
|
|
1518
|
+
const globalConfigDir = path.join(
|
|
1519
|
+
process.env.HOME || process.env.USERPROFILE,
|
|
1520
|
+
".claude",
|
|
1521
|
+
);
|
|
1522
|
+
const globalConfigFile = path.join(globalConfigDir, "config.json");
|
|
1356
1523
|
|
|
1357
1524
|
if (fs.existsSync(globalConfigFile)) {
|
|
1358
|
-
checks.push({
|
|
1525
|
+
checks.push({
|
|
1526
|
+
name: "Global config",
|
|
1527
|
+
status: "pass",
|
|
1528
|
+
msg: globalConfigFile,
|
|
1529
|
+
});
|
|
1359
1530
|
passCount++;
|
|
1360
1531
|
} else {
|
|
1361
|
-
checks.push({
|
|
1532
|
+
checks.push({
|
|
1533
|
+
name: "Global config",
|
|
1534
|
+
status: "fail",
|
|
1535
|
+
msg: "Run: smc init",
|
|
1536
|
+
});
|
|
1362
1537
|
failCount++;
|
|
1363
1538
|
}
|
|
1364
1539
|
|
|
1365
1540
|
// Check 2: Project .claude directory
|
|
1366
1541
|
const projectDir = process.cwd();
|
|
1367
|
-
const projectClaudeDir = path.join(projectDir,
|
|
1542
|
+
const projectClaudeDir = path.join(projectDir, ".claude");
|
|
1368
1543
|
|
|
1369
1544
|
if (fs.existsSync(projectClaudeDir)) {
|
|
1370
|
-
checks.push({
|
|
1545
|
+
checks.push({
|
|
1546
|
+
name: "Project .claude/",
|
|
1547
|
+
status: "pass",
|
|
1548
|
+
msg: projectClaudeDir,
|
|
1549
|
+
});
|
|
1371
1550
|
passCount++;
|
|
1372
1551
|
|
|
1373
1552
|
// Check for key files
|
|
1374
|
-
const agentsFile = path.join(projectClaudeDir,
|
|
1553
|
+
const agentsFile = path.join(projectClaudeDir, "AGENTS.md");
|
|
1375
1554
|
if (fs.existsSync(agentsFile)) {
|
|
1376
|
-
checks.push({ name:
|
|
1555
|
+
checks.push({ name: "AGENTS.md", status: "pass", msg: "Generated" });
|
|
1377
1556
|
passCount++;
|
|
1378
1557
|
} else {
|
|
1379
|
-
checks.push({
|
|
1558
|
+
checks.push({
|
|
1559
|
+
name: "AGENTS.md",
|
|
1560
|
+
status: "warn",
|
|
1561
|
+
msg: "Run: smc sync",
|
|
1562
|
+
});
|
|
1380
1563
|
warnCount++;
|
|
1381
1564
|
}
|
|
1382
1565
|
|
|
1383
1566
|
// Check MEMORY.md age
|
|
1384
|
-
const memoryFile = path.join(projectClaudeDir,
|
|
1567
|
+
const memoryFile = path.join(projectClaudeDir, "MEMORY.md");
|
|
1385
1568
|
if (fs.existsSync(memoryFile)) {
|
|
1386
1569
|
const stats = fs.statSync(memoryFile);
|
|
1387
|
-
const daysSinceUpdate =
|
|
1570
|
+
const daysSinceUpdate =
|
|
1571
|
+
(Date.now() - stats.mtime.getTime()) / (1000 * 60 * 60 * 24);
|
|
1388
1572
|
if (daysSinceUpdate > 30) {
|
|
1389
|
-
checks.push({
|
|
1573
|
+
checks.push({
|
|
1574
|
+
name: "MEMORY.md",
|
|
1575
|
+
status: "warn",
|
|
1576
|
+
msg: `Updated ${Math.floor(daysSinceUpdate)} days ago`,
|
|
1577
|
+
});
|
|
1390
1578
|
warnCount++;
|
|
1391
1579
|
} else {
|
|
1392
|
-
checks.push({
|
|
1580
|
+
checks.push({
|
|
1581
|
+
name: "MEMORY.md",
|
|
1582
|
+
status: "pass",
|
|
1583
|
+
msg: `Updated ${Math.floor(daysSinceUpdate)} days ago`,
|
|
1584
|
+
});
|
|
1393
1585
|
passCount++;
|
|
1394
1586
|
}
|
|
1395
1587
|
} else {
|
|
1396
|
-
checks.push({ name:
|
|
1588
|
+
checks.push({ name: "MEMORY.md", status: "warn", msg: "Not found" });
|
|
1397
1589
|
warnCount++;
|
|
1398
1590
|
}
|
|
1399
1591
|
|
|
1400
1592
|
// Check hooks
|
|
1401
|
-
const hooksDir = path.join(projectClaudeDir,
|
|
1593
|
+
const hooksDir = path.join(projectClaudeDir, "hooks");
|
|
1402
1594
|
if (fs.existsSync(hooksDir)) {
|
|
1403
|
-
const hookFiles = fs
|
|
1404
|
-
|
|
1595
|
+
const hookFiles = fs
|
|
1596
|
+
.readdirSync(hooksDir)
|
|
1597
|
+
.filter((f) => !f.startsWith("."));
|
|
1598
|
+
checks.push({
|
|
1599
|
+
name: "Hooks",
|
|
1600
|
+
status: "pass",
|
|
1601
|
+
msg: `${hookFiles.length} hooks`,
|
|
1602
|
+
});
|
|
1405
1603
|
passCount++;
|
|
1406
1604
|
} else {
|
|
1407
|
-
checks.push({
|
|
1605
|
+
checks.push({
|
|
1606
|
+
name: "Hooks",
|
|
1607
|
+
status: "warn",
|
|
1608
|
+
msg: "No hooks directory",
|
|
1609
|
+
});
|
|
1408
1610
|
warnCount++;
|
|
1409
1611
|
}
|
|
1410
1612
|
|
|
1411
1613
|
// Check skills
|
|
1412
|
-
const skillsDir = path.join(projectClaudeDir,
|
|
1614
|
+
const skillsDir = path.join(projectClaudeDir, "skills");
|
|
1413
1615
|
if (fs.existsSync(skillsDir)) {
|
|
1414
|
-
const skillDirs = fs.readdirSync(skillsDir).filter(f => {
|
|
1616
|
+
const skillDirs = fs.readdirSync(skillsDir).filter((f) => {
|
|
1415
1617
|
const dir = path.join(skillsDir, f);
|
|
1416
|
-
return
|
|
1618
|
+
return (
|
|
1619
|
+
fs.statSync(dir).isDirectory() &&
|
|
1620
|
+
f !== "template" &&
|
|
1621
|
+
f !== "examples"
|
|
1622
|
+
);
|
|
1623
|
+
});
|
|
1624
|
+
checks.push({
|
|
1625
|
+
name: "Project Skills",
|
|
1626
|
+
status: "pass",
|
|
1627
|
+
msg: `${skillDirs.length} skills`,
|
|
1417
1628
|
});
|
|
1418
|
-
checks.push({ name: 'Project Skills', status: 'pass', msg: `${skillDirs.length} skills` });
|
|
1419
1629
|
passCount++;
|
|
1420
1630
|
} else {
|
|
1421
|
-
checks.push({
|
|
1631
|
+
checks.push({
|
|
1632
|
+
name: "Project Skills",
|
|
1633
|
+
status: "warn",
|
|
1634
|
+
msg: "No skills directory",
|
|
1635
|
+
});
|
|
1422
1636
|
warnCount++;
|
|
1423
1637
|
}
|
|
1424
1638
|
} else {
|
|
1425
|
-
checks.push({
|
|
1639
|
+
checks.push({
|
|
1640
|
+
name: "Project .claude/",
|
|
1641
|
+
status: "warn",
|
|
1642
|
+
msg: "Run: smc template or smc sync",
|
|
1643
|
+
});
|
|
1426
1644
|
warnCount++;
|
|
1427
1645
|
}
|
|
1428
1646
|
|
|
1429
1647
|
// Check 3: OpenSkills
|
|
1430
1648
|
try {
|
|
1431
|
-
execSync(
|
|
1432
|
-
const globalSkillsDir = path.join(globalConfigDir,
|
|
1649
|
+
execSync("openskills --version", { stdio: "ignore" });
|
|
1650
|
+
const globalSkillsDir = path.join(globalConfigDir, "skills");
|
|
1433
1651
|
if (fs.existsSync(globalSkillsDir)) {
|
|
1434
|
-
const skillCount = fs.readdirSync(globalSkillsDir).filter(f => {
|
|
1652
|
+
const skillCount = fs.readdirSync(globalSkillsDir).filter((f) => {
|
|
1435
1653
|
const dir = path.join(globalSkillsDir, f);
|
|
1436
|
-
return fs.statSync(dir).isDirectory() && !f.startsWith(
|
|
1654
|
+
return fs.statSync(dir).isDirectory() && !f.startsWith(".");
|
|
1437
1655
|
}).length;
|
|
1438
|
-
checks.push({
|
|
1656
|
+
checks.push({
|
|
1657
|
+
name: "OpenSkills",
|
|
1658
|
+
status: "pass",
|
|
1659
|
+
msg: `${skillCount} global skills`,
|
|
1660
|
+
});
|
|
1439
1661
|
passCount++;
|
|
1440
1662
|
} else {
|
|
1441
|
-
checks.push({ name:
|
|
1663
|
+
checks.push({ name: "OpenSkills", status: "pass", msg: "Installed" });
|
|
1442
1664
|
passCount++;
|
|
1443
1665
|
}
|
|
1444
1666
|
} catch {
|
|
1445
|
-
checks.push({
|
|
1667
|
+
checks.push({
|
|
1668
|
+
name: "OpenSkills",
|
|
1669
|
+
status: "warn",
|
|
1670
|
+
msg: "Not installed (run: npm i -g openskills)",
|
|
1671
|
+
});
|
|
1446
1672
|
warnCount++;
|
|
1447
1673
|
}
|
|
1448
1674
|
|
|
1449
1675
|
// Check 4: Git
|
|
1450
1676
|
try {
|
|
1451
|
-
execSync(
|
|
1452
|
-
checks.push({ name:
|
|
1677
|
+
execSync("git rev-parse --git-dir", { stdio: "ignore", cwd: projectDir });
|
|
1678
|
+
checks.push({ name: "Git", status: "pass", msg: "Repository detected" });
|
|
1453
1679
|
passCount++;
|
|
1454
1680
|
} catch {
|
|
1455
|
-
checks.push({ name:
|
|
1681
|
+
checks.push({ name: "Git", status: "warn", msg: "Not a git repository" });
|
|
1456
1682
|
warnCount++;
|
|
1457
1683
|
}
|
|
1458
1684
|
|
|
1459
1685
|
// Display results
|
|
1460
1686
|
for (const check of checks) {
|
|
1461
|
-
const icon =
|
|
1462
|
-
|
|
1687
|
+
const icon =
|
|
1688
|
+
check.status === "pass" ? "✅" : check.status === "warn" ? "⚠️" : "❌";
|
|
1689
|
+
const color =
|
|
1690
|
+
check.status === "pass"
|
|
1691
|
+
? "green"
|
|
1692
|
+
: check.status === "warn"
|
|
1693
|
+
? "yellow"
|
|
1694
|
+
: "red";
|
|
1463
1695
|
log(`${icon} ${check.name}`, color);
|
|
1464
|
-
log(` ${check.msg}`,
|
|
1465
|
-
log(
|
|
1696
|
+
log(` ${check.msg}`, "gray");
|
|
1697
|
+
log("", "gray");
|
|
1466
1698
|
}
|
|
1467
1699
|
|
|
1468
|
-
log(
|
|
1469
|
-
log(
|
|
1470
|
-
|
|
1700
|
+
log("=====================================", "gray");
|
|
1701
|
+
log(
|
|
1702
|
+
`Summary: ${passCount} passed, ${warnCount} warnings${failCount > 0 ? `, ${failCount} failed` : ""}`,
|
|
1703
|
+
"gray",
|
|
1704
|
+
);
|
|
1705
|
+
log("", "gray");
|
|
1471
1706
|
|
|
1472
1707
|
if (failCount > 0) {
|
|
1473
|
-
log(
|
|
1708
|
+
log("Fix failed checks to continue.", "red");
|
|
1474
1709
|
}
|
|
1475
1710
|
},
|
|
1476
1711
|
|
|
1477
1712
|
// -------------------------------------------------------------------------
|
|
1478
|
-
|
|
1479
|
-
const fs = require(
|
|
1480
|
-
const path = require(
|
|
1713
|
+
"skills:search": (keyword) => {
|
|
1714
|
+
const fs = require("fs");
|
|
1715
|
+
const path = require("path");
|
|
1481
1716
|
const COLORS = {
|
|
1482
|
-
reset:
|
|
1483
|
-
green:
|
|
1484
|
-
blue:
|
|
1485
|
-
yellow:
|
|
1486
|
-
gray:
|
|
1487
|
-
cyan:
|
|
1717
|
+
reset: "\x1b[0m",
|
|
1718
|
+
green: "\x1b[32m",
|
|
1719
|
+
blue: "\x1b[34m",
|
|
1720
|
+
yellow: "\x1b[33m",
|
|
1721
|
+
gray: "\x1b[90m",
|
|
1722
|
+
cyan: "\x1b[36m",
|
|
1488
1723
|
};
|
|
1489
1724
|
|
|
1490
|
-
const log = (msg, color =
|
|
1725
|
+
const log = (msg, color = "reset") => {
|
|
1491
1726
|
console.log(`${COLORS[color]}${msg}${COLORS.reset}`);
|
|
1492
1727
|
};
|
|
1493
1728
|
|
|
1494
1729
|
if (!keyword) {
|
|
1495
|
-
log(
|
|
1496
|
-
log(
|
|
1497
|
-
log(
|
|
1498
|
-
log(
|
|
1499
|
-
log(' smc skills:search "前端设计"',
|
|
1730
|
+
log("Usage: smc skills:search <keyword>", "yellow");
|
|
1731
|
+
log("", "gray");
|
|
1732
|
+
log("Examples:", "gray");
|
|
1733
|
+
log(" smc skills:search pdf", "gray");
|
|
1734
|
+
log(' smc skills:search "前端设计"', "gray");
|
|
1500
1735
|
return;
|
|
1501
1736
|
}
|
|
1502
1737
|
|
|
1503
|
-
log(
|
|
1504
|
-
log(`🔍 Searching for: "${keyword}"`,
|
|
1505
|
-
log(
|
|
1506
|
-
log(
|
|
1738
|
+
log("", "gray");
|
|
1739
|
+
log(`🔍 Searching for: "${keyword}"`, "blue");
|
|
1740
|
+
log("=====================================", "gray");
|
|
1741
|
+
log("", "gray");
|
|
1507
1742
|
|
|
1508
1743
|
const results = [];
|
|
1509
1744
|
const lowerKeyword = keyword.toLowerCase();
|
|
1510
1745
|
|
|
1511
1746
|
// Search in official skills registry
|
|
1512
|
-
const officialSkillsFile = path.join(
|
|
1747
|
+
const officialSkillsFile = path.join(
|
|
1748
|
+
__dirname,
|
|
1749
|
+
"../config/official-skills.json",
|
|
1750
|
+
);
|
|
1513
1751
|
if (fs.existsSync(officialSkillsFile)) {
|
|
1514
|
-
const registry = JSON.parse(fs.readFileSync(officialSkillsFile,
|
|
1752
|
+
const registry = JSON.parse(fs.readFileSync(officialSkillsFile, "utf-8"));
|
|
1515
1753
|
|
|
1516
1754
|
for (const skill of registry.skills) {
|
|
1517
1755
|
const matchName = skill.name.toLowerCase().includes(lowerKeyword);
|
|
1518
|
-
const matchDesc = skill.description
|
|
1519
|
-
|
|
1756
|
+
const matchDesc = skill.description
|
|
1757
|
+
.toLowerCase()
|
|
1758
|
+
.includes(lowerKeyword);
|
|
1759
|
+
const matchCat = registry.categories[skill.category]?.name
|
|
1760
|
+
.toLowerCase()
|
|
1761
|
+
.includes(lowerKeyword);
|
|
1520
1762
|
|
|
1521
1763
|
if (matchName || matchDesc || matchCat) {
|
|
1522
1764
|
results.push({
|
|
1523
1765
|
name: skill.name,
|
|
1524
1766
|
description: skill.description,
|
|
1525
1767
|
category: registry.categories[skill.category]?.name,
|
|
1526
|
-
source:
|
|
1527
|
-
recommended: skill.recommended
|
|
1768
|
+
source: "official",
|
|
1769
|
+
recommended: skill.recommended,
|
|
1528
1770
|
});
|
|
1529
1771
|
}
|
|
1530
1772
|
}
|
|
1531
1773
|
}
|
|
1532
1774
|
|
|
1533
1775
|
// Search in sources.yaml
|
|
1534
|
-
const sourcesFile = path.join(__dirname,
|
|
1776
|
+
const sourcesFile = path.join(__dirname, "../sources.yaml");
|
|
1535
1777
|
if (fs.existsSync(sourcesFile)) {
|
|
1536
|
-
const content = fs.readFileSync(sourcesFile,
|
|
1778
|
+
const content = fs.readFileSync(sourcesFile, "utf-8");
|
|
1537
1779
|
// Simple YAML parsing for skills
|
|
1538
|
-
const lines = content.split(
|
|
1780
|
+
const lines = content.split("\n");
|
|
1539
1781
|
let currentSkill = null;
|
|
1540
1782
|
|
|
1541
1783
|
for (const line of lines) {
|
|
1542
1784
|
const nameMatch = line.match(/^\s*-\s*name:\s*(.+)$/);
|
|
1543
1785
|
if (nameMatch) {
|
|
1544
|
-
currentSkill = { name: nameMatch[1].trim(), source:
|
|
1786
|
+
currentSkill = { name: nameMatch[1].trim(), source: "marketplace" };
|
|
1545
1787
|
if (currentSkill.name.toLowerCase().includes(lowerKeyword)) {
|
|
1546
1788
|
results.push(currentSkill);
|
|
1547
1789
|
}
|
|
@@ -1557,28 +1799,31 @@ const commands = {
|
|
|
1557
1799
|
}
|
|
1558
1800
|
|
|
1559
1801
|
// Search in global skills
|
|
1560
|
-
const globalSkillsDir = path.join(
|
|
1802
|
+
const globalSkillsDir = path.join(
|
|
1803
|
+
process.env.HOME || process.env.USERPROFILE,
|
|
1804
|
+
".claude/skills",
|
|
1805
|
+
);
|
|
1561
1806
|
if (fs.existsSync(globalSkillsDir)) {
|
|
1562
|
-
const skillDirs = fs.readdirSync(globalSkillsDir).filter(f => {
|
|
1807
|
+
const skillDirs = fs.readdirSync(globalSkillsDir).filter((f) => {
|
|
1563
1808
|
const dir = path.join(globalSkillsDir, f);
|
|
1564
|
-
return fs.statSync(dir).isDirectory() && !f.startsWith(
|
|
1809
|
+
return fs.statSync(dir).isDirectory() && !f.startsWith(".");
|
|
1565
1810
|
});
|
|
1566
1811
|
|
|
1567
1812
|
for (const skillName of skillDirs) {
|
|
1568
1813
|
if (skillName.toLowerCase().includes(lowerKeyword)) {
|
|
1569
1814
|
// Check if already in results
|
|
1570
|
-
if (!results.some(r => r.name === skillName)) {
|
|
1571
|
-
const skillFile = path.join(globalSkillsDir, skillName,
|
|
1572
|
-
let description =
|
|
1815
|
+
if (!results.some((r) => r.name === skillName)) {
|
|
1816
|
+
const skillFile = path.join(globalSkillsDir, skillName, "SKILL.md");
|
|
1817
|
+
let description = "";
|
|
1573
1818
|
if (fs.existsSync(skillFile)) {
|
|
1574
|
-
const content = fs.readFileSync(skillFile,
|
|
1819
|
+
const content = fs.readFileSync(skillFile, "utf-8");
|
|
1575
1820
|
const descMatch = content.match(/description:\s*(.+)/);
|
|
1576
1821
|
if (descMatch) description = descMatch[1];
|
|
1577
1822
|
}
|
|
1578
1823
|
results.push({
|
|
1579
1824
|
name: skillName,
|
|
1580
|
-
description: description ||
|
|
1581
|
-
source:
|
|
1825
|
+
description: description || "Local skill",
|
|
1826
|
+
source: "installed",
|
|
1582
1827
|
});
|
|
1583
1828
|
}
|
|
1584
1829
|
}
|
|
@@ -1587,56 +1832,61 @@ const commands = {
|
|
|
1587
1832
|
|
|
1588
1833
|
// Display results
|
|
1589
1834
|
if (results.length === 0) {
|
|
1590
|
-
log(
|
|
1835
|
+
log("No skills found matching your search.", "yellow");
|
|
1591
1836
|
} else {
|
|
1592
1837
|
for (const result of results) {
|
|
1593
|
-
const sourceIcon =
|
|
1594
|
-
|
|
1595
|
-
|
|
1838
|
+
const sourceIcon =
|
|
1839
|
+
result.source === "official"
|
|
1840
|
+
? "🔷"
|
|
1841
|
+
: result.source === "marketplace"
|
|
1842
|
+
? "📦"
|
|
1843
|
+
: "✓";
|
|
1844
|
+
const rec = result.recommended ? " [推荐]" : "";
|
|
1845
|
+
log(`${sourceIcon} ${result.name}${rec}`, "cyan");
|
|
1596
1846
|
if (result.description) {
|
|
1597
|
-
log(` ${result.description}`,
|
|
1847
|
+
log(` ${result.description}`, "gray");
|
|
1598
1848
|
}
|
|
1599
1849
|
if (result.category) {
|
|
1600
|
-
log(` 分类: ${result.category}`,
|
|
1850
|
+
log(` 分类: ${result.category}`, "gray");
|
|
1601
1851
|
}
|
|
1602
|
-
log(
|
|
1852
|
+
log("", "gray");
|
|
1603
1853
|
}
|
|
1604
1854
|
}
|
|
1605
1855
|
|
|
1606
|
-
log(
|
|
1607
|
-
log(`Found ${results.length} result(s)`,
|
|
1608
|
-
log(
|
|
1856
|
+
log("=====================================", "gray");
|
|
1857
|
+
log(`Found ${results.length} result(s)`, "gray");
|
|
1858
|
+
log("", "gray");
|
|
1609
1859
|
},
|
|
1610
1860
|
|
|
1611
1861
|
// -------------------------------------------------------------------------
|
|
1612
|
-
|
|
1613
|
-
const fs = require(
|
|
1614
|
-
const path = require(
|
|
1862
|
+
"skills:validate": (skillPath) => {
|
|
1863
|
+
const fs = require("fs");
|
|
1864
|
+
const path = require("path");
|
|
1615
1865
|
const COLORS = {
|
|
1616
|
-
reset:
|
|
1617
|
-
green:
|
|
1618
|
-
blue:
|
|
1619
|
-
yellow:
|
|
1620
|
-
gray:
|
|
1621
|
-
red:
|
|
1866
|
+
reset: "\x1b[0m",
|
|
1867
|
+
green: "\x1b[32m",
|
|
1868
|
+
blue: "\x1b[34m",
|
|
1869
|
+
yellow: "\x1b[33m",
|
|
1870
|
+
gray: "\x1b[90m",
|
|
1871
|
+
red: "\x1b[31m",
|
|
1622
1872
|
};
|
|
1623
1873
|
|
|
1624
|
-
const log = (msg, color =
|
|
1874
|
+
const log = (msg, color = "reset") => {
|
|
1625
1875
|
console.log(`${COLORS[color]}${msg}${COLORS.reset}`);
|
|
1626
1876
|
};
|
|
1627
1877
|
|
|
1628
1878
|
// Default to current directory if not specified
|
|
1629
|
-
const targetPath = skillPath || path.join(process.cwd(),
|
|
1879
|
+
const targetPath = skillPath || path.join(process.cwd(), ".claude/skills");
|
|
1630
1880
|
|
|
1631
1881
|
if (!fs.existsSync(targetPath)) {
|
|
1632
|
-
log(`❌ Path not found: ${targetPath}`,
|
|
1882
|
+
log(`❌ Path not found: ${targetPath}`, "red");
|
|
1633
1883
|
return;
|
|
1634
1884
|
}
|
|
1635
1885
|
|
|
1636
|
-
log(
|
|
1637
|
-
log(
|
|
1638
|
-
log(
|
|
1639
|
-
log(
|
|
1886
|
+
log("", "gray");
|
|
1887
|
+
log("🔍 Validating Skills", "blue");
|
|
1888
|
+
log("=====================================", "gray");
|
|
1889
|
+
log("", "gray");
|
|
1640
1890
|
|
|
1641
1891
|
const checks = [];
|
|
1642
1892
|
let passCount = 0;
|
|
@@ -1647,22 +1897,22 @@ const commands = {
|
|
|
1647
1897
|
|
|
1648
1898
|
const validateSkill = (skillDir) => {
|
|
1649
1899
|
const skillName = path.basename(skillDir);
|
|
1650
|
-
const skillFile = path.join(skillDir,
|
|
1900
|
+
const skillFile = path.join(skillDir, "SKILL.md");
|
|
1651
1901
|
const errors = [];
|
|
1652
1902
|
const warnings = [];
|
|
1653
1903
|
|
|
1654
1904
|
// Check SKILL.md exists
|
|
1655
1905
|
if (!fs.existsSync(skillFile)) {
|
|
1656
|
-
errors.push(
|
|
1906
|
+
errors.push("SKILL.md not found");
|
|
1657
1907
|
return { name: skillName, errors, warnings };
|
|
1658
1908
|
}
|
|
1659
1909
|
|
|
1660
1910
|
// Parse SKILL.md
|
|
1661
|
-
const content = fs.readFileSync(skillFile,
|
|
1911
|
+
const content = fs.readFileSync(skillFile, "utf-8");
|
|
1662
1912
|
const frontmatterMatch = content.match(/^---\n(.*?)\n---/s);
|
|
1663
1913
|
|
|
1664
1914
|
if (!frontmatterMatch) {
|
|
1665
|
-
errors.push(
|
|
1915
|
+
errors.push("No frontmatter found (--- delimited YAML required)");
|
|
1666
1916
|
} else {
|
|
1667
1917
|
const frontmatter = frontmatterMatch[1];
|
|
1668
1918
|
|
|
@@ -1676,8 +1926,13 @@ const commands = {
|
|
|
1676
1926
|
}
|
|
1677
1927
|
|
|
1678
1928
|
// Check for references directory if referenced
|
|
1679
|
-
if (
|
|
1680
|
-
|
|
1929
|
+
if (
|
|
1930
|
+
content.includes("references/") &&
|
|
1931
|
+
!fs.existsSync(path.join(skillDir, "references"))
|
|
1932
|
+
) {
|
|
1933
|
+
warnings.push(
|
|
1934
|
+
'Content references "references/" but directory not found',
|
|
1935
|
+
);
|
|
1681
1936
|
}
|
|
1682
1937
|
|
|
1683
1938
|
return { name: skillName, errors, warnings };
|
|
@@ -1685,7 +1940,7 @@ const commands = {
|
|
|
1685
1940
|
|
|
1686
1941
|
if (stats.isDirectory()) {
|
|
1687
1942
|
// Check if it's a skills directory or a single skill
|
|
1688
|
-
const skillFile = path.join(targetPath,
|
|
1943
|
+
const skillFile = path.join(targetPath, "SKILL.md");
|
|
1689
1944
|
if (fs.existsSync(skillFile)) {
|
|
1690
1945
|
// Single skill
|
|
1691
1946
|
const result = validateSkill(targetPath);
|
|
@@ -1696,7 +1951,7 @@ const commands = {
|
|
|
1696
1951
|
for (const entry of entries) {
|
|
1697
1952
|
const entryPath = path.join(targetPath, entry);
|
|
1698
1953
|
const entryStats = fs.statSync(entryPath);
|
|
1699
|
-
if (entryStats.isDirectory() && !entry.startsWith(
|
|
1954
|
+
if (entryStats.isDirectory() && !entry.startsWith(".")) {
|
|
1700
1955
|
const result = validateSkill(entryPath);
|
|
1701
1956
|
checks.push(result);
|
|
1702
1957
|
}
|
|
@@ -1707,56 +1962,59 @@ const commands = {
|
|
|
1707
1962
|
// Display results
|
|
1708
1963
|
for (const check of checks) {
|
|
1709
1964
|
if (check.errors.length === 0 && check.warnings.length === 0) {
|
|
1710
|
-
log(`✅ ${check.name}`,
|
|
1965
|
+
log(`✅ ${check.name}`, "green");
|
|
1711
1966
|
passCount++;
|
|
1712
1967
|
} else if (check.errors.length === 0) {
|
|
1713
|
-
log(`⚠️ ${check.name}`,
|
|
1968
|
+
log(`⚠️ ${check.name}`, "yellow");
|
|
1714
1969
|
passCount++;
|
|
1715
1970
|
} else {
|
|
1716
|
-
log(`❌ ${check.name}`,
|
|
1971
|
+
log(`❌ ${check.name}`, "red");
|
|
1717
1972
|
failCount++;
|
|
1718
1973
|
}
|
|
1719
1974
|
|
|
1720
1975
|
for (const error of check.errors) {
|
|
1721
|
-
log(` ❌ ${error}`,
|
|
1976
|
+
log(` ❌ ${error}`, "red");
|
|
1722
1977
|
}
|
|
1723
1978
|
for (const warning of check.warnings) {
|
|
1724
|
-
log(` ⚠️ ${warning}`,
|
|
1979
|
+
log(` ⚠️ ${warning}`, "yellow");
|
|
1725
1980
|
}
|
|
1726
|
-
log(
|
|
1981
|
+
log("", "gray");
|
|
1727
1982
|
}
|
|
1728
1983
|
|
|
1729
|
-
log(
|
|
1730
|
-
log(
|
|
1731
|
-
|
|
1984
|
+
log("=====================================", "gray");
|
|
1985
|
+
log(
|
|
1986
|
+
`Validated ${checks.length} skill(s): ${passCount} passed${failCount > 0 ? `, ${failCount} failed` : ""}`,
|
|
1987
|
+
"gray",
|
|
1988
|
+
);
|
|
1989
|
+
log("", "gray");
|
|
1732
1990
|
},
|
|
1733
1991
|
|
|
1734
1992
|
// -------------------------------------------------------------------------
|
|
1735
|
-
|
|
1736
|
-
const fs = require(
|
|
1737
|
-
const path = require(
|
|
1738
|
-
const { execSync } = require(
|
|
1993
|
+
"skills:update": () => {
|
|
1994
|
+
const fs = require("fs");
|
|
1995
|
+
const path = require("path");
|
|
1996
|
+
const { execSync } = require("child_process");
|
|
1739
1997
|
const COLORS = {
|
|
1740
|
-
reset:
|
|
1741
|
-
green:
|
|
1742
|
-
blue:
|
|
1743
|
-
yellow:
|
|
1744
|
-
gray:
|
|
1745
|
-
red:
|
|
1998
|
+
reset: "\x1b[0m",
|
|
1999
|
+
green: "\x1b[32m",
|
|
2000
|
+
blue: "\x1b[34m",
|
|
2001
|
+
yellow: "\x1b[33m",
|
|
2002
|
+
gray: "\x1b[90m",
|
|
2003
|
+
red: "\x1b[31m",
|
|
1746
2004
|
};
|
|
1747
2005
|
|
|
1748
|
-
const log = (msg, color =
|
|
2006
|
+
const log = (msg, color = "reset") => {
|
|
1749
2007
|
console.log(`${COLORS[color]}${msg}${COLORS.reset}`);
|
|
1750
2008
|
};
|
|
1751
2009
|
|
|
1752
|
-
log(
|
|
1753
|
-
log(
|
|
1754
|
-
log(
|
|
1755
|
-
log(
|
|
2010
|
+
log("", "gray");
|
|
2011
|
+
log("🔄 Updating Official Skills List", "blue");
|
|
2012
|
+
log("=====================================", "gray");
|
|
2013
|
+
log("", "gray");
|
|
1756
2014
|
|
|
1757
2015
|
// Fetch latest skills from anthropics/skills
|
|
1758
|
-
const tempDir = path.join(__dirname,
|
|
1759
|
-
const repoUrl =
|
|
2016
|
+
const tempDir = path.join(__dirname, "../.tmp");
|
|
2017
|
+
const repoUrl = "https://github.com/anthropics/skills.git";
|
|
1760
2018
|
|
|
1761
2019
|
try {
|
|
1762
2020
|
// Create temp dir
|
|
@@ -1764,7 +2022,7 @@ const commands = {
|
|
|
1764
2022
|
fs.mkdirSync(tempDir, { recursive: true });
|
|
1765
2023
|
}
|
|
1766
2024
|
|
|
1767
|
-
const cloneDir = path.join(tempDir,
|
|
2025
|
+
const cloneDir = path.join(tempDir, "anthropics-skills");
|
|
1768
2026
|
|
|
1769
2027
|
// Remove existing clone if present
|
|
1770
2028
|
const rimraf = (dir) => {
|
|
@@ -1775,106 +2033,115 @@ const commands = {
|
|
|
1775
2033
|
|
|
1776
2034
|
rimraf(cloneDir);
|
|
1777
2035
|
|
|
1778
|
-
log(
|
|
1779
|
-
execSync(`git clone --depth 1 ${repoUrl} ${cloneDir}`, { stdio:
|
|
2036
|
+
log("Cloning anthropics/skills...", "gray");
|
|
2037
|
+
execSync(`git clone --depth 1 ${repoUrl} ${cloneDir}`, { stdio: "pipe" });
|
|
1780
2038
|
|
|
1781
2039
|
// Read skills directory to get available skills
|
|
1782
|
-
const skillsDir = path.join(cloneDir,
|
|
1783
|
-
const skillCategories = fs.readdirSync(skillsDir).filter(f => {
|
|
2040
|
+
const skillsDir = path.join(cloneDir, "skills");
|
|
2041
|
+
const skillCategories = fs.readdirSync(skillsDir).filter((f) => {
|
|
1784
2042
|
const dir = path.join(skillsDir, f);
|
|
1785
2043
|
return fs.statSync(dir).isDirectory();
|
|
1786
2044
|
});
|
|
1787
2045
|
|
|
1788
|
-
log(`Found ${skillCategories.length} skills in repository`,
|
|
1789
|
-
log(
|
|
2046
|
+
log(`Found ${skillCategories.length} skills in repository`, "gray");
|
|
2047
|
+
log("", "gray");
|
|
1790
2048
|
|
|
1791
2049
|
// Update the registry file
|
|
1792
|
-
const registryFile = path.join(
|
|
1793
|
-
|
|
2050
|
+
const registryFile = path.join(
|
|
2051
|
+
__dirname,
|
|
2052
|
+
"../config/official-skills.json",
|
|
2053
|
+
);
|
|
2054
|
+
let registry = {
|
|
2055
|
+
version: "1.0.0",
|
|
2056
|
+
last_updated: new Date().toISOString().split("T")[0],
|
|
2057
|
+
source: "anthropics/skills",
|
|
2058
|
+
categories: {},
|
|
2059
|
+
skills: [],
|
|
2060
|
+
};
|
|
1794
2061
|
|
|
1795
2062
|
if (fs.existsSync(registryFile)) {
|
|
1796
|
-
registry = JSON.parse(fs.readFileSync(registryFile,
|
|
2063
|
+
registry = JSON.parse(fs.readFileSync(registryFile, "utf-8"));
|
|
1797
2064
|
}
|
|
1798
2065
|
|
|
1799
2066
|
// Update timestamp
|
|
1800
|
-
registry.last_updated = new Date().toISOString().split(
|
|
2067
|
+
registry.last_updated = new Date().toISOString().split("T")[0];
|
|
1801
2068
|
|
|
1802
2069
|
fs.writeFileSync(registryFile, JSON.stringify(registry, null, 2));
|
|
1803
2070
|
|
|
1804
2071
|
// Cleanup
|
|
1805
2072
|
rimraf(cloneDir);
|
|
1806
2073
|
|
|
1807
|
-
log(
|
|
1808
|
-
log(` Updated: ${registry.last_updated}`,
|
|
1809
|
-
log(
|
|
1810
|
-
log(
|
|
2074
|
+
log("✅ Official skills list updated", "green");
|
|
2075
|
+
log(` Updated: ${registry.last_updated}`, "gray");
|
|
2076
|
+
log("", "gray");
|
|
2077
|
+
log("Run: smc skills:official", "gray");
|
|
1811
2078
|
} catch (e) {
|
|
1812
|
-
log(
|
|
1813
|
-
log(` ${e.message}`,
|
|
2079
|
+
log("❌ Update failed", "red");
|
|
2080
|
+
log(` ${e.message}`, "gray");
|
|
1814
2081
|
}
|
|
1815
2082
|
},
|
|
1816
2083
|
|
|
1817
2084
|
// -------------------------------------------------------------------------
|
|
1818
2085
|
config: (action, key, value) => {
|
|
1819
|
-
const fs = require(
|
|
1820
|
-
const path = require(
|
|
1821
|
-
const { loadConfig, saveConfig, CONFIG_FILE } = require(
|
|
2086
|
+
const fs = require("fs");
|
|
2087
|
+
const path = require("path");
|
|
2088
|
+
const { loadConfig, saveConfig, CONFIG_FILE } = require("./config");
|
|
1822
2089
|
const COLORS = {
|
|
1823
|
-
reset:
|
|
1824
|
-
green:
|
|
1825
|
-
blue:
|
|
1826
|
-
yellow:
|
|
1827
|
-
gray:
|
|
1828
|
-
red:
|
|
2090
|
+
reset: "\x1b[0m",
|
|
2091
|
+
green: "\x1b[32m",
|
|
2092
|
+
blue: "\x1b[34m",
|
|
2093
|
+
yellow: "\x1b[33m",
|
|
2094
|
+
gray: "\x1b[90m",
|
|
2095
|
+
red: "\x1b[31m",
|
|
1829
2096
|
};
|
|
1830
2097
|
|
|
1831
|
-
const log = (msg, color =
|
|
2098
|
+
const log = (msg, color = "reset") => {
|
|
1832
2099
|
console.log(`${COLORS[color]}${msg}${COLORS.reset}`);
|
|
1833
2100
|
};
|
|
1834
2101
|
|
|
1835
2102
|
if (!action) {
|
|
1836
2103
|
// Show current config
|
|
1837
2104
|
const config = loadConfig();
|
|
1838
|
-
log(
|
|
1839
|
-
log(
|
|
1840
|
-
log(
|
|
1841
|
-
log(
|
|
1842
|
-
log(`File: ${CONFIG_FILE}`,
|
|
1843
|
-
log(
|
|
2105
|
+
log("", "gray");
|
|
2106
|
+
log("⚙️ SMC Configuration", "blue");
|
|
2107
|
+
log("=====================================", "gray");
|
|
2108
|
+
log("", "gray");
|
|
2109
|
+
log(`File: ${CONFIG_FILE}`, "gray");
|
|
2110
|
+
log("", "gray");
|
|
1844
2111
|
log(JSON.stringify(config, null, 2));
|
|
1845
|
-
log(
|
|
1846
|
-
log(
|
|
1847
|
-
log(
|
|
1848
|
-
log(
|
|
1849
|
-
log(
|
|
1850
|
-
log(
|
|
1851
|
-
log(
|
|
2112
|
+
log("", "gray");
|
|
2113
|
+
log("=====================================", "gray");
|
|
2114
|
+
log("", "gray");
|
|
2115
|
+
log("Commands:", "gray");
|
|
2116
|
+
log(" smc config get <key> Get a config value", "gray");
|
|
2117
|
+
log(" smc config set <key> <value> Set a config value", "gray");
|
|
2118
|
+
log("", "gray");
|
|
1852
2119
|
return;
|
|
1853
2120
|
}
|
|
1854
2121
|
|
|
1855
|
-
if (action ===
|
|
2122
|
+
if (action === "get") {
|
|
1856
2123
|
if (!key) {
|
|
1857
|
-
log(
|
|
2124
|
+
log("Usage: smc config get <key>", "yellow");
|
|
1858
2125
|
return;
|
|
1859
2126
|
}
|
|
1860
2127
|
const config = loadConfig();
|
|
1861
|
-
const keys = key.split(
|
|
2128
|
+
const keys = key.split(".");
|
|
1862
2129
|
let value = config;
|
|
1863
2130
|
for (const k of keys) {
|
|
1864
2131
|
value = value?.[k];
|
|
1865
2132
|
}
|
|
1866
2133
|
if (value !== undefined) {
|
|
1867
|
-
log(`${key}: ${JSON.stringify(value, null, 2)}`,
|
|
2134
|
+
log(`${key}: ${JSON.stringify(value, null, 2)}`, "green");
|
|
1868
2135
|
} else {
|
|
1869
|
-
log(`Key not found: ${key}`,
|
|
2136
|
+
log(`Key not found: ${key}`, "yellow");
|
|
1870
2137
|
}
|
|
1871
|
-
} else if (action ===
|
|
2138
|
+
} else if (action === "set") {
|
|
1872
2139
|
if (!key || value === undefined) {
|
|
1873
|
-
log(
|
|
2140
|
+
log("Usage: smc config set <key> <value>", "yellow");
|
|
1874
2141
|
return;
|
|
1875
2142
|
}
|
|
1876
2143
|
const config = loadConfig();
|
|
1877
|
-
const keys = key.split(
|
|
2144
|
+
const keys = key.split(".");
|
|
1878
2145
|
let target = config;
|
|
1879
2146
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
1880
2147
|
if (!target[keys[i]]) target[keys[i]] = {};
|
|
@@ -1887,56 +2154,56 @@ const commands = {
|
|
|
1887
2154
|
target[keys[keys.length - 1]] = value;
|
|
1888
2155
|
}
|
|
1889
2156
|
saveConfig(config);
|
|
1890
|
-
log(`✅ Set ${key} = ${target[keys[keys.length - 1]]}`,
|
|
2157
|
+
log(`✅ Set ${key} = ${target[keys[keys.length - 1]]}`, "green");
|
|
1891
2158
|
} else {
|
|
1892
|
-
log(`Unknown action: ${action}`,
|
|
1893
|
-
log(
|
|
2159
|
+
log(`Unknown action: ${action}`, "red");
|
|
2160
|
+
log("Valid actions: get, set", "gray");
|
|
1894
2161
|
}
|
|
1895
2162
|
},
|
|
1896
2163
|
|
|
1897
2164
|
// -------------------------------------------------------------------------
|
|
1898
|
-
|
|
1899
|
-
const fs = require(
|
|
1900
|
-
const path = require(
|
|
1901
|
-
const { execSync } = require(
|
|
2165
|
+
"skills:publish": (skillPath) => {
|
|
2166
|
+
const fs = require("fs");
|
|
2167
|
+
const path = require("path");
|
|
2168
|
+
const { execSync } = require("child_process");
|
|
1902
2169
|
const COLORS = {
|
|
1903
|
-
reset:
|
|
1904
|
-
green:
|
|
1905
|
-
blue:
|
|
1906
|
-
yellow:
|
|
1907
|
-
gray:
|
|
1908
|
-
red:
|
|
1909
|
-
cyan:
|
|
2170
|
+
reset: "\x1b[0m",
|
|
2171
|
+
green: "\x1b[32m",
|
|
2172
|
+
blue: "\x1b[34m",
|
|
2173
|
+
yellow: "\x1b[33m",
|
|
2174
|
+
gray: "\x1b[90m",
|
|
2175
|
+
red: "\x1b[31m",
|
|
2176
|
+
cyan: "\x1b[36m",
|
|
1910
2177
|
};
|
|
1911
2178
|
|
|
1912
|
-
const log = (msg, color =
|
|
2179
|
+
const log = (msg, color = "reset") => {
|
|
1913
2180
|
console.log(`${COLORS[color]}${msg}${COLORS.reset}`);
|
|
1914
2181
|
};
|
|
1915
2182
|
|
|
1916
2183
|
// Default to current directory's skills folder
|
|
1917
|
-
const targetPath = skillPath || path.join(process.cwd(),
|
|
2184
|
+
const targetPath = skillPath || path.join(process.cwd(), ".claude/skills");
|
|
1918
2185
|
|
|
1919
2186
|
if (!fs.existsSync(targetPath)) {
|
|
1920
|
-
log(`❌ Path not found: ${targetPath}`,
|
|
1921
|
-
log(
|
|
1922
|
-
log(
|
|
1923
|
-
log(
|
|
2187
|
+
log(`❌ Path not found: ${targetPath}`, "red");
|
|
2188
|
+
log("", "gray");
|
|
2189
|
+
log("Usage: smc skills:publish [skill-path]", "yellow");
|
|
2190
|
+
log(" Creates a GitHub repo with your skill", "gray");
|
|
1924
2191
|
return;
|
|
1925
2192
|
}
|
|
1926
2193
|
|
|
1927
|
-
log(
|
|
1928
|
-
log(
|
|
1929
|
-
log(
|
|
1930
|
-
log(
|
|
2194
|
+
log("", "gray");
|
|
2195
|
+
log("📦 Publish Skill to GitHub", "blue");
|
|
2196
|
+
log("=====================================", "gray");
|
|
2197
|
+
log("", "gray");
|
|
1931
2198
|
|
|
1932
|
-
const readline = require(
|
|
2199
|
+
const readline = require("readline");
|
|
1933
2200
|
const rl = readline.createInterface({
|
|
1934
2201
|
input: process.stdin,
|
|
1935
|
-
output: process.stdout
|
|
2202
|
+
output: process.stdout,
|
|
1936
2203
|
});
|
|
1937
2204
|
|
|
1938
2205
|
const question = (prompt) => {
|
|
1939
|
-
return new Promise(resolve => {
|
|
2206
|
+
return new Promise((resolve) => {
|
|
1940
2207
|
rl.question(prompt, resolve);
|
|
1941
2208
|
});
|
|
1942
2209
|
};
|
|
@@ -1947,234 +2214,258 @@ const commands = {
|
|
|
1947
2214
|
let skillName, skillDir;
|
|
1948
2215
|
|
|
1949
2216
|
if (stat.isDirectory()) {
|
|
1950
|
-
const skillFile = path.join(targetPath,
|
|
2217
|
+
const skillFile = path.join(targetPath, "SKILL.md");
|
|
1951
2218
|
if (fs.existsSync(skillFile)) {
|
|
1952
2219
|
// Single skill directory
|
|
1953
2220
|
skillDir = targetPath;
|
|
1954
2221
|
skillName = path.basename(targetPath);
|
|
1955
2222
|
} else {
|
|
1956
2223
|
// Skills directory - ask which skill
|
|
1957
|
-
const entries = fs.readdirSync(targetPath).filter(f => {
|
|
2224
|
+
const entries = fs.readdirSync(targetPath).filter((f) => {
|
|
1958
2225
|
const dir = path.join(targetPath, f);
|
|
1959
|
-
return
|
|
2226
|
+
return (
|
|
2227
|
+
fs.statSync(dir).isDirectory() &&
|
|
2228
|
+
!f.startsWith(".") &&
|
|
2229
|
+
f !== "template" &&
|
|
2230
|
+
f !== "examples"
|
|
2231
|
+
);
|
|
1960
2232
|
});
|
|
1961
2233
|
|
|
1962
2234
|
if (entries.length === 0) {
|
|
1963
|
-
log(
|
|
2235
|
+
log("❌ No skills found in directory", "red");
|
|
1964
2236
|
rl.close();
|
|
1965
2237
|
return;
|
|
1966
2238
|
}
|
|
1967
2239
|
|
|
1968
|
-
log(
|
|
1969
|
-
entries.forEach((e, i) => log(` ${i + 1}. ${e}`,
|
|
1970
|
-
log(
|
|
2240
|
+
log("Found skills:", "gray");
|
|
2241
|
+
entries.forEach((e, i) => log(` ${i + 1}. ${e}`, "gray"));
|
|
2242
|
+
log("", "gray");
|
|
1971
2243
|
|
|
1972
|
-
const choice = await question(
|
|
1973
|
-
const index = parseInt(choice ||
|
|
2244
|
+
const choice = await question("Select skill [1]: ");
|
|
2245
|
+
const index = parseInt(choice || "1") - 1;
|
|
1974
2246
|
skillName = entries[index] || entries[0];
|
|
1975
2247
|
skillDir = path.join(targetPath, skillName);
|
|
1976
2248
|
}
|
|
1977
2249
|
} else {
|
|
1978
|
-
log(
|
|
2250
|
+
log("❌ Path must be a directory", "red");
|
|
1979
2251
|
rl.close();
|
|
1980
2252
|
return;
|
|
1981
2253
|
}
|
|
1982
2254
|
|
|
1983
2255
|
// Read SKILL.md to get info
|
|
1984
|
-
const skillFile = path.join(skillDir,
|
|
2256
|
+
const skillFile = path.join(skillDir, "SKILL.md");
|
|
1985
2257
|
if (!fs.existsSync(skillFile)) {
|
|
1986
|
-
log(
|
|
2258
|
+
log("❌ SKILL.md not found. A skill must have SKILL.md", "red");
|
|
1987
2259
|
rl.close();
|
|
1988
2260
|
return;
|
|
1989
2261
|
}
|
|
1990
2262
|
|
|
1991
|
-
const skillContent = fs.readFileSync(skillFile,
|
|
2263
|
+
const skillContent = fs.readFileSync(skillFile, "utf-8");
|
|
1992
2264
|
const nameMatch = skillContent.match(/name:\s*(.+)/);
|
|
1993
2265
|
const descMatch = skillContent.match(/description:\s*(.+)/);
|
|
1994
2266
|
const displayName = nameMatch ? nameMatch[1].trim() : skillName;
|
|
1995
|
-
const description = descMatch ? descMatch[1].trim() :
|
|
2267
|
+
const description = descMatch ? descMatch[1].trim() : "";
|
|
1996
2268
|
|
|
1997
|
-
log(`Skill: ${displayName}`,
|
|
2269
|
+
log(`Skill: ${displayName}`, "cyan");
|
|
1998
2270
|
if (description) {
|
|
1999
|
-
log(`Description: ${description}`,
|
|
2271
|
+
log(`Description: ${description}`, "gray");
|
|
2000
2272
|
}
|
|
2001
|
-
log(`Path: ${skillDir}`,
|
|
2002
|
-
log(
|
|
2273
|
+
log(`Path: ${skillDir}`, "gray");
|
|
2274
|
+
log("", "gray");
|
|
2003
2275
|
|
|
2004
2276
|
// Get GitHub info
|
|
2005
|
-
const githubUser = await question(
|
|
2277
|
+
const githubUser = await question("GitHub username: ");
|
|
2006
2278
|
if (!githubUser) {
|
|
2007
|
-
log(
|
|
2279
|
+
log("❌ GitHub username required", "red");
|
|
2008
2280
|
rl.close();
|
|
2009
2281
|
return;
|
|
2010
2282
|
}
|
|
2011
2283
|
|
|
2012
|
-
const repoName =
|
|
2013
|
-
|
|
2284
|
+
const repoName =
|
|
2285
|
+
(await question(`Repository name [${skillName}]: `)) || skillName;
|
|
2286
|
+
const isPrivate = await question("Private repo? [y/N]: ");
|
|
2014
2287
|
|
|
2015
2288
|
rl.close();
|
|
2016
2289
|
|
|
2017
|
-
log(
|
|
2018
|
-
log(
|
|
2019
|
-
log(
|
|
2020
|
-
log(`1. Create GitHub repo:`,
|
|
2021
|
-
log(` https://github.com/new`,
|
|
2022
|
-
log(` Name: ${repoName}`,
|
|
2023
|
-
log(
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
log(
|
|
2028
|
-
log(`
|
|
2029
|
-
log(`
|
|
2030
|
-
log(` git
|
|
2031
|
-
log(` git
|
|
2032
|
-
log(` git
|
|
2033
|
-
log(
|
|
2034
|
-
log(
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
log(`
|
|
2039
|
-
log(
|
|
2040
|
-
log(`
|
|
2041
|
-
log(`
|
|
2042
|
-
log(
|
|
2290
|
+
log("", "gray");
|
|
2291
|
+
log("📝 Instructions:", "yellow");
|
|
2292
|
+
log("", "gray");
|
|
2293
|
+
log(`1. Create GitHub repo:`, "gray");
|
|
2294
|
+
log(` https://github.com/new`, "gray");
|
|
2295
|
+
log(` Name: ${repoName}`, "gray");
|
|
2296
|
+
log(
|
|
2297
|
+
` ${isPrivate.toLowerCase() === "y" ? "Private" : "Public"}`,
|
|
2298
|
+
"gray",
|
|
2299
|
+
);
|
|
2300
|
+
log("", "gray");
|
|
2301
|
+
log(`2. Initialize git and push:`, "gray");
|
|
2302
|
+
log(` cd ${skillDir}`, "gray");
|
|
2303
|
+
log(` git init`, "gray");
|
|
2304
|
+
log(` git add .`, "gray");
|
|
2305
|
+
log(` git commit -m "Initial commit"`, "gray");
|
|
2306
|
+
log(` git branch -M main`, "gray");
|
|
2307
|
+
log(
|
|
2308
|
+
` git remote add origin https://github.com/${githubUser}/${repoName}.git`,
|
|
2309
|
+
"gray",
|
|
2310
|
+
);
|
|
2311
|
+
log(` git push -u origin main`, "gray");
|
|
2312
|
+
log("", "gray");
|
|
2313
|
+
log(`3. Add to sources.yaml:`, "gray");
|
|
2314
|
+
log(` - name: ${skillName}`, "gray");
|
|
2315
|
+
log(` source:`, "gray");
|
|
2316
|
+
log(` repo: ${githubUser}/${repoName}`, "gray");
|
|
2317
|
+
log(` path: .`, "gray");
|
|
2318
|
+
log(` target:`, "gray");
|
|
2319
|
+
log(` category: tools`, "gray");
|
|
2320
|
+
log(` path: template/.claude/skills/tools/${skillName}`, "gray");
|
|
2321
|
+
log("", "gray");
|
|
2043
2322
|
|
|
2044
2323
|
// Ask if user wants to auto-execute
|
|
2045
|
-
const autoExecute = await question(
|
|
2046
|
-
if (autoExecute.toLowerCase() ===
|
|
2324
|
+
const autoExecute = await question("Auto-execute git commands? [y/N]: ");
|
|
2325
|
+
if (autoExecute.toLowerCase() === "y") {
|
|
2047
2326
|
try {
|
|
2048
|
-
log(
|
|
2049
|
-
log(
|
|
2050
|
-
execSync(
|
|
2051
|
-
execSync(
|
|
2052
|
-
execSync('git commit -m "Initial commit"', {
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2327
|
+
log("", "gray");
|
|
2328
|
+
log("Initializing git...", "gray");
|
|
2329
|
+
execSync("git init", { cwd: skillDir, stdio: "pipe" });
|
|
2330
|
+
execSync("git add .", { cwd: skillDir, stdio: "pipe" });
|
|
2331
|
+
execSync('git commit -m "Initial commit"', {
|
|
2332
|
+
cwd: skillDir,
|
|
2333
|
+
stdio: "pipe",
|
|
2334
|
+
});
|
|
2335
|
+
execSync("git branch -M main", { cwd: skillDir, stdio: "pipe" });
|
|
2336
|
+
log("✅ Git initialized", "green");
|
|
2337
|
+
|
|
2338
|
+
log("Adding remote...", "gray");
|
|
2339
|
+
execSync(
|
|
2340
|
+
`git remote add origin https://github.com/${githubUser}/${repoName}.git`,
|
|
2341
|
+
{ cwd: skillDir, stdio: "pipe" },
|
|
2342
|
+
);
|
|
2343
|
+
log("✅ Remote added", "green");
|
|
2344
|
+
|
|
2345
|
+
log("", "gray");
|
|
2346
|
+
log("⚠️ Please create the GitHub repo first, then run:", "yellow");
|
|
2347
|
+
log(` cd ${skillDir} && git push -u origin main`, "gray");
|
|
2063
2348
|
} catch (e) {
|
|
2064
|
-
log(
|
|
2065
|
-
log(` ${e.message}`,
|
|
2349
|
+
log("❌ Failed to initialize git", "red");
|
|
2350
|
+
log(` ${e.message}`, "gray");
|
|
2066
2351
|
}
|
|
2067
2352
|
}
|
|
2068
2353
|
|
|
2069
|
-
log(
|
|
2070
|
-
log(
|
|
2071
|
-
log(
|
|
2072
|
-
log(
|
|
2354
|
+
log("", "gray");
|
|
2355
|
+
log("=====================================", "gray");
|
|
2356
|
+
log("✅ Skill publishing guide complete", "green");
|
|
2357
|
+
log("", "gray");
|
|
2073
2358
|
})();
|
|
2074
2359
|
},
|
|
2075
2360
|
|
|
2076
2361
|
// -------------------------------------------------------------------------
|
|
2077
2362
|
changelog: (...args) => {
|
|
2078
|
-
const fs = require(
|
|
2079
|
-
const path = require(
|
|
2080
|
-
const { execSync } = require(
|
|
2363
|
+
const fs = require("fs");
|
|
2364
|
+
const path = require("path");
|
|
2365
|
+
const { execSync } = require("child_process");
|
|
2081
2366
|
const COLORS = {
|
|
2082
|
-
reset:
|
|
2083
|
-
green:
|
|
2084
|
-
blue:
|
|
2085
|
-
yellow:
|
|
2086
|
-
gray:
|
|
2087
|
-
red:
|
|
2088
|
-
cyan:
|
|
2367
|
+
reset: "\x1b[0m",
|
|
2368
|
+
green: "\x1b[32m",
|
|
2369
|
+
blue: "\x1b[34m",
|
|
2370
|
+
yellow: "\x1b[33m",
|
|
2371
|
+
gray: "\x1b[90m",
|
|
2372
|
+
red: "\x1b[31m",
|
|
2373
|
+
cyan: "\x1b[36m",
|
|
2089
2374
|
};
|
|
2090
2375
|
|
|
2091
|
-
const log = (msg, color =
|
|
2376
|
+
const log = (msg, color = "reset") => {
|
|
2092
2377
|
console.log(`${COLORS[color]}${msg}${COLORS.reset}`);
|
|
2093
2378
|
};
|
|
2094
2379
|
|
|
2095
2380
|
// Parse options
|
|
2096
2381
|
const options = { from: null, to: null, json: false, help: false };
|
|
2097
2382
|
for (let i = 0; i < args.length; i++) {
|
|
2098
|
-
if (args[i] ===
|
|
2099
|
-
if (args[i] ===
|
|
2100
|
-
if (args[i] ===
|
|
2101
|
-
if (args[i] ===
|
|
2383
|
+
if (args[i] === "--from" && args[i + 1]) options.from = args[++i];
|
|
2384
|
+
if (args[i] === "--to" && args[i + 1]) options.to = args[++i];
|
|
2385
|
+
if (args[i] === "--json") options.json = true;
|
|
2386
|
+
if (args[i] === "--help" || args[i] === "-h") options.help = true;
|
|
2102
2387
|
}
|
|
2103
2388
|
|
|
2104
2389
|
if (options.help) {
|
|
2105
|
-
log(
|
|
2106
|
-
log(
|
|
2107
|
-
log(
|
|
2108
|
-
log(
|
|
2109
|
-
log(
|
|
2110
|
-
log(
|
|
2111
|
-
log(
|
|
2112
|
-
log(
|
|
2113
|
-
log(
|
|
2114
|
-
log(
|
|
2115
|
-
log(
|
|
2116
|
-
log(
|
|
2117
|
-
log(
|
|
2118
|
-
log(
|
|
2119
|
-
log(
|
|
2120
|
-
log(
|
|
2121
|
-
log(
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
log(
|
|
2390
|
+
log("", "gray");
|
|
2391
|
+
log("📝 Changelog Generator", "blue");
|
|
2392
|
+
log("=====================================", "gray");
|
|
2393
|
+
log("", "gray");
|
|
2394
|
+
log("Generate changelog from git commits.", "gray");
|
|
2395
|
+
log("", "gray");
|
|
2396
|
+
log("Usage:", "gray");
|
|
2397
|
+
log(" smc changelog [options]", "gray");
|
|
2398
|
+
log("", "gray");
|
|
2399
|
+
log("Options:", "gray");
|
|
2400
|
+
log(" --from <tag> Start from this tag (default: last tag)", "gray");
|
|
2401
|
+
log(" --to <tag> End at this tag (default: HEAD)", "gray");
|
|
2402
|
+
log(" --json Output as JSON", "gray");
|
|
2403
|
+
log(" --help, -h Show this help", "gray");
|
|
2404
|
+
log("", "gray");
|
|
2405
|
+
log("Examples:", "gray");
|
|
2406
|
+
log(
|
|
2407
|
+
" smc changelog # Changelog since last tag",
|
|
2408
|
+
"gray",
|
|
2409
|
+
);
|
|
2410
|
+
log(" smc changelog --from v1.0.0 # Since v1.0.0", "gray");
|
|
2411
|
+
log(
|
|
2412
|
+
" smc changelog --from v1.0 --to v1.1 # Range between tags",
|
|
2413
|
+
"gray",
|
|
2414
|
+
);
|
|
2415
|
+
log(" smc changelog --json # JSON output", "gray");
|
|
2416
|
+
log("", "gray");
|
|
2126
2417
|
return;
|
|
2127
2418
|
}
|
|
2128
2419
|
|
|
2129
2420
|
// Check if we're in a git repo
|
|
2130
2421
|
const projectDir = process.cwd();
|
|
2131
2422
|
try {
|
|
2132
|
-
execSync(
|
|
2423
|
+
execSync("git rev-parse --git-dir", { stdio: "ignore", cwd: projectDir });
|
|
2133
2424
|
} catch {
|
|
2134
|
-
log(
|
|
2425
|
+
log("❌ Not a git repository", "red");
|
|
2135
2426
|
return;
|
|
2136
2427
|
}
|
|
2137
2428
|
|
|
2138
|
-
log(
|
|
2139
|
-
log(
|
|
2140
|
-
log(
|
|
2141
|
-
log(
|
|
2429
|
+
log("", "gray");
|
|
2430
|
+
log("📝 Generating Changelog", "blue");
|
|
2431
|
+
log("=====================================", "gray");
|
|
2432
|
+
log("", "gray");
|
|
2142
2433
|
|
|
2143
2434
|
// Get version range
|
|
2144
2435
|
let fromRef = options.from;
|
|
2145
|
-
let toRef = options.to ||
|
|
2436
|
+
let toRef = options.to || "HEAD";
|
|
2146
2437
|
|
|
2147
2438
|
if (!fromRef) {
|
|
2148
2439
|
// Try to get the last tag
|
|
2149
2440
|
try {
|
|
2150
|
-
const lastTag = execSync(
|
|
2441
|
+
const lastTag = execSync("git describe --tags --abbrev=0", {
|
|
2151
2442
|
cwd: projectDir,
|
|
2152
|
-
stdio:
|
|
2153
|
-
encoding:
|
|
2443
|
+
stdio: "pipe",
|
|
2444
|
+
encoding: "utf-8",
|
|
2154
2445
|
}).trim();
|
|
2155
2446
|
fromRef = lastTag;
|
|
2156
|
-
log(`From: ${lastTag} (last tag)`,
|
|
2447
|
+
log(`From: ${lastTag} (last tag)`, "gray");
|
|
2157
2448
|
} catch {
|
|
2158
2449
|
// No tags found, use first commit
|
|
2159
2450
|
try {
|
|
2160
|
-
const firstCommit = execSync(
|
|
2451
|
+
const firstCommit = execSync("git rev-list --max-parents=0 HEAD", {
|
|
2161
2452
|
cwd: projectDir,
|
|
2162
|
-
stdio:
|
|
2163
|
-
encoding:
|
|
2453
|
+
stdio: "pipe",
|
|
2454
|
+
encoding: "utf-8",
|
|
2164
2455
|
}).trim();
|
|
2165
2456
|
fromRef = firstCommit;
|
|
2166
|
-
log(`From: ${firstCommit.substring(0, 8)} (first commit)`,
|
|
2457
|
+
log(`From: ${firstCommit.substring(0, 8)} (first commit)`, "gray");
|
|
2167
2458
|
} catch {
|
|
2168
|
-
fromRef =
|
|
2169
|
-
log(`From: HEAD~10 (default)`,
|
|
2459
|
+
fromRef = "HEAD~10"; // Fallback to last 10 commits
|
|
2460
|
+
log(`From: HEAD~10 (default)`, "gray");
|
|
2170
2461
|
}
|
|
2171
2462
|
}
|
|
2172
2463
|
} else {
|
|
2173
|
-
log(`From: ${fromRef}`,
|
|
2464
|
+
log(`From: ${fromRef}`, "gray");
|
|
2174
2465
|
}
|
|
2175
2466
|
|
|
2176
|
-
log(`To: ${toRef}`,
|
|
2177
|
-
log(
|
|
2467
|
+
log(`To: ${toRef}`, "gray");
|
|
2468
|
+
log("", "gray");
|
|
2178
2469
|
|
|
2179
2470
|
// Get commit log
|
|
2180
2471
|
const range = `${fromRef}..${toRef}`;
|
|
@@ -2182,44 +2473,48 @@ const commands = {
|
|
|
2182
2473
|
try {
|
|
2183
2474
|
const logOutput = execSync(
|
|
2184
2475
|
`git log ${range} --pretty=format:"%H%x1F%s%x1F%an%x1F%ad" --date=short`,
|
|
2185
|
-
{ cwd: projectDir, stdio:
|
|
2476
|
+
{ cwd: projectDir, stdio: "pipe", encoding: "utf-8" },
|
|
2186
2477
|
);
|
|
2187
|
-
commits = logOutput
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2478
|
+
commits = logOutput
|
|
2479
|
+
.trim()
|
|
2480
|
+
.split("\n")
|
|
2481
|
+
.filter(Boolean)
|
|
2482
|
+
.map((line) => {
|
|
2483
|
+
const [hash, subject, author, date] = line.split("\x1F");
|
|
2484
|
+
return { hash, subject, author, date };
|
|
2485
|
+
});
|
|
2191
2486
|
} catch (e) {
|
|
2192
2487
|
// Empty range or invalid refs
|
|
2193
2488
|
commits = [];
|
|
2194
2489
|
}
|
|
2195
2490
|
|
|
2196
2491
|
if (commits.length === 0) {
|
|
2197
|
-
log(
|
|
2198
|
-
log(
|
|
2492
|
+
log("⚠️ No commits found in range", "yellow");
|
|
2493
|
+
log("", "gray");
|
|
2199
2494
|
return;
|
|
2200
2495
|
}
|
|
2201
2496
|
|
|
2202
|
-
log(`Found ${commits.length} commit(s)`,
|
|
2203
|
-
log(
|
|
2497
|
+
log(`Found ${commits.length} commit(s)`, "gray");
|
|
2498
|
+
log("", "gray");
|
|
2204
2499
|
|
|
2205
2500
|
// Parse conventional commits
|
|
2206
2501
|
const categories = {
|
|
2207
|
-
feat: { name:
|
|
2208
|
-
fix: { name:
|
|
2209
|
-
docs: { name:
|
|
2210
|
-
style: { name:
|
|
2211
|
-
refactor: { name:
|
|
2212
|
-
perf: { name:
|
|
2213
|
-
test: { name:
|
|
2214
|
-
build: { name:
|
|
2215
|
-
ci: { name:
|
|
2216
|
-
chore: { name:
|
|
2217
|
-
other: { name:
|
|
2502
|
+
feat: { name: "Features", icon: "✨", commits: [] },
|
|
2503
|
+
fix: { name: "Bug Fixes", icon: "🐛", commits: [] },
|
|
2504
|
+
docs: { name: "Documentation", icon: "📝", commits: [] },
|
|
2505
|
+
style: { name: "Styles", icon: "💄", commits: [] },
|
|
2506
|
+
refactor: { name: "Refactor", icon: "♻️", commits: [] },
|
|
2507
|
+
perf: { name: "Performance", icon: "⚡", commits: [] },
|
|
2508
|
+
test: { name: "Tests", icon: "🧪", commits: [] },
|
|
2509
|
+
build: { name: "Build", icon: "📦", commits: [] },
|
|
2510
|
+
ci: { name: "CI", icon: "🤖", commits: [] },
|
|
2511
|
+
chore: { name: "Chores", icon: "🧹", commits: [] },
|
|
2512
|
+
other: { name: "Other", icon: "📌", commits: [] },
|
|
2218
2513
|
};
|
|
2219
2514
|
|
|
2220
2515
|
const conventionalCommitRegex = /^(\w+)(?:\(([^)]+)\))?:?\s*(.+)$/;
|
|
2221
2516
|
|
|
2222
|
-
commits.forEach(commit => {
|
|
2517
|
+
commits.forEach((commit) => {
|
|
2223
2518
|
const match = commit.subject.match(conventionalCommitRegex);
|
|
2224
2519
|
if (match) {
|
|
2225
2520
|
const [, type, scope, description] = match;
|
|
@@ -2229,7 +2524,7 @@ const commands = {
|
|
|
2229
2524
|
description: description.trim(),
|
|
2230
2525
|
scope: scope || null,
|
|
2231
2526
|
author: commit.author,
|
|
2232
|
-
date: commit.date
|
|
2527
|
+
date: commit.date,
|
|
2233
2528
|
});
|
|
2234
2529
|
} else {
|
|
2235
2530
|
categories.other.commits.push({
|
|
@@ -2237,7 +2532,7 @@ const commands = {
|
|
|
2237
2532
|
description: commit.subject,
|
|
2238
2533
|
scope: null,
|
|
2239
2534
|
author: commit.author,
|
|
2240
|
-
date: commit.date
|
|
2535
|
+
date: commit.date,
|
|
2241
2536
|
});
|
|
2242
2537
|
}
|
|
2243
2538
|
});
|
|
@@ -2250,7 +2545,7 @@ const commands = {
|
|
|
2250
2545
|
jsonOutput[key] = {
|
|
2251
2546
|
name: category.name,
|
|
2252
2547
|
icon: category.icon,
|
|
2253
|
-
commits: category.commits
|
|
2548
|
+
commits: category.commits,
|
|
2254
2549
|
};
|
|
2255
2550
|
}
|
|
2256
2551
|
}
|
|
@@ -2259,14 +2554,14 @@ const commands = {
|
|
|
2259
2554
|
}
|
|
2260
2555
|
|
|
2261
2556
|
// Markdown output
|
|
2262
|
-
const today = new Date().toISOString().split(
|
|
2557
|
+
const today = new Date().toISOString().split("T")[0];
|
|
2263
2558
|
|
|
2264
2559
|
// Check if CHANGELOG.md exists and append
|
|
2265
|
-
const changelogFile = path.join(projectDir,
|
|
2560
|
+
const changelogFile = path.join(projectDir, "CHANGELOG.md");
|
|
2266
2561
|
|
|
2267
|
-
let existingContent =
|
|
2562
|
+
let existingContent = "";
|
|
2268
2563
|
if (fs.existsSync(changelogFile)) {
|
|
2269
|
-
existingContent = fs.readFileSync(changelogFile,
|
|
2564
|
+
existingContent = fs.readFileSync(changelogFile, "utf-8");
|
|
2270
2565
|
}
|
|
2271
2566
|
|
|
2272
2567
|
// Generate new entry
|
|
@@ -2275,11 +2570,11 @@ const commands = {
|
|
|
2275
2570
|
for (const [key, category] of Object.entries(categories)) {
|
|
2276
2571
|
if (category.commits.length > 0) {
|
|
2277
2572
|
newEntry += `### ${category.icon} ${category.name}\n\n`;
|
|
2278
|
-
category.commits.forEach(commit => {
|
|
2279
|
-
const scopeStr = commit.scope ? `**${commit.scope}**: ` :
|
|
2573
|
+
category.commits.forEach((commit) => {
|
|
2574
|
+
const scopeStr = commit.scope ? `**${commit.scope}**: ` : "";
|
|
2280
2575
|
newEntry += `- ${scopeStr}${commit.description} (${commit.hash})\n`;
|
|
2281
2576
|
});
|
|
2282
|
-
newEntry +=
|
|
2577
|
+
newEntry += "\n";
|
|
2283
2578
|
}
|
|
2284
2579
|
}
|
|
2285
2580
|
|
|
@@ -2293,15 +2588,15 @@ const commands = {
|
|
|
2293
2588
|
// Replace existing unreleased section
|
|
2294
2589
|
const updatedContent = existingContent.replace(
|
|
2295
2590
|
unreleasedRegex,
|
|
2296
|
-
newEntry.trimEnd() +
|
|
2591
|
+
newEntry.trimEnd() + "\n\n",
|
|
2297
2592
|
);
|
|
2298
2593
|
fs.writeFileSync(changelogFile, updatedContent);
|
|
2299
|
-
log(
|
|
2594
|
+
log("✅ Updated [Unreleased] section in CHANGELOG.md", "green");
|
|
2300
2595
|
} else {
|
|
2301
2596
|
// Prepend to existing content
|
|
2302
2597
|
const newContent = newEntry + existingContent;
|
|
2303
2598
|
fs.writeFileSync(changelogFile, newContent);
|
|
2304
|
-
log(
|
|
2599
|
+
log("✅ Added to CHANGELOG.md", "green");
|
|
2305
2600
|
}
|
|
2306
2601
|
} else {
|
|
2307
2602
|
// Create new CHANGELOG.md
|
|
@@ -2311,37 +2606,37 @@ All notable changes to this project will be documented in this file.
|
|
|
2311
2606
|
|
|
2312
2607
|
`;
|
|
2313
2608
|
fs.writeFileSync(changelogFile, header + newEntry);
|
|
2314
|
-
log(
|
|
2609
|
+
log("✅ Created CHANGELOG.md", "green");
|
|
2315
2610
|
}
|
|
2316
2611
|
|
|
2317
|
-
log(
|
|
2318
|
-
log(
|
|
2319
|
-
log(
|
|
2612
|
+
log("", "gray");
|
|
2613
|
+
log("📄 Preview:", "gray");
|
|
2614
|
+
log("", "gray");
|
|
2320
2615
|
console.log(newEntry);
|
|
2321
2616
|
|
|
2322
|
-
log(
|
|
2323
|
-
log(
|
|
2617
|
+
log("=====================================", "gray");
|
|
2618
|
+
log("", "gray");
|
|
2324
2619
|
},
|
|
2325
2620
|
|
|
2326
2621
|
// ==========================================================================
|
|
2327
2622
|
// Quality Gate Commands
|
|
2328
2623
|
// ==========================================================================
|
|
2329
2624
|
|
|
2330
|
-
|
|
2331
|
-
const { QualityGate } = require(
|
|
2625
|
+
"qg:check": async (severity = "warn") => {
|
|
2626
|
+
const { QualityGate } = require("./quality-gate");
|
|
2332
2627
|
const gate = new QualityGate({ projectDir: process.cwd() });
|
|
2333
2628
|
const result = await gate.check({ severity });
|
|
2334
2629
|
process.exit(result.passed ? 0 : 1);
|
|
2335
2630
|
},
|
|
2336
2631
|
|
|
2337
|
-
|
|
2338
|
-
const registry = require(
|
|
2632
|
+
"qg:rules": () => {
|
|
2633
|
+
const registry = require("./quality-rules").registry;
|
|
2339
2634
|
const rules = registry.getAll();
|
|
2340
2635
|
|
|
2341
|
-
console.log(
|
|
2342
|
-
console.log(
|
|
2343
|
-
console.log(
|
|
2344
|
-
console.log(
|
|
2636
|
+
console.log("📋 Available Quality Rules");
|
|
2637
|
+
console.log("");
|
|
2638
|
+
console.log("Rules are checked when running quality gate:");
|
|
2639
|
+
console.log("");
|
|
2345
2640
|
|
|
2346
2641
|
const byCategory = {};
|
|
2347
2642
|
for (const rule of rules) {
|
|
@@ -2352,159 +2647,176 @@ All notable changes to this project will be documented in this file.
|
|
|
2352
2647
|
for (const [category, rules] of Object.entries(byCategory)) {
|
|
2353
2648
|
console.log(`${category.toUpperCase()}:`);
|
|
2354
2649
|
for (const rule of rules) {
|
|
2355
|
-
const status = rule.enabled ?
|
|
2356
|
-
const sev = { info:
|
|
2650
|
+
const status = rule.enabled ? "✅" : "⊝";
|
|
2651
|
+
const sev = { info: "I", warn: "W", error: "E", critical: "X" }[
|
|
2652
|
+
rule.severity
|
|
2653
|
+
];
|
|
2357
2654
|
console.log(` ${status} ${rule.id} [${sev}] - ${rule.name}`);
|
|
2358
2655
|
}
|
|
2359
|
-
console.log(
|
|
2656
|
+
console.log("");
|
|
2360
2657
|
}
|
|
2361
2658
|
},
|
|
2362
2659
|
|
|
2363
|
-
|
|
2660
|
+
"qg:init": () => {
|
|
2364
2661
|
const projectDir = process.cwd();
|
|
2365
|
-
const configDir = path.join(projectDir,
|
|
2366
|
-
const targetPath = path.join(configDir,
|
|
2367
|
-
const sourcePath = path.join(__dirname,
|
|
2662
|
+
const configDir = path.join(projectDir, ".claude");
|
|
2663
|
+
const targetPath = path.join(configDir, "quality-gate.json");
|
|
2664
|
+
const sourcePath = path.join(__dirname, "../config/quality-gate.json");
|
|
2368
2665
|
|
|
2369
2666
|
if (fs.existsSync(targetPath)) {
|
|
2370
|
-
console.log(
|
|
2667
|
+
console.log("⚠️ quality-gate.json already exists");
|
|
2371
2668
|
return;
|
|
2372
2669
|
}
|
|
2373
2670
|
|
|
2374
2671
|
ensureDir(configDir);
|
|
2375
2672
|
fs.copyFileSync(sourcePath, targetPath);
|
|
2376
|
-
console.log(
|
|
2377
|
-
console.log(
|
|
2378
|
-
console.log(
|
|
2379
|
-
console.log(
|
|
2380
|
-
console.log(
|
|
2673
|
+
console.log("✅ Created .claude/quality-gate.json");
|
|
2674
|
+
console.log("");
|
|
2675
|
+
console.log("To enable Git hooks:");
|
|
2676
|
+
console.log(" ln -s .claude/hooks/pre-commit.cjs .git/hooks/pre-commit");
|
|
2677
|
+
console.log(" ln -s .claude/hooks/pre-push.cjs .git/hooks/pre-push");
|
|
2381
2678
|
},
|
|
2382
2679
|
|
|
2383
2680
|
// ==========================================================================
|
|
2384
2681
|
// Config Commands
|
|
2385
2682
|
// ==========================================================================
|
|
2386
2683
|
|
|
2387
|
-
|
|
2388
|
-
const { ConfigValidator } = require(
|
|
2684
|
+
"config:validate": () => {
|
|
2685
|
+
const { ConfigValidator } = require("./config-validator");
|
|
2389
2686
|
const validator = new ConfigValidator();
|
|
2390
2687
|
|
|
2391
|
-
console.log(
|
|
2392
|
-
console.log(
|
|
2688
|
+
console.log("🔍 Validating configuration...");
|
|
2689
|
+
console.log("");
|
|
2393
2690
|
|
|
2394
2691
|
let hasErrors = false;
|
|
2395
2692
|
let hasWarnings = false;
|
|
2396
2693
|
|
|
2397
2694
|
// Check global config
|
|
2398
|
-
const globalConfigPath = path.join(
|
|
2695
|
+
const globalConfigPath = path.join(
|
|
2696
|
+
process.env.HOME,
|
|
2697
|
+
".claude",
|
|
2698
|
+
"config.json",
|
|
2699
|
+
);
|
|
2399
2700
|
console.log(`Global: ${globalConfigPath}`);
|
|
2400
2701
|
const globalResult = validator.validateFile(globalConfigPath);
|
|
2401
2702
|
|
|
2402
2703
|
if (globalResult.valid) {
|
|
2403
|
-
console.log(
|
|
2704
|
+
console.log(" ✅ Valid");
|
|
2404
2705
|
} else {
|
|
2405
2706
|
if (globalResult.errors.length > 0) {
|
|
2406
2707
|
hasErrors = true;
|
|
2407
2708
|
console.log(` ❌ ${globalResult.errors.length} error(s)`);
|
|
2408
|
-
globalResult.errors.forEach(e => {
|
|
2709
|
+
globalResult.errors.forEach((e) => {
|
|
2409
2710
|
console.log(` [${e.severity}] ${e.path}: ${e.message}`);
|
|
2410
2711
|
});
|
|
2411
2712
|
}
|
|
2412
2713
|
if (globalResult.warnings.length > 0) {
|
|
2413
2714
|
hasWarnings = true;
|
|
2414
2715
|
console.log(` ⚠️ ${globalResult.warnings.length} warning(s)`);
|
|
2415
|
-
globalResult.warnings.forEach(e => {
|
|
2716
|
+
globalResult.warnings.forEach((e) => {
|
|
2416
2717
|
console.log(` [${e.severity}] ${e.path}: ${e.message}`);
|
|
2417
2718
|
});
|
|
2418
2719
|
}
|
|
2419
|
-
if (
|
|
2420
|
-
|
|
2720
|
+
if (
|
|
2721
|
+
globalResult.errors.length === 0 &&
|
|
2722
|
+
globalResult.warnings.length === 0
|
|
2723
|
+
) {
|
|
2724
|
+
console.log(" ❌ Validation failed");
|
|
2421
2725
|
}
|
|
2422
2726
|
}
|
|
2423
|
-
console.log(
|
|
2727
|
+
console.log("");
|
|
2424
2728
|
|
|
2425
2729
|
// Check project config if in project
|
|
2426
2730
|
const projectDir = process.cwd();
|
|
2427
|
-
const projectConfigPath = path.join(projectDir,
|
|
2731
|
+
const projectConfigPath = path.join(projectDir, ".claude", "settings.json");
|
|
2428
2732
|
if (fs.existsSync(projectConfigPath)) {
|
|
2429
2733
|
console.log(`Project: ${projectConfigPath}`);
|
|
2430
|
-
const projectResult = validator.validateFile(
|
|
2734
|
+
const projectResult = validator.validateFile(
|
|
2735
|
+
projectConfigPath,
|
|
2736
|
+
"settings",
|
|
2737
|
+
);
|
|
2431
2738
|
if (projectResult.valid) {
|
|
2432
|
-
console.log(
|
|
2739
|
+
console.log(" ✅ Valid");
|
|
2433
2740
|
} else {
|
|
2434
2741
|
if (projectResult.errors.length > 0) {
|
|
2435
2742
|
hasErrors = true;
|
|
2436
2743
|
console.log(` ❌ ${projectResult.errors.length} error(s)`);
|
|
2437
|
-
projectResult.errors.forEach(e => {
|
|
2744
|
+
projectResult.errors.forEach((e) => {
|
|
2438
2745
|
console.log(` [${e.severity}] ${e.path}: ${e.message}`);
|
|
2439
2746
|
});
|
|
2440
2747
|
}
|
|
2441
2748
|
if (projectResult.warnings.length > 0) {
|
|
2442
2749
|
hasWarnings = true;
|
|
2443
2750
|
console.log(` ⚠️ ${projectResult.warnings.length} warning(s)`);
|
|
2444
|
-
projectResult.warnings.forEach(e => {
|
|
2751
|
+
projectResult.warnings.forEach((e) => {
|
|
2445
2752
|
console.log(` [${e.severity}] ${e.path}: ${e.message}`);
|
|
2446
2753
|
});
|
|
2447
2754
|
}
|
|
2448
|
-
if (
|
|
2449
|
-
|
|
2755
|
+
if (
|
|
2756
|
+
projectResult.errors.length === 0 &&
|
|
2757
|
+
projectResult.warnings.length === 0
|
|
2758
|
+
) {
|
|
2759
|
+
console.log(" ❌ Validation failed");
|
|
2450
2760
|
}
|
|
2451
2761
|
}
|
|
2452
2762
|
}
|
|
2453
|
-
console.log(
|
|
2763
|
+
console.log("");
|
|
2454
2764
|
|
|
2455
2765
|
if (hasErrors) {
|
|
2456
|
-
console.log(
|
|
2766
|
+
console.log("❌ Configuration validation failed");
|
|
2457
2767
|
process.exit(1);
|
|
2458
2768
|
} else if (hasWarnings) {
|
|
2459
|
-
console.log(
|
|
2769
|
+
console.log("⚠️ Configuration has warnings (but is valid)");
|
|
2460
2770
|
} else {
|
|
2461
|
-
console.log(
|
|
2771
|
+
console.log("✅ All configurations are valid");
|
|
2462
2772
|
}
|
|
2463
2773
|
},
|
|
2464
2774
|
|
|
2465
|
-
|
|
2466
|
-
const { ConfigManager } = require(
|
|
2775
|
+
"config:backup": () => {
|
|
2776
|
+
const { ConfigManager } = require("./config-manager");
|
|
2467
2777
|
const manager = new ConfigManager();
|
|
2468
2778
|
const backupPath = manager._createBackup();
|
|
2469
|
-
console.log(
|
|
2779
|
+
console.log("✅ Config backed up to:", backupPath);
|
|
2470
2780
|
},
|
|
2471
2781
|
|
|
2472
|
-
|
|
2473
|
-
const { ConfigManager } = require(
|
|
2782
|
+
"config:rollback": (version) => {
|
|
2783
|
+
const { ConfigManager } = require("./config-manager");
|
|
2474
2784
|
const manager = new ConfigManager();
|
|
2475
2785
|
|
|
2476
2786
|
const backups = manager.listBackups();
|
|
2477
2787
|
if (backups.length === 0) {
|
|
2478
|
-
console.log(
|
|
2788
|
+
console.log("❌ No backups available");
|
|
2479
2789
|
return;
|
|
2480
2790
|
}
|
|
2481
2791
|
|
|
2482
2792
|
if (!version) {
|
|
2483
|
-
console.log(
|
|
2793
|
+
console.log("Available backups:");
|
|
2484
2794
|
backups.forEach((b, i) => {
|
|
2485
|
-
console.log(
|
|
2795
|
+
console.log(
|
|
2796
|
+
` ${i + 1}. ${b.version} (${new Date(b.timestamp).toLocaleString()})`,
|
|
2797
|
+
);
|
|
2486
2798
|
});
|
|
2487
|
-
console.log(
|
|
2488
|
-
console.log(
|
|
2799
|
+
console.log("");
|
|
2800
|
+
console.log("Usage: smc config:rollback <version>");
|
|
2489
2801
|
return;
|
|
2490
2802
|
}
|
|
2491
2803
|
|
|
2492
2804
|
try {
|
|
2493
2805
|
const result = manager.rollback(version);
|
|
2494
|
-
console.log(
|
|
2806
|
+
console.log("✅ Rolled back to:", result.restoredFrom);
|
|
2495
2807
|
} catch (e) {
|
|
2496
|
-
console.log(
|
|
2808
|
+
console.log("❌", e.message);
|
|
2497
2809
|
}
|
|
2498
2810
|
},
|
|
2499
2811
|
|
|
2500
|
-
|
|
2501
|
-
const { ConfigManager } = require(
|
|
2812
|
+
"config:diff": (file1, file2) => {
|
|
2813
|
+
const { ConfigManager } = require("./config-manager");
|
|
2502
2814
|
const manager = new ConfigManager();
|
|
2503
2815
|
|
|
2504
2816
|
if (!file1) {
|
|
2505
2817
|
const backups = manager.listBackups();
|
|
2506
2818
|
if (backups.length === 0) {
|
|
2507
|
-
console.log(
|
|
2819
|
+
console.log("❌ No backups available");
|
|
2508
2820
|
return;
|
|
2509
2821
|
}
|
|
2510
2822
|
file1 = path.join(manager.backupDir, backups[0].file);
|
|
@@ -2513,19 +2825,19 @@ All notable changes to this project will be documented in this file.
|
|
|
2513
2825
|
|
|
2514
2826
|
const changes = manager.diff(file1, file2);
|
|
2515
2827
|
if (changes.length === 0) {
|
|
2516
|
-
console.log(
|
|
2828
|
+
console.log("✅ No differences found");
|
|
2517
2829
|
return;
|
|
2518
2830
|
}
|
|
2519
2831
|
|
|
2520
|
-
console.log(
|
|
2521
|
-
console.log(
|
|
2832
|
+
console.log("📊 Config Diff:");
|
|
2833
|
+
console.log("");
|
|
2522
2834
|
for (const change of changes) {
|
|
2523
|
-
const icon = { added:
|
|
2835
|
+
const icon = { added: "+", removed: "-", changed: "~" }[change.type];
|
|
2524
2836
|
console.log(` ${icon} ${change.path}`);
|
|
2525
|
-
if (change.type !==
|
|
2837
|
+
if (change.type !== "removed") {
|
|
2526
2838
|
console.log(` from: ${JSON.stringify(change.from)}`);
|
|
2527
2839
|
}
|
|
2528
|
-
if (change.type !==
|
|
2840
|
+
if (change.type !== "added") {
|
|
2529
2841
|
console.log(` to: ${JSON.stringify(change.to)}`);
|
|
2530
2842
|
}
|
|
2531
2843
|
}
|
|
@@ -2539,180 +2851,226 @@ All notable changes to this project will be documented in this file.
|
|
|
2539
2851
|
const [action, ...rest] = args;
|
|
2540
2852
|
|
|
2541
2853
|
const COLORS = {
|
|
2542
|
-
reset:
|
|
2543
|
-
green:
|
|
2544
|
-
blue:
|
|
2545
|
-
yellow:
|
|
2546
|
-
gray:
|
|
2547
|
-
red:
|
|
2548
|
-
cyan:
|
|
2854
|
+
reset: "\x1b[0m",
|
|
2855
|
+
green: "\x1b[32m",
|
|
2856
|
+
blue: "\x1b[34m",
|
|
2857
|
+
yellow: "\x1b[33m",
|
|
2858
|
+
gray: "\x1b[90m",
|
|
2859
|
+
red: "\x1b[31m",
|
|
2860
|
+
cyan: "\x1b[36m",
|
|
2549
2861
|
};
|
|
2550
2862
|
|
|
2551
|
-
const log = (msg, color =
|
|
2863
|
+
const log = (msg, color = "reset") => {
|
|
2552
2864
|
console.log(`${COLORS[color]}${msg}${COLORS.reset}`);
|
|
2553
2865
|
};
|
|
2554
2866
|
|
|
2555
2867
|
switch (action) {
|
|
2556
|
-
case
|
|
2557
|
-
const idea = rest.join(
|
|
2868
|
+
case "start": {
|
|
2869
|
+
const idea = rest.join(" ");
|
|
2558
2870
|
if (!idea) {
|
|
2559
|
-
log('Usage: smc workflow start "<your idea>" [context...]',
|
|
2560
|
-
log(
|
|
2561
|
-
log(
|
|
2871
|
+
log('Usage: smc workflow start "<your idea>" [context...]', "gray");
|
|
2872
|
+
log("", "gray");
|
|
2873
|
+
log(
|
|
2874
|
+
'Example: smc workflow start "Build a REST API for task management"',
|
|
2875
|
+
"gray",
|
|
2876
|
+
);
|
|
2562
2877
|
return;
|
|
2563
2878
|
}
|
|
2564
2879
|
|
|
2565
2880
|
// Import workflow modules
|
|
2566
|
-
const {
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
log(
|
|
2572
|
-
log(
|
|
2573
|
-
log(
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2881
|
+
const {
|
|
2882
|
+
createProject,
|
|
2883
|
+
generateProjectId,
|
|
2884
|
+
} = require("../.claude/workflow/phases/phase1-research");
|
|
2885
|
+
|
|
2886
|
+
log("", "gray");
|
|
2887
|
+
log("🚀 Starting Phase 1: Research", "blue");
|
|
2888
|
+
log("=====================================", "gray");
|
|
2889
|
+
log("", "gray");
|
|
2890
|
+
log(`Idea: ${idea}`, "cyan");
|
|
2891
|
+
log("", "gray");
|
|
2892
|
+
|
|
2893
|
+
createProject(idea)
|
|
2894
|
+
.then((projectId) => {
|
|
2895
|
+
log("", "gray");
|
|
2896
|
+
log("✅ Project initialized!", "green");
|
|
2897
|
+
log(` Project ID: ${projectId}`, "gray");
|
|
2898
|
+
log(
|
|
2899
|
+
` Report: development/projects/${projectId}/phase1/feasibility-report.md`,
|
|
2900
|
+
"gray",
|
|
2901
|
+
);
|
|
2902
|
+
log("", "gray");
|
|
2903
|
+
log("Next steps:", "gray");
|
|
2904
|
+
log(" 1. Complete the feasibility report", "gray");
|
|
2905
|
+
log(" 2. Validate: smc workflow validate", "gray");
|
|
2906
|
+
log(" 3. Proceed to Phase 2: Claude approval", "gray");
|
|
2907
|
+
log("", "gray");
|
|
2908
|
+
})
|
|
2909
|
+
.catch((err) => {
|
|
2910
|
+
log("", "gray");
|
|
2911
|
+
log(`❌ Error: ${err.message}`, "red");
|
|
2912
|
+
process.exit(1);
|
|
2913
|
+
});
|
|
2591
2914
|
break;
|
|
2592
2915
|
}
|
|
2593
2916
|
|
|
2594
|
-
case
|
|
2595
|
-
const {
|
|
2917
|
+
case "status": {
|
|
2918
|
+
const {
|
|
2919
|
+
getAllProjectsWithPhases,
|
|
2920
|
+
} = require("../.claude/workflow/phases/phase4-develop");
|
|
2596
2921
|
const projects = getAllProjectsWithPhases();
|
|
2597
2922
|
|
|
2598
|
-
log(
|
|
2599
|
-
log(
|
|
2600
|
-
log(
|
|
2601
|
-
log(
|
|
2923
|
+
log("", "gray");
|
|
2924
|
+
log("📋 Workflow Projects", "blue");
|
|
2925
|
+
log("=====================================", "gray");
|
|
2926
|
+
log("", "gray");
|
|
2602
2927
|
|
|
2603
2928
|
if (projects.length === 0) {
|
|
2604
|
-
log(
|
|
2605
|
-
log('Start one with: smc workflow start "<your idea>"',
|
|
2929
|
+
log("No projects yet.", "gray");
|
|
2930
|
+
log('Start one with: smc workflow start "<your idea>"', "gray");
|
|
2606
2931
|
} else {
|
|
2607
|
-
projects.forEach(p => {
|
|
2608
|
-
const phaseNames = [
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2932
|
+
projects.forEach((p) => {
|
|
2933
|
+
const phaseNames = [
|
|
2934
|
+
"",
|
|
2935
|
+
"Research",
|
|
2936
|
+
"Approval",
|
|
2937
|
+
"Planning",
|
|
2938
|
+
"Development",
|
|
2939
|
+
"Deployment",
|
|
2940
|
+
];
|
|
2941
|
+
const phaseIcons = ["", "🔍", "🤝", "📐", "💻", "🚀"];
|
|
2942
|
+
const phaseIcon = phaseIcons[p.currentPhase] || "📋";
|
|
2943
|
+
log(`${phaseIcon} ${p.id}`, "gray");
|
|
2944
|
+
log(
|
|
2945
|
+
` Phase: ${p.currentPhase} - ${phaseNames[p.currentPhase] || "Unknown"}`,
|
|
2946
|
+
"gray",
|
|
2947
|
+
);
|
|
2613
2948
|
if (p.hasPhase1) {
|
|
2614
|
-
log(` ✅ Phase 1: feasibility-report.md`,
|
|
2949
|
+
log(` ✅ Phase 1: feasibility-report.md`, "gray");
|
|
2615
2950
|
}
|
|
2616
2951
|
if (p.hasPhase2) {
|
|
2617
|
-
log(` ✅ Phase 2: requirements.md`,
|
|
2952
|
+
log(` ✅ Phase 2: requirements.md`, "gray");
|
|
2618
2953
|
}
|
|
2619
2954
|
if (p.hasPhase3) {
|
|
2620
|
-
log(` ✅ Phase 3: PRD.md`,
|
|
2955
|
+
log(` ✅ Phase 3: PRD.md`, "gray");
|
|
2621
2956
|
}
|
|
2622
2957
|
if (p.hasPhase4) {
|
|
2623
|
-
log(` ✅ Phase 4: source/`,
|
|
2958
|
+
log(` ✅ Phase 4: source/`, "gray");
|
|
2624
2959
|
}
|
|
2625
2960
|
});
|
|
2626
2961
|
}
|
|
2627
|
-
log(
|
|
2962
|
+
log("", "gray");
|
|
2628
2963
|
break;
|
|
2629
2964
|
}
|
|
2630
2965
|
|
|
2631
|
-
case
|
|
2966
|
+
case "validate": {
|
|
2632
2967
|
const [reportPath] = rest;
|
|
2633
2968
|
|
|
2634
2969
|
if (!reportPath) {
|
|
2635
|
-
log(
|
|
2636
|
-
log(
|
|
2637
|
-
log(
|
|
2638
|
-
log(
|
|
2639
|
-
log(
|
|
2640
|
-
log(
|
|
2641
|
-
log(
|
|
2970
|
+
log("Usage: smc workflow validate <file-path|project-id>", "gray");
|
|
2971
|
+
log("", "gray");
|
|
2972
|
+
log("Validates:", "gray");
|
|
2973
|
+
log(" - Phase 1: feasibility-report.md", "gray");
|
|
2974
|
+
log(" - Phase 2: requirements.md", "gray");
|
|
2975
|
+
log(" - Phase 3: PRD.md or TASK_PLAN.md", "gray");
|
|
2976
|
+
log(" - Phase 4: Pass project-id to validate development", "gray");
|
|
2642
2977
|
return;
|
|
2643
2978
|
}
|
|
2644
2979
|
|
|
2645
2980
|
// Check if it's a project ID (for Phase 4 validation)
|
|
2646
|
-
if (reportPath.startsWith(
|
|
2647
|
-
const {
|
|
2981
|
+
if (reportPath.startsWith("proj_")) {
|
|
2982
|
+
const {
|
|
2983
|
+
DevelopmentValidator,
|
|
2984
|
+
} = require("../.claude/workflow/phases/phase4-develop");
|
|
2648
2985
|
const result = DevelopmentValidator.validateProjectDir(reportPath);
|
|
2649
2986
|
log(DevelopmentValidator.generateReport(result));
|
|
2650
2987
|
process.exit(result.passed ? 0 : 1);
|
|
2651
2988
|
}
|
|
2652
2989
|
|
|
2653
2990
|
// Determine which validator to use based on file name
|
|
2654
|
-
const isPhase3 =
|
|
2655
|
-
|
|
2991
|
+
const isPhase3 =
|
|
2992
|
+
reportPath.includes("PRD") ||
|
|
2993
|
+
reportPath.includes("TASK_PLAN") ||
|
|
2994
|
+
reportPath.includes("phase3");
|
|
2995
|
+
const isPhase2 =
|
|
2996
|
+
reportPath.includes("requirements") || reportPath.includes("phase2");
|
|
2656
2997
|
|
|
2657
2998
|
if (isPhase3) {
|
|
2658
|
-
const {
|
|
2999
|
+
const {
|
|
3000
|
+
PlanningValidator,
|
|
3001
|
+
} = require("../.claude/workflow/phases/phase3-plan");
|
|
2659
3002
|
const result = PlanningValidator.validateFile(reportPath);
|
|
2660
3003
|
log(PlanningValidator.generateReport(result));
|
|
2661
3004
|
process.exit(result.passed ? 0 : 1);
|
|
2662
3005
|
} else if (isPhase2) {
|
|
2663
|
-
const {
|
|
3006
|
+
const {
|
|
3007
|
+
ApprovalValidator,
|
|
3008
|
+
} = require("../.claude/workflow/phases/phase2-approve");
|
|
2664
3009
|
const result = ApprovalValidator.validateFile(reportPath);
|
|
2665
3010
|
log(ApprovalValidator.generateReport(result));
|
|
2666
3011
|
process.exit(result.passed ? 0 : 1);
|
|
2667
3012
|
} else {
|
|
2668
|
-
const {
|
|
3013
|
+
const {
|
|
3014
|
+
FeasibilityValidator,
|
|
3015
|
+
} = require("../.claude/workflow/phases/phase1-research");
|
|
2669
3016
|
const result = FeasibilityValidator.validateFile(reportPath);
|
|
2670
3017
|
log(FeasibilityValidator.generateReport(result));
|
|
2671
3018
|
process.exit(result.passed ? 0 : 1);
|
|
2672
3019
|
}
|
|
2673
3020
|
}
|
|
2674
3021
|
|
|
2675
|
-
case
|
|
3022
|
+
case "phase": {
|
|
2676
3023
|
const [phaseNum] = rest;
|
|
2677
3024
|
const phases = [
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
3025
|
+
"1 - Research (NotebookLM)",
|
|
3026
|
+
"2 - Approval (Claude)",
|
|
3027
|
+
"3 - Planning (Claude)",
|
|
3028
|
+
"4 - Development (Claude)",
|
|
3029
|
+
"5 - Deployment",
|
|
2683
3030
|
];
|
|
2684
3031
|
|
|
2685
3032
|
if (phaseNum) {
|
|
2686
|
-
log(
|
|
3033
|
+
log(
|
|
3034
|
+
`Phase ${phaseNum}: ${phases[parseInt(phaseNum) - 1] || "Unknown"}`,
|
|
3035
|
+
"cyan",
|
|
3036
|
+
);
|
|
2687
3037
|
} else {
|
|
2688
|
-
log(
|
|
2689
|
-
log(
|
|
2690
|
-
log(
|
|
2691
|
-
log(
|
|
3038
|
+
log("", "gray");
|
|
3039
|
+
log("🔄 Workflow Phases", "blue");
|
|
3040
|
+
log("=====================================", "gray");
|
|
3041
|
+
log("", "gray");
|
|
2692
3042
|
phases.forEach((p, i) => {
|
|
2693
|
-
log(` Phase ${i + 1}: ${p}`,
|
|
3043
|
+
log(` Phase ${i + 1}: ${p}`, "gray");
|
|
2694
3044
|
});
|
|
2695
|
-
log(
|
|
3045
|
+
log("", "gray");
|
|
2696
3046
|
}
|
|
2697
3047
|
break;
|
|
2698
3048
|
}
|
|
2699
3049
|
|
|
2700
|
-
case
|
|
2701
|
-
case
|
|
3050
|
+
case "approve":
|
|
3051
|
+
case "next": {
|
|
2702
3052
|
// Determine project ID
|
|
2703
3053
|
let projectId;
|
|
2704
3054
|
|
|
2705
|
-
if (action ===
|
|
3055
|
+
if (action === "approve") {
|
|
2706
3056
|
projectId = rest[0];
|
|
2707
3057
|
} else {
|
|
2708
3058
|
// 'next' - find the latest project that needs the next phase
|
|
2709
|
-
const {
|
|
3059
|
+
const {
|
|
3060
|
+
getAllProjectsWithPhases,
|
|
3061
|
+
} = require("../.claude/workflow/phases/phase4-develop");
|
|
2710
3062
|
const projects = getAllProjectsWithPhases();
|
|
2711
3063
|
|
|
2712
3064
|
// Find project ready for next phase
|
|
2713
|
-
const readyForPhase4 = projects.find(
|
|
2714
|
-
|
|
2715
|
-
|
|
3065
|
+
const readyForPhase4 = projects.find(
|
|
3066
|
+
(p) => p.hasPhase3 && !p.hasPhase4,
|
|
3067
|
+
);
|
|
3068
|
+
const readyForPhase3 = projects.find(
|
|
3069
|
+
(p) => p.hasPhase2 && !p.hasPhase3,
|
|
3070
|
+
);
|
|
3071
|
+
const readyForPhase2 = projects.find(
|
|
3072
|
+
(p) => p.hasPhase1 && !p.hasPhase2,
|
|
3073
|
+
);
|
|
2716
3074
|
|
|
2717
3075
|
if (readyForPhase4) {
|
|
2718
3076
|
projectId = readyForPhase4.id;
|
|
@@ -2721,183 +3079,235 @@ All notable changes to this project will be documented in this file.
|
|
|
2721
3079
|
} else if (readyForPhase2) {
|
|
2722
3080
|
projectId = readyForPhase2.id;
|
|
2723
3081
|
} else if (projects.length === 0) {
|
|
2724
|
-
log(
|
|
3082
|
+
log(
|
|
3083
|
+
'No projects found. Start one with: smc workflow start "<your idea>"',
|
|
3084
|
+
"gray",
|
|
3085
|
+
);
|
|
2725
3086
|
return;
|
|
2726
3087
|
} else {
|
|
2727
|
-
log(
|
|
3088
|
+
log(
|
|
3089
|
+
'All projects are at the latest phase. Start a new project with: smc workflow start "<your idea>"',
|
|
3090
|
+
"yellow",
|
|
3091
|
+
);
|
|
2728
3092
|
return;
|
|
2729
3093
|
}
|
|
2730
3094
|
}
|
|
2731
3095
|
|
|
2732
3096
|
if (!projectId) {
|
|
2733
|
-
log(
|
|
2734
|
-
log(
|
|
3097
|
+
log("Usage: smc workflow approve <project-id>", "gray");
|
|
3098
|
+
log(
|
|
3099
|
+
"Or use: smc workflow next (auto-selects and advances latest project)",
|
|
3100
|
+
"gray",
|
|
3101
|
+
);
|
|
2735
3102
|
return;
|
|
2736
3103
|
}
|
|
2737
3104
|
|
|
2738
3105
|
// Determine which phase to execute
|
|
2739
|
-
const {
|
|
3106
|
+
const {
|
|
3107
|
+
getAllProjectsWithPhases,
|
|
3108
|
+
} = require("../.claude/workflow/phases/phase4-develop");
|
|
2740
3109
|
const projects = getAllProjectsWithPhases();
|
|
2741
|
-
const project = projects.find(p => p.id === projectId);
|
|
3110
|
+
const project = projects.find((p) => p.id === projectId);
|
|
2742
3111
|
|
|
2743
3112
|
if (!project) {
|
|
2744
|
-
log(`Project not found: ${projectId}`,
|
|
3113
|
+
log(`Project not found: ${projectId}`, "red");
|
|
2745
3114
|
return;
|
|
2746
3115
|
}
|
|
2747
3116
|
|
|
2748
3117
|
// Execute the appropriate phase
|
|
2749
3118
|
if (project.hasPhase3 && !project.hasPhase4) {
|
|
2750
3119
|
// Execute Phase 4
|
|
2751
|
-
const {
|
|
3120
|
+
const {
|
|
3121
|
+
Phase4DevelopmentExecutor,
|
|
3122
|
+
} = require("../.claude/workflow/phases/phase4-develop");
|
|
2752
3123
|
|
|
2753
|
-
log(
|
|
2754
|
-
log(
|
|
2755
|
-
log(
|
|
2756
|
-
log(
|
|
2757
|
-
log(`Project: ${projectId}`,
|
|
2758
|
-
log(
|
|
3124
|
+
log("", "gray");
|
|
3125
|
+
log("💻 Starting Phase 4: Development", "blue");
|
|
3126
|
+
log("=====================================", "gray");
|
|
3127
|
+
log("", "gray");
|
|
3128
|
+
log(`Project: ${projectId}`, "cyan");
|
|
3129
|
+
log("", "gray");
|
|
2759
3130
|
|
|
2760
3131
|
const executor = new Phase4DevelopmentExecutor(projectId);
|
|
2761
3132
|
|
|
2762
3133
|
// Check if Phase 3 task plan exists
|
|
2763
3134
|
if (!fs.existsSync(executor.taskPlanPath)) {
|
|
2764
|
-
log(
|
|
2765
|
-
|
|
3135
|
+
log(
|
|
3136
|
+
`❌ Phase 3 task plan not found: ${executor.taskPlanPath}`,
|
|
3137
|
+
"red",
|
|
3138
|
+
);
|
|
3139
|
+
log("Complete Phase 3 first.", "yellow");
|
|
2766
3140
|
process.exit(1);
|
|
2767
3141
|
}
|
|
2768
3142
|
|
|
2769
|
-
executor
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
3143
|
+
executor
|
|
3144
|
+
.execute((msg, current, total) => {
|
|
3145
|
+
const progress = Math.round((current / total) * 100);
|
|
3146
|
+
log(`[${progress}%] ${msg}`, "gray");
|
|
3147
|
+
})
|
|
3148
|
+
.then((result) => {
|
|
3149
|
+
log("", "gray");
|
|
3150
|
+
log("✅ Phase 4 initialized!", "green");
|
|
3151
|
+
log(` Source: ${result.sourceDir}`, "gray");
|
|
3152
|
+
log(` Tasks: ${result.tasksPath}`, "gray");
|
|
3153
|
+
log(` Dev Log: ${result.devLogPath}`, "gray");
|
|
3154
|
+
log("", "gray");
|
|
3155
|
+
log("Next steps:", "gray");
|
|
3156
|
+
log(" 1. Review the generated scaffold", "gray");
|
|
3157
|
+
log(" 2. Implement tasks according to TASK_PLAN.md", "gray");
|
|
3158
|
+
log(" 3. Write tests for each feature", "gray");
|
|
3159
|
+
log(" 4. Update task progress in TASKS.md", "gray");
|
|
3160
|
+
log(" 5. Validate: smc workflow validate " + projectId, "gray");
|
|
3161
|
+
log("", "gray");
|
|
3162
|
+
})
|
|
3163
|
+
.catch((err) => {
|
|
3164
|
+
log("", "gray");
|
|
3165
|
+
log(`❌ Error: ${err.message}`, "red");
|
|
3166
|
+
process.exit(1);
|
|
3167
|
+
});
|
|
2792
3168
|
} else if (project.hasPhase2 && !project.hasPhase3) {
|
|
2793
3169
|
// Execute Phase 3
|
|
2794
|
-
const {
|
|
3170
|
+
const {
|
|
3171
|
+
Phase3PlanningExecutor,
|
|
3172
|
+
} = require("../.claude/workflow/phases/phase3-plan");
|
|
2795
3173
|
|
|
2796
|
-
log(
|
|
2797
|
-
log(
|
|
2798
|
-
log(
|
|
2799
|
-
log(
|
|
2800
|
-
log(`Project: ${projectId}`,
|
|
2801
|
-
log(
|
|
3174
|
+
log("", "gray");
|
|
3175
|
+
log("📐 Starting Phase 3: Planning", "blue");
|
|
3176
|
+
log("=====================================", "gray");
|
|
3177
|
+
log("", "gray");
|
|
3178
|
+
log(`Project: ${projectId}`, "cyan");
|
|
3179
|
+
log("", "gray");
|
|
2802
3180
|
|
|
2803
3181
|
const executor = new Phase3PlanningExecutor(projectId);
|
|
2804
3182
|
|
|
2805
3183
|
// Check if Phase 2 requirements exist
|
|
2806
3184
|
if (!fs.existsSync(executor.phase2RequirementsPath)) {
|
|
2807
|
-
log(
|
|
2808
|
-
|
|
3185
|
+
log(
|
|
3186
|
+
`❌ Phase 2 requirements not found: ${executor.phase2RequirementsPath}`,
|
|
3187
|
+
"red",
|
|
3188
|
+
);
|
|
3189
|
+
log("Complete Phase 2 first.", "yellow");
|
|
2809
3190
|
process.exit(1);
|
|
2810
3191
|
}
|
|
2811
3192
|
|
|
2812
|
-
executor
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
3193
|
+
executor
|
|
3194
|
+
.execute((msg, current, total) => {
|
|
3195
|
+
const progress = Math.round((current / total) * 100);
|
|
3196
|
+
log(`[${progress}%] ${msg}`, "gray");
|
|
3197
|
+
})
|
|
3198
|
+
.then((result) => {
|
|
3199
|
+
log("", "gray");
|
|
3200
|
+
log("✅ Phase 3 initialized!", "green");
|
|
3201
|
+
log(` PRD: ${result.prdPath}`, "gray");
|
|
3202
|
+
log(` Task Plan: ${result.taskPlanPath}`, "gray");
|
|
3203
|
+
log(` Prototype: ${result.prototypeDir}`, "gray");
|
|
3204
|
+
log("", "gray");
|
|
3205
|
+
log("Next steps:", "gray");
|
|
3206
|
+
log(" 1. Review and complete the PRD", "gray");
|
|
3207
|
+
log(" 2. Review and adjust the task plan", "gray");
|
|
3208
|
+
log(" 3. Create prototypes/proofs-of-concept", "gray");
|
|
3209
|
+
log(
|
|
3210
|
+
" 4. Validate: smc workflow validate " + result.prdPath,
|
|
3211
|
+
"gray",
|
|
3212
|
+
);
|
|
3213
|
+
log(" 5. Proceed to Phase 4", "gray");
|
|
3214
|
+
log("", "gray");
|
|
3215
|
+
})
|
|
3216
|
+
.catch((err) => {
|
|
3217
|
+
log("", "gray");
|
|
3218
|
+
log(`❌ Error: ${err.message}`, "red");
|
|
3219
|
+
process.exit(1);
|
|
3220
|
+
});
|
|
2835
3221
|
} else {
|
|
2836
3222
|
// Execute Phase 2 (original logic)
|
|
2837
|
-
const {
|
|
3223
|
+
const {
|
|
3224
|
+
Phase2ApprovalExecutor,
|
|
3225
|
+
} = require("../.claude/workflow/phases/phase2-approve");
|
|
2838
3226
|
|
|
2839
|
-
log(
|
|
2840
|
-
log(
|
|
2841
|
-
log(
|
|
2842
|
-
log(
|
|
2843
|
-
log(`Project: ${projectId}`,
|
|
2844
|
-
log(
|
|
3227
|
+
log("", "gray");
|
|
3228
|
+
log("🤝 Starting Phase 2: Approval", "blue");
|
|
3229
|
+
log("=====================================", "gray");
|
|
3230
|
+
log("", "gray");
|
|
3231
|
+
log(`Project: ${projectId}`, "cyan");
|
|
3232
|
+
log("", "gray");
|
|
2845
3233
|
|
|
2846
3234
|
const executor = new Phase2ApprovalExecutor(projectId);
|
|
2847
3235
|
|
|
2848
3236
|
// Check if Phase 1 report exists
|
|
2849
3237
|
if (!fs.existsSync(executor.phase1ReportPath)) {
|
|
2850
|
-
log(
|
|
2851
|
-
|
|
3238
|
+
log(
|
|
3239
|
+
`❌ Phase 1 report not found: ${executor.phase1ReportPath}`,
|
|
3240
|
+
"red",
|
|
3241
|
+
);
|
|
3242
|
+
log("Complete Phase 1 first.", "yellow");
|
|
2852
3243
|
process.exit(1);
|
|
2853
3244
|
}
|
|
2854
3245
|
|
|
2855
|
-
executor
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
3246
|
+
executor
|
|
3247
|
+
.execute((msg, current, total) => {
|
|
3248
|
+
const progress = Math.round((current / total) * 100);
|
|
3249
|
+
log(`[${progress}%] ${msg}`, "gray");
|
|
3250
|
+
})
|
|
3251
|
+
.then((result) => {
|
|
3252
|
+
log("", "gray");
|
|
3253
|
+
log("✅ Phase 2 initialized!", "green");
|
|
3254
|
+
log(` Requirements: ${result.requirementsPath}`, "gray");
|
|
3255
|
+
log("", "gray");
|
|
3256
|
+
log("Next steps:", "gray");
|
|
3257
|
+
log(" 1. Review and answer clarification questions", "gray");
|
|
3258
|
+
log(
|
|
3259
|
+
" 2. Define functional requirements with acceptance criteria",
|
|
3260
|
+
"gray",
|
|
3261
|
+
);
|
|
3262
|
+
log(
|
|
3263
|
+
" 3. Validate: smc workflow validate " +
|
|
3264
|
+
result.requirementsPath,
|
|
3265
|
+
"gray",
|
|
3266
|
+
);
|
|
3267
|
+
log(" 4. Proceed to Phase 3", "gray");
|
|
3268
|
+
log("", "gray");
|
|
3269
|
+
})
|
|
3270
|
+
.catch((err) => {
|
|
3271
|
+
log("", "gray");
|
|
3272
|
+
log(`❌ Error: ${err.message}`, "red");
|
|
3273
|
+
process.exit(1);
|
|
3274
|
+
});
|
|
2874
3275
|
}
|
|
2875
3276
|
break;
|
|
2876
3277
|
}
|
|
2877
3278
|
|
|
2878
3279
|
default:
|
|
2879
|
-
log(
|
|
2880
|
-
log(
|
|
2881
|
-
log(
|
|
2882
|
-
log(
|
|
2883
|
-
log(
|
|
2884
|
-
log(
|
|
2885
|
-
log(
|
|
2886
|
-
log(
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
log(
|
|
2891
|
-
log(
|
|
2892
|
-
log(
|
|
2893
|
-
log(
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
log(
|
|
2898
|
-
log(
|
|
2899
|
-
log(
|
|
2900
|
-
log('',
|
|
3280
|
+
log("", "gray");
|
|
3281
|
+
log("🔄 Workflow Commands", "blue");
|
|
3282
|
+
log("=====================================", "gray");
|
|
3283
|
+
log("", "gray");
|
|
3284
|
+
log("Usage: smc workflow <action> [args...]", "gray");
|
|
3285
|
+
log("", "gray");
|
|
3286
|
+
log("Actions:", "gray");
|
|
3287
|
+
log(
|
|
3288
|
+
" start <idea> Start a new project workflow (Phase 1)",
|
|
3289
|
+
"gray",
|
|
3290
|
+
);
|
|
3291
|
+
log(" approve <id> Start Phase 2 approval for a project", "gray");
|
|
3292
|
+
log(" next Auto-advance to next phase", "gray");
|
|
3293
|
+
log(" status Show all projects and their phases", "gray");
|
|
3294
|
+
log(
|
|
3295
|
+
" validate <file> Validate a report (feasibility or requirements)",
|
|
3296
|
+
"gray",
|
|
3297
|
+
);
|
|
3298
|
+
log(" phase [n] Show phase information", "gray");
|
|
3299
|
+
log("", "gray");
|
|
3300
|
+
log("Examples:", "gray");
|
|
3301
|
+
log(' smc workflow start "Build a REST API"', "gray");
|
|
3302
|
+
log(" smc workflow approve proj_abc123", "gray");
|
|
3303
|
+
log(" smc workflow next", "gray");
|
|
3304
|
+
log(" smc workflow status", "gray");
|
|
3305
|
+
log(
|
|
3306
|
+
" smc workflow validate development/projects/xxx/phase1/feasibility-report.md",
|
|
3307
|
+
"gray",
|
|
3308
|
+
);
|
|
3309
|
+
log(" smc workflow phase 1", "gray");
|
|
3310
|
+
log("", "gray");
|
|
2901
3311
|
}
|
|
2902
3312
|
},
|
|
2903
3313
|
|
|
@@ -2906,7 +3316,9 @@ All notable changes to this project will be documented in this file.
|
|
|
2906
3316
|
// ==========================================================================
|
|
2907
3317
|
|
|
2908
3318
|
knowledge: async (...args) => {
|
|
2909
|
-
const {
|
|
3319
|
+
const {
|
|
3320
|
+
handleKnowledgeCommand,
|
|
3321
|
+
} = require("../.claude/workflow/knowledge-engine");
|
|
2910
3322
|
await handleKnowledgeCommand(args);
|
|
2911
3323
|
},
|
|
2912
3324
|
|
|
@@ -2915,9 +3327,11 @@ All notable changes to this project will be documented in this file.
|
|
|
2915
3327
|
// ==========================================================================
|
|
2916
3328
|
|
|
2917
3329
|
notebooklm: async (...args) => {
|
|
2918
|
-
const {
|
|
3330
|
+
const {
|
|
3331
|
+
handleNotebookLMCommand,
|
|
3332
|
+
} = require("../.claude/workflow/notebooklm/browser");
|
|
2919
3333
|
await handleNotebookLMCommand(args);
|
|
2920
|
-
}
|
|
3334
|
+
},
|
|
2921
3335
|
};
|
|
2922
3336
|
|
|
2923
3337
|
// ============================================================================
|
|
@@ -2930,7 +3344,7 @@ function generateAgentsMd(config) {
|
|
|
2930
3344
|
const model = agent.model || config.model;
|
|
2931
3345
|
return `### ${name}\n- **Model**: ${model}\n- **Role**: ${agent.role}`;
|
|
2932
3346
|
})
|
|
2933
|
-
.join(
|
|
3347
|
+
.join("\n\n");
|
|
2934
3348
|
|
|
2935
3349
|
return `# AGENTS
|
|
2936
3350
|
|
|
@@ -2971,9 +3385,14 @@ sumulige-claude skill:list
|
|
|
2971
3385
|
async function runCommand(cmd, args) {
|
|
2972
3386
|
const command = commands[cmd];
|
|
2973
3387
|
if (command) {
|
|
2974
|
-
await command(...args);
|
|
3388
|
+
await command(...(args || []));
|
|
2975
3389
|
}
|
|
2976
3390
|
}
|
|
2977
3391
|
|
|
2978
3392
|
exports.runCommand = runCommand;
|
|
2979
3393
|
exports.commands = commands;
|
|
3394
|
+
// Export helper functions for testing
|
|
3395
|
+
exports.countFiles = countFiles;
|
|
3396
|
+
exports.copySingleFile = copySingleFile;
|
|
3397
|
+
exports.setExecutablePermission = setExecutablePermission;
|
|
3398
|
+
exports.generateAgentsMd = generateAgentsMd;
|