sumulige-claude 1.0.5 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/.kickoff-hint.txt +51 -0
- package/.claude/ANCHORS.md +40 -0
- package/.claude/MEMORY.md +34 -0
- package/.claude/PROJECT_LOG.md +101 -0
- package/.claude/THINKING_CHAIN_GUIDE.md +287 -0
- package/.claude/commands/commit-push-pr.md +59 -0
- package/.claude/commands/commit.md +53 -0
- package/.claude/commands/pr.md +76 -0
- package/.claude/commands/review.md +61 -0
- package/.claude/commands/sessions.md +62 -0
- package/.claude/commands/skill-create.md +131 -0
- package/.claude/commands/test.md +56 -0
- package/.claude/commands/todos.md +99 -0
- package/.claude/commands/verify-work.md +63 -0
- package/.claude/hooks/code-formatter.cjs +187 -0
- package/.claude/hooks/code-tracer.cjs +331 -0
- package/.claude/hooks/conversation-recorder.cjs +340 -0
- package/.claude/hooks/decision-tracker.cjs +398 -0
- package/.claude/hooks/export.cjs +329 -0
- package/.claude/hooks/multi-session.cjs +181 -0
- package/.claude/hooks/privacy-filter.js +224 -0
- package/.claude/hooks/project-kickoff.cjs +114 -0
- package/.claude/hooks/rag-skill-loader.cjs +159 -0
- package/.claude/hooks/session-end.sh +61 -0
- package/.claude/hooks/sync-to-log.sh +83 -0
- package/.claude/hooks/thinking-silent.cjs +145 -0
- package/.claude/hooks/tl-summary.sh +54 -0
- package/.claude/hooks/todo-manager.cjs +248 -0
- package/.claude/hooks/verify-work.cjs +134 -0
- package/.claude/sessions/active-sessions.json +444 -0
- package/.claude/settings.local.json +36 -2
- package/.claude/thinking-routes/.last-sync +1 -0
- package/.claude/thinking-routes/QUICKREF.md +98 -0
- package/CHANGELOG.md +56 -0
- package/DEV_TOOLS_GUIDE.md +190 -0
- package/PROJECT_STRUCTURE.md +10 -1
- package/README.md +20 -6
- package/cli.js +85 -824
- package/config/defaults.json +34 -0
- package/development/todos/INDEX.md +14 -58
- package/lib/commands.js +698 -0
- package/lib/config.js +71 -0
- package/lib/utils.js +62 -0
- package/package.json +2 -2
package/lib/commands.js
ADDED
|
@@ -0,0 +1,698 @@
|
|
|
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 { loadConfig, CONFIG_DIR, CONFIG_FILE, SKILLS_DIR, ensureDir, saveConfig } = require('./config');
|
|
11
|
+
const { copyRecursive, toTitleCase } = require('./utils');
|
|
12
|
+
|
|
13
|
+
const TEMPLATE_DIR = path.join(__dirname, '../template');
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Commands
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
const commands = {
|
|
20
|
+
// -------------------------------------------------------------------------
|
|
21
|
+
init: () => {
|
|
22
|
+
console.log('🚀 Initializing Sumulige Claude...');
|
|
23
|
+
|
|
24
|
+
// Create config directory
|
|
25
|
+
ensureDir(CONFIG_DIR);
|
|
26
|
+
|
|
27
|
+
// Create config file
|
|
28
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
29
|
+
saveConfig(loadConfig());
|
|
30
|
+
console.log('✅ Created config:', CONFIG_FILE);
|
|
31
|
+
} else {
|
|
32
|
+
console.log('ℹ️ Config already exists:', CONFIG_FILE);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Create skills directory
|
|
36
|
+
ensureDir(SKILLS_DIR);
|
|
37
|
+
console.log('✅ Created skills directory:', SKILLS_DIR);
|
|
38
|
+
|
|
39
|
+
// Install openskills if not installed
|
|
40
|
+
try {
|
|
41
|
+
execSync('openskills --version', { stdio: 'ignore' });
|
|
42
|
+
console.log('✅ OpenSkills already installed');
|
|
43
|
+
} catch {
|
|
44
|
+
console.log('📦 Installing OpenSkills...');
|
|
45
|
+
try {
|
|
46
|
+
execSync('npm i -g openskills', { stdio: 'inherit' });
|
|
47
|
+
console.log('✅ OpenSkills installed');
|
|
48
|
+
} catch (e) {
|
|
49
|
+
console.log('⚠️ Failed to install OpenSkills. Run: npm i -g openskills');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log('');
|
|
54
|
+
console.log('🎉 Sumulige Claude initialized!');
|
|
55
|
+
console.log('');
|
|
56
|
+
console.log('Next steps:');
|
|
57
|
+
console.log(' sumulige-claude sync # Sync to current project');
|
|
58
|
+
console.log(' sumulige-claude agent # Run agent orchestration');
|
|
59
|
+
console.log(' sumulige-claude status # Show configuration');
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
// -------------------------------------------------------------------------
|
|
63
|
+
sync: () => {
|
|
64
|
+
console.log('🔄 Syncing Sumulige Claude to current project...');
|
|
65
|
+
|
|
66
|
+
const projectDir = process.cwd();
|
|
67
|
+
const projectConfigDir = path.join(projectDir, '.claude');
|
|
68
|
+
const agentsFile = path.join(projectConfigDir, 'AGENTS.md');
|
|
69
|
+
const readmeFile = path.join(projectConfigDir, 'README.md');
|
|
70
|
+
const templateReadme = path.join(TEMPLATE_DIR, '.claude', 'README.md');
|
|
71
|
+
|
|
72
|
+
// Create .claude directory
|
|
73
|
+
ensureDir(projectConfigDir);
|
|
74
|
+
console.log('✅ Created .claude directory');
|
|
75
|
+
|
|
76
|
+
// Sync config
|
|
77
|
+
const config = loadConfig();
|
|
78
|
+
|
|
79
|
+
// Generate AGENTS.md
|
|
80
|
+
const agentsMd = generateAgentsMd(config);
|
|
81
|
+
fs.writeFileSync(agentsFile, agentsMd);
|
|
82
|
+
console.log('✅ Created AGENTS.md');
|
|
83
|
+
|
|
84
|
+
// Silently sync README.md if template updated
|
|
85
|
+
if (fs.existsSync(templateReadme)) {
|
|
86
|
+
const templateContent = fs.readFileSync(templateReadme, 'utf-8');
|
|
87
|
+
let needsUpdate = true;
|
|
88
|
+
|
|
89
|
+
if (fs.existsSync(readmeFile)) {
|
|
90
|
+
const existingContent = fs.readFileSync(readmeFile, 'utf-8');
|
|
91
|
+
const templateVersion = templateContent.match(/@version:\s*(\d+\.\d+\.\d+)/)?.[1] || '0.0.0';
|
|
92
|
+
const existingVersion = existingContent.match(/@version:\s*(\d+\.\d+\.\d+)/)?.[1] || '0.0.0';
|
|
93
|
+
needsUpdate = templateVersion !== existingVersion;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (needsUpdate) {
|
|
97
|
+
fs.writeFileSync(readmeFile, templateContent);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Sync todos directory structure
|
|
102
|
+
const todosTemplateDir = path.join(TEMPLATE_DIR, 'development', 'todos');
|
|
103
|
+
const todosProjectDir = path.join(projectDir, 'development', 'todos');
|
|
104
|
+
|
|
105
|
+
if (fs.existsSync(todosTemplateDir)) {
|
|
106
|
+
copyRecursive(todosTemplateDir, todosProjectDir);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Sync skills
|
|
110
|
+
try {
|
|
111
|
+
execSync('openskills sync -y', { stdio: 'pipe' });
|
|
112
|
+
console.log('✅ Synced skills');
|
|
113
|
+
} catch (e) {
|
|
114
|
+
console.log('⚠️ Failed to sync skills');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
console.log('');
|
|
118
|
+
console.log('✅ Sync complete!');
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
// -------------------------------------------------------------------------
|
|
122
|
+
agent: (task) => {
|
|
123
|
+
if (!task) {
|
|
124
|
+
console.log('Usage: sumulige-claude agent <task>');
|
|
125
|
+
console.log('');
|
|
126
|
+
console.log('Example: sumulige-claude agent "Build a React dashboard"');
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const config = loadConfig();
|
|
131
|
+
console.log('🤖 Starting Agent Orchestration...');
|
|
132
|
+
console.log('');
|
|
133
|
+
console.log('Task:', task);
|
|
134
|
+
console.log('');
|
|
135
|
+
console.log('Available Agents:');
|
|
136
|
+
Object.entries(config.agents).forEach(([name, agent]) => {
|
|
137
|
+
const model = agent.model || config.model;
|
|
138
|
+
console.log(` - ${name}: ${model} (${agent.role})`);
|
|
139
|
+
});
|
|
140
|
+
console.log('');
|
|
141
|
+
console.log('💡 In Claude Code, use /skill <name> to invoke specific agent capabilities');
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
// -------------------------------------------------------------------------
|
|
145
|
+
status: () => {
|
|
146
|
+
const config = loadConfig();
|
|
147
|
+
console.log('📊 Sumulige Claude Status');
|
|
148
|
+
console.log('');
|
|
149
|
+
console.log('Config:', CONFIG_FILE);
|
|
150
|
+
console.log('');
|
|
151
|
+
console.log('Agents:');
|
|
152
|
+
Object.entries(config.agents).forEach(([name, agent]) => {
|
|
153
|
+
const model = agent.model || config.model;
|
|
154
|
+
console.log(` ${name.padEnd(12)} ${model.padEnd(20)} (${agent.role})`);
|
|
155
|
+
});
|
|
156
|
+
console.log('');
|
|
157
|
+
console.log('Skills:', config.skills.join(', '));
|
|
158
|
+
console.log('');
|
|
159
|
+
console.log('ThinkingLens:', config.thinkingLens.enabled ? '✅ Enabled' : '❌ Disabled');
|
|
160
|
+
console.log('');
|
|
161
|
+
|
|
162
|
+
// Show project todos status
|
|
163
|
+
const projectDir = process.cwd();
|
|
164
|
+
const todosIndex = path.join(projectDir, 'development', 'todos', 'INDEX.md');
|
|
165
|
+
|
|
166
|
+
if (fs.existsSync(todosIndex)) {
|
|
167
|
+
const content = fs.readFileSync(todosIndex, 'utf-8');
|
|
168
|
+
|
|
169
|
+
const totalMatch = content.match(/Total:\s+`([^`]+)`\s+(\d+)%/);
|
|
170
|
+
const p0Match = content.match(/P0[^`]*`([^`]+)`\s+(\d+)%\s+\((\d+)\/(\d+)\)/);
|
|
171
|
+
const p1Match = content.match(/P1[^`]*`([^`]+)`\s+(\d+)%\s+\((\d+)\/(\d+)\)/);
|
|
172
|
+
const p2Match = content.match(/P2[^`]*`([^`]+)`\s+(\d+)%\s+\((\d+)\/(\d+)\)/);
|
|
173
|
+
|
|
174
|
+
const activeMatch = content.match(/\|\s+🚧 进行中[^|]*\|\s+`active\/`\s+\|\s+(\d+)/);
|
|
175
|
+
const completedMatch = content.match(/\|\s+✅ 已完成[^|]*\|\s+`completed\/`\s+\|\s+(\d+)/);
|
|
176
|
+
const backlogMatch = content.match(/\|\s+📋 待办[^|]*\|\s+`backlog\/`\s+\|\s+(\d+)/);
|
|
177
|
+
|
|
178
|
+
console.log('📋 Project Tasks:');
|
|
179
|
+
console.log('');
|
|
180
|
+
if (totalMatch) {
|
|
181
|
+
console.log(` Total: ${totalMatch[1]} ${totalMatch[2]}%`);
|
|
182
|
+
}
|
|
183
|
+
if (p0Match) {
|
|
184
|
+
console.log(` P0: ${p0Match[1]} ${p0Match[2]}% (${p0Match[3]}/${p0Match[4]})`);
|
|
185
|
+
}
|
|
186
|
+
if (p1Match) {
|
|
187
|
+
console.log(` P1: ${p1Match[1]} ${p1Match[2]}% (${p1Match[3]}/${p1Match[4]})`);
|
|
188
|
+
}
|
|
189
|
+
if (p2Match) {
|
|
190
|
+
console.log(` P2: ${p2Match[1]} ${p2Match[2]}% (${p2Match[3]}/${p2Match[4]})`);
|
|
191
|
+
}
|
|
192
|
+
console.log('');
|
|
193
|
+
console.log(` 🚧 Active: ${activeMatch ? activeMatch[1] : 0}`);
|
|
194
|
+
console.log(` ✅ Completed: ${completedMatch ? completedMatch[1] : 0}`);
|
|
195
|
+
console.log(` 📋 Backlog: ${backlogMatch ? backlogMatch[1] : 0}`);
|
|
196
|
+
console.log('');
|
|
197
|
+
console.log(` View: cat development/todos/INDEX.md`);
|
|
198
|
+
} else {
|
|
199
|
+
console.log('📋 Project Tasks: (not initialized)');
|
|
200
|
+
console.log(' Run: node .claude/hooks/todo-manager.cjs --force');
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
// -------------------------------------------------------------------------
|
|
205
|
+
'skill:list': () => {
|
|
206
|
+
try {
|
|
207
|
+
const result = execSync('openskills list', { encoding: 'utf-8' });
|
|
208
|
+
console.log(result);
|
|
209
|
+
} catch (e) {
|
|
210
|
+
console.log('⚠️ OpenSkills not installed. Run: npm i -g openskills');
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
// -------------------------------------------------------------------------
|
|
215
|
+
'skill:create': (skillName) => {
|
|
216
|
+
if (!skillName) {
|
|
217
|
+
console.log('Usage: sumulige-claude skill:create <skill-name>');
|
|
218
|
+
console.log('');
|
|
219
|
+
console.log('Example: sumulige-claude skill:create api-tester');
|
|
220
|
+
console.log('');
|
|
221
|
+
console.log('The skill will be created at:');
|
|
222
|
+
console.log(' .claude/skills/<skill-name>/');
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Validate skill name (kebab-case)
|
|
227
|
+
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(skillName)) {
|
|
228
|
+
console.log('❌ Invalid skill name. Use kebab-case (e.g., api-tester, code-reviewer)');
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const projectDir = process.cwd();
|
|
233
|
+
const skillsDir = path.join(projectDir, '.claude', 'skills');
|
|
234
|
+
const skillDir = path.join(skillsDir, skillName);
|
|
235
|
+
const templateDir = path.join(TEMPLATE_DIR, '.claude', 'skills', 'template');
|
|
236
|
+
|
|
237
|
+
// Check if skill already exists
|
|
238
|
+
if (fs.existsSync(skillDir)) {
|
|
239
|
+
console.log(`⚠️ Skill "${skillName}" already exists at ${skillDir}`);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
console.log(`📝 Creating skill: ${skillName}`);
|
|
244
|
+
console.log('');
|
|
245
|
+
|
|
246
|
+
// Create skill directory structure
|
|
247
|
+
fs.mkdirSync(path.join(skillDir, 'templates'), { recursive: true });
|
|
248
|
+
fs.mkdirSync(path.join(skillDir, 'examples'), { recursive: true });
|
|
249
|
+
console.log('✅ Created directory structure');
|
|
250
|
+
|
|
251
|
+
// Copy template files
|
|
252
|
+
if (fs.existsSync(templateDir)) {
|
|
253
|
+
const skillTemplate = fs.readFileSync(path.join(templateDir, 'SKILL.md'), 'utf-8');
|
|
254
|
+
const metadataTemplate = fs.readFileSync(path.join(templateDir, 'metadata.yaml'), 'utf-8');
|
|
255
|
+
|
|
256
|
+
// Replace placeholders
|
|
257
|
+
const date = new Date().toISOString().split('T')[0];
|
|
258
|
+
let skillContent = skillTemplate
|
|
259
|
+
.replace(/Skill Name/g, toTitleCase(skillName.replace(/-/g, ' ')))
|
|
260
|
+
.replace(/{current-date}/g, date)
|
|
261
|
+
.replace(/skill-name/g, skillName);
|
|
262
|
+
|
|
263
|
+
let metadataContent = metadataTemplate
|
|
264
|
+
.replace(/skill-name/g, skillName);
|
|
265
|
+
|
|
266
|
+
fs.writeFileSync(path.join(skillDir, 'SKILL.md'), skillContent);
|
|
267
|
+
fs.writeFileSync(path.join(skillDir, 'metadata.yaml'), metadataContent);
|
|
268
|
+
console.log('✅ Created SKILL.md and metadata.yaml');
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Create example templates
|
|
272
|
+
fs.writeFileSync(
|
|
273
|
+
path.join(skillDir, 'templates', 'default.md'),
|
|
274
|
+
`# Default Template for ${skillName}\n\nReplace this with your actual template.\n`
|
|
275
|
+
);
|
|
276
|
+
fs.writeFileSync(
|
|
277
|
+
path.join(skillDir, 'examples', 'basic.md'),
|
|
278
|
+
`# Basic Example for ${skillName}\n\nReplace this with your actual example.\n`
|
|
279
|
+
);
|
|
280
|
+
console.log('✅ Created templates and examples');
|
|
281
|
+
|
|
282
|
+
// Update RAG index
|
|
283
|
+
const ragDir = path.join(projectDir, '.claude', 'rag');
|
|
284
|
+
const ragIndexFile = path.join(ragDir, 'skill-index.json');
|
|
285
|
+
let ragIndex = { skills: [], auto_load: { enabled: true } };
|
|
286
|
+
|
|
287
|
+
ensureDir(ragDir);
|
|
288
|
+
|
|
289
|
+
if (fs.existsSync(ragIndexFile)) {
|
|
290
|
+
try {
|
|
291
|
+
ragIndex = JSON.parse(fs.readFileSync(ragIndexFile, 'utf-8'));
|
|
292
|
+
} catch (e) { }
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Add new skill to index
|
|
296
|
+
const newSkill = {
|
|
297
|
+
name: skillName,
|
|
298
|
+
description: `TODO: Add description for ${skillName}`,
|
|
299
|
+
keywords: [skillName.replace(/-/g, ' ')],
|
|
300
|
+
path: `.claude/skills/${skillName}/SKILL.md`
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// Avoid duplicates
|
|
304
|
+
if (!ragIndex.skills.some(s => s.name === skillName)) {
|
|
305
|
+
ragIndex.skills.push(newSkill);
|
|
306
|
+
fs.writeFileSync(ragIndexFile, JSON.stringify(ragIndex, null, 2));
|
|
307
|
+
console.log('✅ Updated RAG skill index');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
console.log('');
|
|
311
|
+
console.log('✅ Skill created successfully!');
|
|
312
|
+
console.log('');
|
|
313
|
+
console.log(`Next steps:`);
|
|
314
|
+
console.log(` 1. Edit .claude/skills/${skillName}/SKILL.md`);
|
|
315
|
+
console.log(` 2. Add your templates and examples`);
|
|
316
|
+
console.log(` 3. Use in Claude Code: /skill ${skillName}`);
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
// -------------------------------------------------------------------------
|
|
320
|
+
'skill:check': (skillName) => {
|
|
321
|
+
const projectDir = process.cwd();
|
|
322
|
+
const skillsDir = path.join(projectDir, '.claude', 'skills');
|
|
323
|
+
|
|
324
|
+
console.log('🔍 Checking skill dependencies...');
|
|
325
|
+
console.log('');
|
|
326
|
+
|
|
327
|
+
const checkSkill = (name, visited = new Set()) => {
|
|
328
|
+
if (visited.has(name)) {
|
|
329
|
+
console.log(`⚠️ Circular dependency detected: ${name}`);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
visited.add(name);
|
|
333
|
+
|
|
334
|
+
const skillDir = path.join(skillsDir, name);
|
|
335
|
+
const metadataFile = path.join(skillDir, 'metadata.yaml');
|
|
336
|
+
|
|
337
|
+
if (!fs.existsSync(skillDir)) {
|
|
338
|
+
console.log(`❌ Skill "${name}" not found`);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (!fs.existsSync(metadataFile)) {
|
|
343
|
+
console.log(`ℹ️ ${name}: No metadata.yaml`);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Simple YAML parser (basic key: value format only)
|
|
348
|
+
const parseSimpleYaml = (content) => {
|
|
349
|
+
const result = {};
|
|
350
|
+
content.split('\n').forEach(line => {
|
|
351
|
+
const match = line.match(/^(\w+):\s*(.*)$/);
|
|
352
|
+
if (match) {
|
|
353
|
+
const value = match[2].trim();
|
|
354
|
+
if (value === '[]') {
|
|
355
|
+
result[match[1]] = [];
|
|
356
|
+
} else if (value.startsWith('[')) {
|
|
357
|
+
try {
|
|
358
|
+
result[match[1]] = JSON.parse(value.replace(/'/g, '"'));
|
|
359
|
+
} catch (e) {
|
|
360
|
+
result[match[1]] = [];
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
result[match[1]] = value;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
return result;
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
const metadata = parseSimpleYaml(fs.readFileSync(metadataFile, 'utf-8'));
|
|
371
|
+
const deps = metadata.dependencies || [];
|
|
372
|
+
|
|
373
|
+
if (deps.length === 0) {
|
|
374
|
+
console.log(`✅ ${name}: No dependencies`);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
console.log(`📦 ${name} depends on:`);
|
|
379
|
+
deps.forEach(dep => {
|
|
380
|
+
const depDir = path.join(skillsDir, dep);
|
|
381
|
+
if (fs.existsSync(depDir)) {
|
|
382
|
+
console.log(` ✅ ${dep}`);
|
|
383
|
+
checkSkill(dep, new Set(visited));
|
|
384
|
+
} else {
|
|
385
|
+
console.log(` ❌ ${dep} (missing)`);
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
if (skillName) {
|
|
391
|
+
checkSkill(skillName);
|
|
392
|
+
} else {
|
|
393
|
+
// Check all skills
|
|
394
|
+
const allSkills = fs.existsSync(skillsDir)
|
|
395
|
+
? fs.readdirSync(skillsDir).filter(f => {
|
|
396
|
+
const dir = path.join(skillsDir, f);
|
|
397
|
+
return fs.statSync(dir).isDirectory() && f !== 'template' && f !== 'examples';
|
|
398
|
+
})
|
|
399
|
+
: [];
|
|
400
|
+
|
|
401
|
+
console.log(`Found ${allSkills.length} skills\n`);
|
|
402
|
+
allSkills.forEach(skill => checkSkill(skill));
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
|
|
406
|
+
// -------------------------------------------------------------------------
|
|
407
|
+
'skill:install': (source) => {
|
|
408
|
+
if (!source) {
|
|
409
|
+
console.log('Usage: sumulige-claude skill:install <source>');
|
|
410
|
+
console.log('Example: sumulige-claude skill:install anthropics/skills');
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
try {
|
|
414
|
+
execSync(`openskills install ${source} -y`, { stdio: 'inherit' });
|
|
415
|
+
execSync('openskills sync -y', { stdio: 'pipe' });
|
|
416
|
+
console.log('✅ Skill installed and synced');
|
|
417
|
+
} catch (e) {
|
|
418
|
+
console.log('❌ Failed to install skill');
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
|
|
422
|
+
// -------------------------------------------------------------------------
|
|
423
|
+
template: (targetPath) => {
|
|
424
|
+
const targetDir = targetPath ? path.resolve(targetPath) : process.cwd();
|
|
425
|
+
|
|
426
|
+
console.log('🚀 Initializing Claude Code project template...');
|
|
427
|
+
console.log(' Target:', targetDir);
|
|
428
|
+
console.log('');
|
|
429
|
+
|
|
430
|
+
// Check template directory exists
|
|
431
|
+
if (!fs.existsSync(TEMPLATE_DIR)) {
|
|
432
|
+
console.log('❌ Template not found at:', TEMPLATE_DIR);
|
|
433
|
+
console.log(' Please reinstall sumulige-claude');
|
|
434
|
+
process.exit(1);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Create directory structure
|
|
438
|
+
console.log('📁 Creating directory structure...');
|
|
439
|
+
const dirs = [
|
|
440
|
+
path.join(targetDir, '.claude'),
|
|
441
|
+
path.join(targetDir, 'prompts'),
|
|
442
|
+
path.join(targetDir, 'development/todos/active'),
|
|
443
|
+
path.join(targetDir, 'development/todos/completed'),
|
|
444
|
+
path.join(targetDir, 'development/todos/backlog'),
|
|
445
|
+
path.join(targetDir, 'development/todos/archived')
|
|
446
|
+
];
|
|
447
|
+
|
|
448
|
+
dirs.forEach(ensureDir);
|
|
449
|
+
console.log(' ✅ Directories created');
|
|
450
|
+
|
|
451
|
+
// Copy files
|
|
452
|
+
console.log('📋 Copying template files...');
|
|
453
|
+
|
|
454
|
+
const claudeTemplateDir = path.join(TEMPLATE_DIR, '.claude');
|
|
455
|
+
const targetClaudeDir = path.join(targetDir, '.claude');
|
|
456
|
+
|
|
457
|
+
// Files to copy
|
|
458
|
+
const filesToCopy = [
|
|
459
|
+
{ src: 'CLAUDE-template.md', dest: 'CLAUDE.md' },
|
|
460
|
+
{ src: 'README.md', dest: 'README.md' },
|
|
461
|
+
{ src: 'settings.json', dest: 'settings.json' },
|
|
462
|
+
{ src: 'boris-optimizations.md', dest: 'boris-optimizations.md' }
|
|
463
|
+
];
|
|
464
|
+
|
|
465
|
+
filesToCopy.forEach(({ src, dest }) => {
|
|
466
|
+
const srcPath = path.join(claudeTemplateDir, src);
|
|
467
|
+
if (fs.existsSync(srcPath)) {
|
|
468
|
+
fs.copyFileSync(srcPath, path.join(targetClaudeDir, dest));
|
|
469
|
+
console.log(` ✅ .claude/${dest}`);
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// Directories to copy recursively
|
|
474
|
+
const dirsToCopy = [
|
|
475
|
+
{ src: 'hooks', overwrite: true },
|
|
476
|
+
{ src: 'commands', overwrite: true },
|
|
477
|
+
{ src: 'skills', overwrite: false },
|
|
478
|
+
{ src: 'templates', overwrite: false },
|
|
479
|
+
{ src: 'thinking-routes', overwrite: false },
|
|
480
|
+
{ src: 'rag', overwrite: true }
|
|
481
|
+
];
|
|
482
|
+
|
|
483
|
+
dirsToCopy.forEach(({ src, overwrite }) => {
|
|
484
|
+
const srcPath = path.join(claudeTemplateDir, src);
|
|
485
|
+
if (fs.existsSync(srcPath)) {
|
|
486
|
+
const count = copyRecursive(srcPath, path.join(targetClaudeDir, src), overwrite);
|
|
487
|
+
console.log(` ✅ .claude/${src}/ (${count} files)`);
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
// Copy prompts
|
|
492
|
+
const promptsDir = path.join(TEMPLATE_DIR, 'prompts');
|
|
493
|
+
if (fs.existsSync(promptsDir)) {
|
|
494
|
+
const count = copyRecursive(promptsDir, path.join(targetDir, 'prompts'), false);
|
|
495
|
+
console.log(` ✅ prompts/ (${count} files)`);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Copy todos
|
|
499
|
+
const todosDir = path.join(TEMPLATE_DIR, 'development', 'todos');
|
|
500
|
+
if (fs.existsSync(todosDir)) {
|
|
501
|
+
const count = copyRecursive(todosDir, path.join(targetDir, 'development', 'todos'), false);
|
|
502
|
+
console.log(` ✅ development/todos/ (${count} files)`);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Root files
|
|
506
|
+
const rootFiles = ['project-paradigm.md', 'thinkinglens-silent.md', 'CLAUDE-template.md'];
|
|
507
|
+
rootFiles.forEach(file => {
|
|
508
|
+
const src = path.join(TEMPLATE_DIR, file);
|
|
509
|
+
if (fs.existsSync(src)) {
|
|
510
|
+
fs.copyFileSync(src, path.join(targetDir, file));
|
|
511
|
+
console.log(' ✅ ' + file);
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
// Create memory files
|
|
516
|
+
console.log('📝 Creating memory files...');
|
|
517
|
+
if (!fs.existsSync(path.join(targetClaudeDir, 'MEMORY.md'))) {
|
|
518
|
+
fs.writeFileSync(path.join(targetClaudeDir, 'MEMORY.md'), '# Memory\n\n<!-- Project memory updated by AI -->\n');
|
|
519
|
+
}
|
|
520
|
+
if (!fs.existsSync(path.join(targetClaudeDir, 'PROJECT_LOG.md'))) {
|
|
521
|
+
fs.writeFileSync(path.join(targetClaudeDir, 'PROJECT_LOG.md'), '# Project Log\n\n<!-- Build history and decisions -->\n');
|
|
522
|
+
}
|
|
523
|
+
console.log(' ✅ Memory files created');
|
|
524
|
+
|
|
525
|
+
// Create ANCHORS.md
|
|
526
|
+
const anchorsContent = `# [Project Name] - Skill Anchors Index
|
|
527
|
+
|
|
528
|
+
> This file is auto-maintained by AI as a quick index for the skill system
|
|
529
|
+
> Last updated: ${new Date().toISOString().split('T')[0]}
|
|
530
|
+
|
|
531
|
+
---
|
|
532
|
+
|
|
533
|
+
## 🚀 AI Startup: Memory Loading Order
|
|
534
|
+
|
|
535
|
+
\`\`\`
|
|
536
|
+
1. ANCHORS.md (this file) → Quick locate modules
|
|
537
|
+
2. PROJECT_LOG.md → Understand build history
|
|
538
|
+
3. MEMORY.md → View latest changes
|
|
539
|
+
4. CLAUDE.md → Load core knowledge
|
|
540
|
+
5. prompts/ → View tutorials
|
|
541
|
+
6. .claude/rag/skills.md → RAG skill index ⭐
|
|
542
|
+
7. Specific files → Deep dive into implementation
|
|
543
|
+
\`\`\`
|
|
544
|
+
|
|
545
|
+
---
|
|
546
|
+
|
|
547
|
+
## Current Anchor Mapping
|
|
548
|
+
|
|
549
|
+
### Teaching Resources
|
|
550
|
+
| Anchor | File Path | Purpose |
|
|
551
|
+
|--------|-----------|---------|
|
|
552
|
+
| \`[doc:paradigm]\` | \`prompts/project-paradigm.md\` | General development paradigm ⭐ |
|
|
553
|
+
| \`[doc:claude-template]\` | \`.claude/CLAUDE.md\` | CLAUDE.md template for new projects |
|
|
554
|
+
|
|
555
|
+
### RAG System
|
|
556
|
+
| Anchor | File Path | Purpose |
|
|
557
|
+
|--------|-----------|---------|
|
|
558
|
+
| \`[system:rag-index]\` | \`.claude/rag/skill-index.json\` | Dynamic skill index ⭐ |
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
## Add Your Anchors Here...
|
|
563
|
+
|
|
564
|
+
`;
|
|
565
|
+
fs.writeFileSync(path.join(targetClaudeDir, 'ANCHORS.md'), anchorsContent);
|
|
566
|
+
console.log(' ✅ .claude/ANCHORS.md');
|
|
567
|
+
|
|
568
|
+
// Initialize Sumulige Claude if installed
|
|
569
|
+
console.log('');
|
|
570
|
+
console.log('🤖 Initializing Sumulige Claude...');
|
|
571
|
+
try {
|
|
572
|
+
execSync('sumulige-claude sync', { cwd: targetDir, stdio: 'pipe' });
|
|
573
|
+
console.log(' ✅ Sumulige Claude synced');
|
|
574
|
+
} catch (e) {
|
|
575
|
+
console.log(' ⚠️ Sumulige Claude not available (run: npm i -g sumulige-claude)');
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
console.log('');
|
|
579
|
+
console.log('✅ Template initialization complete!');
|
|
580
|
+
console.log('');
|
|
581
|
+
console.log('📦 What was included:');
|
|
582
|
+
console.log(' • AI autonomous memory system (ThinkingLens)');
|
|
583
|
+
console.log(' • Slash commands (/commit, /test, /review, etc.)');
|
|
584
|
+
console.log(' • Skills system with templates');
|
|
585
|
+
console.log(' • RAG dynamic skill index');
|
|
586
|
+
console.log(' • Hooks for automation');
|
|
587
|
+
console.log(' • TODO management system');
|
|
588
|
+
console.log('');
|
|
589
|
+
console.log('Next steps:');
|
|
590
|
+
console.log(' 1. Edit .claude/CLAUDE.md with your project info');
|
|
591
|
+
console.log(' 2. Run: claude # Start Claude Code');
|
|
592
|
+
console.log(' 3. Try: /commit, /test, /review');
|
|
593
|
+
console.log('');
|
|
594
|
+
},
|
|
595
|
+
|
|
596
|
+
// -------------------------------------------------------------------------
|
|
597
|
+
kickoff: () => {
|
|
598
|
+
const projectDir = process.cwd();
|
|
599
|
+
const kickoffFile = path.join(projectDir, 'PROJECT_KICKOFF.md');
|
|
600
|
+
const hintFile = path.join(projectDir, '.claude', '.kickoff-hint.txt');
|
|
601
|
+
|
|
602
|
+
console.log('🚀 Project Kickoff - Manus 风格项目启动');
|
|
603
|
+
console.log('');
|
|
604
|
+
|
|
605
|
+
if (fs.existsSync(kickoffFile)) {
|
|
606
|
+
console.log('ℹ️ 项目已经完成启动流程');
|
|
607
|
+
console.log(' 文件:', kickoffFile);
|
|
608
|
+
console.log('');
|
|
609
|
+
console.log('如需重新规划,请先删除以下文件:');
|
|
610
|
+
console.log(' - PROJECT_KICKOFF.md');
|
|
611
|
+
console.log(' - TASK_PLAN.md');
|
|
612
|
+
console.log(' - PROJECT_PROPOSAL.md');
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// Run kickoff hook
|
|
617
|
+
const kickoffHook = path.join(projectDir, '.claude', 'hooks', 'project-kickoff.cjs');
|
|
618
|
+
if (fs.existsSync(kickoffHook)) {
|
|
619
|
+
try {
|
|
620
|
+
execSync(`node "${kickoffHook}"`, {
|
|
621
|
+
cwd: projectDir,
|
|
622
|
+
env: { ...process.env, CLAUDE_PROJECT_DIR: projectDir },
|
|
623
|
+
stdio: 'inherit'
|
|
624
|
+
});
|
|
625
|
+
} catch (e) {
|
|
626
|
+
// Hook may output and exit, this is normal
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// Show hint file if exists
|
|
630
|
+
if (fs.existsSync(hintFile)) {
|
|
631
|
+
const hint = fs.readFileSync(hintFile, 'utf-8');
|
|
632
|
+
console.log(hint);
|
|
633
|
+
}
|
|
634
|
+
} else {
|
|
635
|
+
console.log('⚠️ 启动 Hook 不存在');
|
|
636
|
+
console.log(' 请先运行: sumulige-claude template');
|
|
637
|
+
console.log(' 或: sumulige-claude sync');
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
// ============================================================================
|
|
643
|
+
// Helpers
|
|
644
|
+
// ============================================================================
|
|
645
|
+
|
|
646
|
+
function generateAgentsMd(config) {
|
|
647
|
+
const agentsList = Object.entries(config.agents)
|
|
648
|
+
.map(([name, agent]) => {
|
|
649
|
+
const model = agent.model || config.model;
|
|
650
|
+
return `### ${name}\n- **Model**: ${model}\n- **Role**: ${agent.role}`;
|
|
651
|
+
})
|
|
652
|
+
.join('\n\n');
|
|
653
|
+
|
|
654
|
+
return `# AGENTS
|
|
655
|
+
|
|
656
|
+
<skills_system priority="1">
|
|
657
|
+
|
|
658
|
+
## Agent Orchestration
|
|
659
|
+
|
|
660
|
+
This project uses **Sumulige Claude** for multi-agent collaboration.
|
|
661
|
+
|
|
662
|
+
${agentsList}
|
|
663
|
+
|
|
664
|
+
## Usage
|
|
665
|
+
|
|
666
|
+
\`\`\`bash
|
|
667
|
+
# View agent status
|
|
668
|
+
sumulige-claude status
|
|
669
|
+
|
|
670
|
+
# Run agent task
|
|
671
|
+
sumulige-claude agent <task>
|
|
672
|
+
|
|
673
|
+
# List skills
|
|
674
|
+
sumulige-claude skill:list
|
|
675
|
+
\`\`\`
|
|
676
|
+
|
|
677
|
+
</skills_system>
|
|
678
|
+
`;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// ============================================================================
|
|
682
|
+
// Exports
|
|
683
|
+
// ============================================================================
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Run a command
|
|
687
|
+
* @param {string} cmd - Command name
|
|
688
|
+
* @param {Array} args - Command arguments
|
|
689
|
+
*/
|
|
690
|
+
function runCommand(cmd, args) {
|
|
691
|
+
const command = commands[cmd];
|
|
692
|
+
if (command) {
|
|
693
|
+
command(...args);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
exports.runCommand = runCommand;
|
|
698
|
+
exports.commands = commands;
|