sumulige-claude 1.5.1 → 1.5.2

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