@haposoft/cafekit 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +866 -0
- package/bin/install.js +773 -0
- package/package.json +40 -0
- package/src/antigravity/GEMINI.md +162 -0
- package/src/antigravity/workflows/code.md +16 -0
- package/src/antigravity/workflows/docs-init.md +432 -0
- package/src/antigravity/workflows/docs-update.md +245 -0
- package/src/antigravity/workflows/review.md +15 -0
- package/src/antigravity/workflows/spec-design.md +220 -0
- package/src/antigravity/workflows/spec-init.md +78 -0
- package/src/antigravity/workflows/spec-requirements.md +122 -0
- package/src/antigravity/workflows/spec-status.md +95 -0
- package/src/antigravity/workflows/spec-tasks.md +156 -0
- package/src/antigravity/workflows/spec-validate.md +106 -0
- package/src/antigravity/workflows/test.md +13 -0
- package/src/claude/ROUTING.md +101 -0
- package/src/claude/agents/code-reviewer.md +131 -0
- package/src/claude/agents/debugger.md +137 -0
- package/src/claude/agents/fullstack-developer.md +95 -0
- package/src/claude/agents/tester.md +105 -0
- package/src/claude/commands/code.md +17 -0
- package/src/claude/commands/docs.md +609 -0
- package/src/claude/commands/review/codebase/parallel.md +122 -0
- package/src/claude/commands/review/codebase.md +49 -0
- package/src/claude/commands/review.md +16 -0
- package/src/claude/commands/spec-design.md +247 -0
- package/src/claude/commands/spec-init.md +118 -0
- package/src/claude/commands/spec-requirements.md +138 -0
- package/src/claude/commands/spec-status.md +98 -0
- package/src/claude/commands/spec-tasks.md +173 -0
- package/src/claude/commands/spec-validate.md +118 -0
- package/src/claude/commands/test.md +8 -0
- package/src/claude/migration-manifest.json +39 -0
- package/src/common/skills/specs/SKILL.md +101 -0
- package/src/common/skills/specs/rules/design-discovery-full.md +93 -0
- package/src/common/skills/specs/rules/design-discovery-light.md +49 -0
- package/src/common/skills/specs/rules/design-principles.md +182 -0
- package/src/common/skills/specs/rules/design-review.md +110 -0
- package/src/common/skills/specs/rules/ears-format.md +49 -0
- package/src/common/skills/specs/rules/gap-analysis.md +144 -0
- package/src/common/skills/specs/rules/steering-principles.md +90 -0
- package/src/common/skills/specs/rules/tasks-generation.md +131 -0
- package/src/common/skills/specs/rules/tasks-parallel-analysis.md +34 -0
- package/src/common/skills/specs/templates/design.md +276 -0
- package/src/common/skills/specs/templates/init.json +41 -0
- package/src/common/skills/specs/templates/requirements-init.md +9 -0
- package/src/common/skills/specs/templates/requirements.md +26 -0
- package/src/common/skills/specs/templates/research.md +61 -0
- package/src/common/skills/specs/templates/tasks.md +21 -0
package/bin/install.js
ADDED
|
@@ -0,0 +1,773 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CafeKit Installer
|
|
5
|
+
* Multi-platform installer for AI coding assistants
|
|
6
|
+
*
|
|
7
|
+
* Supported platforms:
|
|
8
|
+
* - claude: Claude Code (.claude/)
|
|
9
|
+
* - antigravity: Antigravity (.agent/)
|
|
10
|
+
*
|
|
11
|
+
* To add a new platform:
|
|
12
|
+
* 1. Add to PLATFORMS registry below
|
|
13
|
+
* 2. Add platform-specific commands in src/[platform]/commands/
|
|
14
|
+
* 3. Update detectPlatforms() if using different folder names
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
const readline = require('readline');
|
|
20
|
+
const packageJson = require('../package.json');
|
|
21
|
+
|
|
22
|
+
function loadClaudeMigrationManifest() {
|
|
23
|
+
const manifestPath = path.join(__dirname, '../src/claude/migration-manifest.json');
|
|
24
|
+
|
|
25
|
+
if (!fs.existsSync(manifestPath)) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.warn(`⚠ Failed to parse Claude migration manifest: ${error.message}`);
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const CLAUDE_MIGRATION_MANIFEST = loadClaudeMigrationManifest();
|
|
38
|
+
|
|
39
|
+
const DEPENDENCY_TEMPLATES = {
|
|
40
|
+
commands: {
|
|
41
|
+
claude: {},
|
|
42
|
+
antigravity: {
|
|
43
|
+
'code.md': `---
|
|
44
|
+
description: Implement approved work from specification tasks and then hand off to test and review.
|
|
45
|
+
allowed-tools: Read, Glob, Grep, Edit, Write, Bash
|
|
46
|
+
argument-hint: <feature-name>
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
# /code - Implement from spec tasks
|
|
50
|
+
|
|
51
|
+
Use this workflow after /spec-tasks.
|
|
52
|
+
|
|
53
|
+
1. Read .specs/$ARGUMENTS/tasks.md and identify the next pending task.
|
|
54
|
+
2. Implement only that task following project standards.
|
|
55
|
+
3. Run /test.
|
|
56
|
+
4. Run /review.
|
|
57
|
+
|
|
58
|
+
Preferred flow: /spec-init -> /spec-requirements -> /spec-design -> /spec-tasks -> /code -> /test -> /review
|
|
59
|
+
`,
|
|
60
|
+
'test.md': `---
|
|
61
|
+
description: Run project tests and report failures concisely.
|
|
62
|
+
allowed-tools: Bash, Read, Grep
|
|
63
|
+
argument-hint: [scope]
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
# /test
|
|
67
|
+
|
|
68
|
+
Run the project's test command and report:
|
|
69
|
+
- total passed/failed
|
|
70
|
+
- failing test names
|
|
71
|
+
- root cause hints
|
|
72
|
+
- next fix action
|
|
73
|
+
`,
|
|
74
|
+
'review.md': `---
|
|
75
|
+
description: Review recent code changes for quality, security, and maintainability.
|
|
76
|
+
allowed-tools: Bash, Read, Grep, Glob
|
|
77
|
+
argument-hint: [scope]
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
# /review
|
|
81
|
+
|
|
82
|
+
Review recent code changes. Prioritize:
|
|
83
|
+
- correctness
|
|
84
|
+
- security
|
|
85
|
+
- regressions
|
|
86
|
+
- maintainability
|
|
87
|
+
|
|
88
|
+
Output findings by severity and include concrete fixes.
|
|
89
|
+
`
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
agents: {
|
|
93
|
+
claude: {},
|
|
94
|
+
antigravity: {
|
|
95
|
+
'frontend-specialist.md': `---
|
|
96
|
+
name: frontend-specialist
|
|
97
|
+
description: Implement approved UI and interaction tasks from specifications.
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
Implement UI tasks from approved specs with accessibility and responsive behavior.
|
|
101
|
+
`,
|
|
102
|
+
'test-engineer.md': `---
|
|
103
|
+
name: test-engineer
|
|
104
|
+
description: Execute tests and report reliability issues.
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
Run test suites, highlight failures, and propose precise fixes.
|
|
108
|
+
`,
|
|
109
|
+
'code-archaeologist.md': `---
|
|
110
|
+
name: code-archaeologist
|
|
111
|
+
description: Review recent changes for regressions and hidden impacts.
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
Inspect changed code paths, dependencies, and potential regressions.
|
|
115
|
+
`
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// ═══════════════════════════════════════════════════════════
|
|
121
|
+
// PLATFORM REGISTRY - Add new platforms here
|
|
122
|
+
// ═══════════════════════════════════════════════════════════
|
|
123
|
+
const PLATFORMS = {
|
|
124
|
+
claude: {
|
|
125
|
+
id: 'claude',
|
|
126
|
+
name: 'Claude Code',
|
|
127
|
+
description: 'Anthropic\'s Claude Code CLI',
|
|
128
|
+
folder: '.claude',
|
|
129
|
+
commandsDir: '.claude/commands',
|
|
130
|
+
skillsDir: '.claude/skills',
|
|
131
|
+
agentsDir: '.claude/agents',
|
|
132
|
+
skillsRef: '.claude/skills',
|
|
133
|
+
commandPrefix: '/',
|
|
134
|
+
sourceDir: 'claude', // Maps to src/claude/
|
|
135
|
+
sourceSubdir: 'commands' // Source subfolder within src/claude/
|
|
136
|
+
},
|
|
137
|
+
antigravity: {
|
|
138
|
+
id: 'antigravity',
|
|
139
|
+
name: 'Antigravity',
|
|
140
|
+
description: 'Google\'s Antigravity Kit',
|
|
141
|
+
folder: '.agent',
|
|
142
|
+
commandsDir: '.agent/workflows', // Antigravity uses workflows/ not commands/
|
|
143
|
+
skillsDir: '.agent/skills',
|
|
144
|
+
agentsDir: '.agent/agents',
|
|
145
|
+
skillsRef: '.agent/skills',
|
|
146
|
+
commandPrefix: '/',
|
|
147
|
+
sourceDir: 'antigravity', // Maps to src/antigravity/
|
|
148
|
+
sourceSubdir: 'workflows' // Source subfolder within src/antigravity/
|
|
149
|
+
}
|
|
150
|
+
// Add new platforms here:
|
|
151
|
+
// cursor: {
|
|
152
|
+
// id: 'cursor',
|
|
153
|
+
// name: 'Cursor',
|
|
154
|
+
// description: 'Cursor IDE',
|
|
155
|
+
// folder: '.cursor',
|
|
156
|
+
// commandsDir: '.cursor/commands',
|
|
157
|
+
// sourceDir: 'cursor',
|
|
158
|
+
// sourceSubdir: 'commands'
|
|
159
|
+
// }
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// ═══════════════════════════════════════════════════════════
|
|
163
|
+
// DETECTION
|
|
164
|
+
// ═══════════════════════════════════════════════════════════
|
|
165
|
+
|
|
166
|
+
function detectPlatforms() {
|
|
167
|
+
const detected = [];
|
|
168
|
+
|
|
169
|
+
for (const [key, config] of Object.entries(PLATFORMS)) {
|
|
170
|
+
if (fs.existsSync(config.folder)) {
|
|
171
|
+
detected.push(key);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return detected;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function formatPlatformList() {
|
|
179
|
+
return Object.entries(PLATFORMS)
|
|
180
|
+
.map(([key, config], index) => {
|
|
181
|
+
return `${index + 1}) ${config.name} (${config.folder}/)\n ${config.description}`;
|
|
182
|
+
})
|
|
183
|
+
.join('\n');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function getPlatformKeys() {
|
|
187
|
+
return Object.keys(PLATFORMS);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function parseInstallerArgs(argv) {
|
|
191
|
+
const args = {
|
|
192
|
+
upgrade: false
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
for (let i = 2; i < argv.length; i++) {
|
|
196
|
+
const arg = argv[i];
|
|
197
|
+
|
|
198
|
+
if (arg === '--upgrade' || arg === '-u' || arg === '--force' || arg === '-f') {
|
|
199
|
+
args.upgrade = true;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return args;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ═══════════════════════════════════════════════════════════
|
|
207
|
+
// USER INTERACTION
|
|
208
|
+
// ═══════════════════════════════════════════════════════════
|
|
209
|
+
|
|
210
|
+
async function promptPlatformSelection() {
|
|
211
|
+
const rl = readline.createInterface({
|
|
212
|
+
input: process.stdin,
|
|
213
|
+
output: process.stdout
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const maxChoice = Object.keys(PLATFORMS).length + 1;
|
|
217
|
+
|
|
218
|
+
console.log('╔════════════════════════════════════════════════════════╗');
|
|
219
|
+
console.log('║ CafeKit - Platform Selection ║');
|
|
220
|
+
console.log('╚════════════════════════════════════════════════════════╝');
|
|
221
|
+
console.log();
|
|
222
|
+
console.log('No existing AI editor configuration detected.\n');
|
|
223
|
+
console.log('Select which platform(s) to install for:\n');
|
|
224
|
+
console.log(formatPlatformList());
|
|
225
|
+
console.log(`${maxChoice}) All platforms`);
|
|
226
|
+
console.log('0) Cancel');
|
|
227
|
+
console.log();
|
|
228
|
+
|
|
229
|
+
return new Promise((resolve) => {
|
|
230
|
+
rl.question(`Select (0-${maxChoice}): `, (answer) => {
|
|
231
|
+
rl.close();
|
|
232
|
+
const choice = parseInt(answer.trim(), 10);
|
|
233
|
+
|
|
234
|
+
if (choice === 0 || isNaN(choice)) {
|
|
235
|
+
resolve([]);
|
|
236
|
+
} else if (choice === maxChoice) {
|
|
237
|
+
resolve(getPlatformKeys());
|
|
238
|
+
} else if (choice >= 1 && choice <= Object.keys(PLATFORMS).length) {
|
|
239
|
+
resolve([getPlatformKeys()[choice - 1]]);
|
|
240
|
+
} else {
|
|
241
|
+
console.log('Invalid selection. Please run the installer again.');
|
|
242
|
+
resolve([]);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async function promptMultiPlatformConfirm(detected) {
|
|
249
|
+
const rl = readline.createInterface({
|
|
250
|
+
input: process.stdin,
|
|
251
|
+
output: process.stdout
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
const platformNames = detected.map(key => PLATFORMS[key].name).join(', ');
|
|
255
|
+
|
|
256
|
+
console.log(`\nDetected existing configurations: ${platformNames}`);
|
|
257
|
+
console.log();
|
|
258
|
+
|
|
259
|
+
return new Promise((resolve) => {
|
|
260
|
+
rl.question('Install for all detected platforms? (Y/n): ', (answer) => {
|
|
261
|
+
rl.close();
|
|
262
|
+
const response = answer.trim().toLowerCase();
|
|
263
|
+
resolve(response === '' || response === 'y' || response === 'yes');
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ═══════════════════════════════════════════════════════════
|
|
269
|
+
// FILE OPERATIONS
|
|
270
|
+
// ═══════════════════════════════════════════════════════════
|
|
271
|
+
|
|
272
|
+
function copyRecursive(src, dest, options = {}) {
|
|
273
|
+
const exists = fs.existsSync(src);
|
|
274
|
+
const stats = exists && fs.statSync(src);
|
|
275
|
+
const isDirectory = exists && stats.isDirectory();
|
|
276
|
+
const shouldOverwriteManagedFiles = Boolean(options.upgrade);
|
|
277
|
+
|
|
278
|
+
if (isDirectory) {
|
|
279
|
+
if (!fs.existsSync(dest)) {
|
|
280
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
281
|
+
}
|
|
282
|
+
fs.readdirSync(src).forEach(childItemName => {
|
|
283
|
+
copyRecursive(path.join(src, childItemName), path.join(dest, childItemName), options);
|
|
284
|
+
});
|
|
285
|
+
} else {
|
|
286
|
+
if (fs.existsSync(dest) && !shouldOverwriteManagedFiles) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
291
|
+
fs.copyFileSync(src, dest);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function ensureDependencyFile(targetPath, content, results, label, options = {}) {
|
|
296
|
+
const destinationExists = fs.existsSync(targetPath);
|
|
297
|
+
const shouldOverwriteManagedFiles = Boolean(options.upgrade);
|
|
298
|
+
|
|
299
|
+
if (destinationExists && !shouldOverwriteManagedFiles) {
|
|
300
|
+
console.log(` → Dependency exists: ${label}`);
|
|
301
|
+
results.dependencyChecks++;
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (!content) {
|
|
306
|
+
console.log(` ⚠ Missing dependency template: ${label}`);
|
|
307
|
+
results.dependencyChecks++;
|
|
308
|
+
results.missingDependencies++;
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
313
|
+
fs.writeFileSync(targetPath, content, 'utf8');
|
|
314
|
+
|
|
315
|
+
if (destinationExists && shouldOverwriteManagedFiles) {
|
|
316
|
+
console.log(` ↻ Dependency updated: ${label}`);
|
|
317
|
+
results.updated++;
|
|
318
|
+
} else {
|
|
319
|
+
console.log(` ✓ Dependency installed: ${label}`);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
results.dependencyChecks++;
|
|
323
|
+
results.installedDependencies++;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function ensureWorkflowDependencies(platformKey, platform, results, options = {}) {
|
|
327
|
+
const commandTemplates = DEPENDENCY_TEMPLATES.commands[platformKey] || {};
|
|
328
|
+
Object.entries(commandTemplates).forEach(([fileName, content]) => {
|
|
329
|
+
const targetPath = path.join(platform.commandsDir, fileName);
|
|
330
|
+
ensureDependencyFile(targetPath, content, results, path.join(platform.commandsDir, fileName), options);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
const agentTemplates = DEPENDENCY_TEMPLATES.agents[platformKey] || {};
|
|
334
|
+
Object.entries(agentTemplates).forEach(([fileName, content]) => {
|
|
335
|
+
const targetPath = path.join(platform.agentsDir, fileName);
|
|
336
|
+
ensureDependencyFile(targetPath, content, results, path.join(platform.agentsDir, fileName), options);
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function getPlatformSpecFiles(platformKey) {
|
|
341
|
+
if (platformKey === 'claude') {
|
|
342
|
+
const manifestCommands = CLAUDE_MIGRATION_MANIFEST?.commands?.core;
|
|
343
|
+
if (Array.isArray(manifestCommands) && manifestCommands.length > 0) {
|
|
344
|
+
return manifestCommands;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return [
|
|
348
|
+
'spec-init.md',
|
|
349
|
+
'spec-requirements.md',
|
|
350
|
+
'spec-design.md',
|
|
351
|
+
'spec-validate.md',
|
|
352
|
+
'spec-tasks.md',
|
|
353
|
+
'spec-status.md',
|
|
354
|
+
'code.md',
|
|
355
|
+
'test.md',
|
|
356
|
+
'review.md',
|
|
357
|
+
'review/codebase.md',
|
|
358
|
+
'review/codebase/parallel.md',
|
|
359
|
+
'docs.md'
|
|
360
|
+
];
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (platformKey === 'antigravity') {
|
|
364
|
+
return [
|
|
365
|
+
'spec-init.md',
|
|
366
|
+
'spec-requirements.md',
|
|
367
|
+
'spec-design.md',
|
|
368
|
+
'spec-validate.md',
|
|
369
|
+
'spec-tasks.md',
|
|
370
|
+
'spec-status.md',
|
|
371
|
+
'docs-init.md',
|
|
372
|
+
'docs-update.md'
|
|
373
|
+
];
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return [];
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function copyPlatformFiles(platformKey, results, options = {}) {
|
|
380
|
+
const platform = PLATFORMS[platformKey];
|
|
381
|
+
const shouldOverwriteManagedFiles = Boolean(options.upgrade);
|
|
382
|
+
const recordWriteResult = (didOverwrite) => {
|
|
383
|
+
if (didOverwrite) {
|
|
384
|
+
results.updated++;
|
|
385
|
+
} else {
|
|
386
|
+
results.copied++;
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
// Source directories - support different subfolder names per platform
|
|
391
|
+
const sourceSubdir = platform.sourceSubdir || 'commands';
|
|
392
|
+
const commandsSourceDir = path.join(__dirname, `../src/${platform.sourceDir}/${sourceSubdir}`);
|
|
393
|
+
const skillsSourceDir = path.join(__dirname, '../src/common/skills');
|
|
394
|
+
const agentsSourceDir = path.join(__dirname, `../src/${platform.sourceDir}/agents`);
|
|
395
|
+
|
|
396
|
+
// Create directories
|
|
397
|
+
if (!fs.existsSync(platform.commandsDir)) {
|
|
398
|
+
fs.mkdirSync(platform.commandsDir, { recursive: true });
|
|
399
|
+
}
|
|
400
|
+
if (!fs.existsSync(platform.skillsDir)) {
|
|
401
|
+
fs.mkdirSync(platform.skillsDir, { recursive: true });
|
|
402
|
+
}
|
|
403
|
+
if (!fs.existsSync(platform.agentsDir)) {
|
|
404
|
+
fs.mkdirSync(platform.agentsDir, { recursive: true });
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Copy skills (shared across all platforms)
|
|
408
|
+
if (fs.existsSync(skillsSourceDir)) {
|
|
409
|
+
const specSkillSource = path.join(skillsSourceDir, 'specs');
|
|
410
|
+
const specSkillDest = path.join(platform.skillsDir, 'specs');
|
|
411
|
+
|
|
412
|
+
if (fs.existsSync(specSkillSource)) {
|
|
413
|
+
const skillExisted = fs.existsSync(specSkillDest);
|
|
414
|
+
copyRecursive(specSkillSource, specSkillDest, options);
|
|
415
|
+
results.installedSkills++;
|
|
416
|
+
|
|
417
|
+
if (shouldOverwriteManagedFiles && skillExisted) {
|
|
418
|
+
console.log(` ↻ Skill updated: specs`);
|
|
419
|
+
results.updated++;
|
|
420
|
+
} else {
|
|
421
|
+
console.log(` ✓ Skill installed: specs`);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Keep templates in sync for claude command runtime copies under .claude/skills/specs
|
|
426
|
+
if (platformKey === 'claude') {
|
|
427
|
+
const specTemplates = [
|
|
428
|
+
'init.json',
|
|
429
|
+
'requirements-init.md',
|
|
430
|
+
'requirements.md',
|
|
431
|
+
'design.md',
|
|
432
|
+
'research.md',
|
|
433
|
+
'tasks.md'
|
|
434
|
+
];
|
|
435
|
+
|
|
436
|
+
specTemplates.forEach((fileName) => {
|
|
437
|
+
const source = path.join(specSkillSource, 'templates', fileName);
|
|
438
|
+
const dest = path.join(platform.skillsDir, 'specs', 'templates', fileName);
|
|
439
|
+
|
|
440
|
+
if (!fs.existsSync(source)) {
|
|
441
|
+
console.log(` ⚠ Missing dependency template: ${path.join(platform.skillsDir, 'specs', 'templates', fileName)}`);
|
|
442
|
+
results.missingDependencies++;
|
|
443
|
+
results.dependencyChecks++;
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const destinationExists = fs.existsSync(dest);
|
|
448
|
+
|
|
449
|
+
if (destinationExists && !shouldOverwriteManagedFiles) {
|
|
450
|
+
console.log(` → Dependency exists: ${path.join(platform.skillsDir, 'specs', 'templates', fileName)}`);
|
|
451
|
+
results.dependencyChecks++;
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
456
|
+
fs.copyFileSync(source, dest);
|
|
457
|
+
results.dependencyChecks++;
|
|
458
|
+
results.installedDependencies++;
|
|
459
|
+
|
|
460
|
+
if (destinationExists && shouldOverwriteManagedFiles) {
|
|
461
|
+
console.log(` ↻ Dependency updated: ${path.join(platform.skillsDir, 'specs', 'templates', fileName)}`);
|
|
462
|
+
results.updated++;
|
|
463
|
+
} else {
|
|
464
|
+
console.log(` ✓ Dependency installed: ${path.join(platform.skillsDir, 'specs', 'templates', fileName)}`);
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (platformKey === 'claude') {
|
|
470
|
+
const requiredSkills = CLAUDE_MIGRATION_MANIFEST?.skills?.required || [];
|
|
471
|
+
|
|
472
|
+
requiredSkills
|
|
473
|
+
.filter((skillName) => skillName !== 'specs')
|
|
474
|
+
.forEach((skillName) => {
|
|
475
|
+
const skillSource = path.join(skillsSourceDir, skillName);
|
|
476
|
+
const skillDest = path.join(platform.skillsDir, skillName);
|
|
477
|
+
|
|
478
|
+
if (fs.existsSync(skillSource)) {
|
|
479
|
+
const skillExisted = fs.existsSync(skillDest);
|
|
480
|
+
copyRecursive(skillSource, skillDest, options);
|
|
481
|
+
results.installedSkills++;
|
|
482
|
+
|
|
483
|
+
if (shouldOverwriteManagedFiles && skillExisted) {
|
|
484
|
+
console.log(` ↻ Skill updated: ${skillName}`);
|
|
485
|
+
results.updated++;
|
|
486
|
+
} else {
|
|
487
|
+
console.log(` ✓ Skill installed: ${skillName}`);
|
|
488
|
+
}
|
|
489
|
+
} else {
|
|
490
|
+
results.missingDependencies++;
|
|
491
|
+
console.log(` ⚠ Missing dependency template: ${path.join(platform.skillsDir, skillName)}`);
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Copy agents
|
|
498
|
+
if (platformKey === 'claude') {
|
|
499
|
+
if (fs.existsSync(agentsSourceDir)) {
|
|
500
|
+
const requiredAgents = CLAUDE_MIGRATION_MANIFEST?.agents?.required || [
|
|
501
|
+
'tester.md',
|
|
502
|
+
'code-reviewer.md',
|
|
503
|
+
'fullstack-developer.md',
|
|
504
|
+
'debugger.md'
|
|
505
|
+
];
|
|
506
|
+
|
|
507
|
+
requiredAgents.forEach((fileName) => {
|
|
508
|
+
const source = path.join(agentsSourceDir, fileName);
|
|
509
|
+
const dest = path.join(platform.agentsDir, fileName);
|
|
510
|
+
|
|
511
|
+
if (!fs.existsSync(source)) {
|
|
512
|
+
console.log(` ⚠ Missing dependency template: ${path.join(platform.agentsDir, fileName)}`);
|
|
513
|
+
results.missingDependencies++;
|
|
514
|
+
results.dependencyChecks++;
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const destinationExists = fs.existsSync(dest);
|
|
519
|
+
|
|
520
|
+
if (destinationExists && !shouldOverwriteManagedFiles) {
|
|
521
|
+
console.log(` → Dependency exists: ${path.join(platform.agentsDir, fileName)}`);
|
|
522
|
+
results.dependencyChecks++;
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
527
|
+
fs.copyFileSync(source, dest);
|
|
528
|
+
results.dependencyChecks++;
|
|
529
|
+
results.installedDependencies++;
|
|
530
|
+
|
|
531
|
+
if (destinationExists && shouldOverwriteManagedFiles) {
|
|
532
|
+
console.log(` ↻ Dependency updated: ${path.join(platform.agentsDir, fileName)}`);
|
|
533
|
+
results.updated++;
|
|
534
|
+
} else {
|
|
535
|
+
console.log(` ✓ Dependency installed: ${path.join(platform.agentsDir, fileName)}`);
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
} else {
|
|
539
|
+
results.missingDependencies++;
|
|
540
|
+
console.log(` ⚠ Missing dependency template: ${platform.agentsDir}`);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
ensureWorkflowDependencies(platformKey, platform, results, options);
|
|
546
|
+
|
|
547
|
+
// Copy commands/workflows
|
|
548
|
+
const specFiles = getPlatformSpecFiles(platformKey);
|
|
549
|
+
|
|
550
|
+
specFiles.forEach(file => {
|
|
551
|
+
const source = path.join(commandsSourceDir, file);
|
|
552
|
+
const dest = path.join(platform.commandsDir, file);
|
|
553
|
+
const destinationExists = fs.existsSync(dest);
|
|
554
|
+
|
|
555
|
+
if (!fs.existsSync(source)) {
|
|
556
|
+
console.error(` ✗ Error: Source file not found: ${file}`);
|
|
557
|
+
results.errors++;
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (destinationExists && !shouldOverwriteManagedFiles) {
|
|
562
|
+
console.log(` → Skipped: ${file} (already exists)`);
|
|
563
|
+
results.skipped++;
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
568
|
+
let content = fs.readFileSync(source, 'utf8');
|
|
569
|
+
content = content.replace(/\{\{SKILLS_DIR\}\}/g, platform.skillsRef);
|
|
570
|
+
fs.writeFileSync(dest, content);
|
|
571
|
+
|
|
572
|
+
if (destinationExists && shouldOverwriteManagedFiles) {
|
|
573
|
+
console.log(` ↻ Updated: ${file}`);
|
|
574
|
+
recordWriteResult(true);
|
|
575
|
+
} else {
|
|
576
|
+
console.log(` ✓ Copied: ${file}`);
|
|
577
|
+
recordWriteResult(false);
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Copy ROUTING.md to .claude/
|
|
583
|
+
function copyRoutingFile(platformKey, results, options = {}) {
|
|
584
|
+
const platform = PLATFORMS[platformKey];
|
|
585
|
+
const shouldOverwriteManagedFiles = Boolean(options.upgrade);
|
|
586
|
+
const source = path.join(__dirname, `../src/${platform.sourceDir}/ROUTING.md`);
|
|
587
|
+
const dest = path.join(platform.folder, 'ROUTING.md');
|
|
588
|
+
|
|
589
|
+
if (fs.existsSync(source)) {
|
|
590
|
+
const destinationExists = fs.existsSync(dest);
|
|
591
|
+
|
|
592
|
+
if (destinationExists && !shouldOverwriteManagedFiles) {
|
|
593
|
+
console.log(` → Skipped: ROUTING.md (already exists)`);
|
|
594
|
+
results.skipped++;
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
let content = fs.readFileSync(source, 'utf8');
|
|
599
|
+
content = content.replace(/\{\{SKILLS_DIR\}\}/g, platform.skillsRef);
|
|
600
|
+
fs.writeFileSync(dest, content);
|
|
601
|
+
|
|
602
|
+
if (destinationExists && shouldOverwriteManagedFiles) {
|
|
603
|
+
console.log(` ↻ Updated: ROUTING.md`);
|
|
604
|
+
results.updated++;
|
|
605
|
+
} else {
|
|
606
|
+
console.log(` ✓ Copied: ROUTING.md`);
|
|
607
|
+
results.copied++;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Copy GEMINI.md rule file to .agent/rules/ for Antigravity
|
|
613
|
+
function copyGeminiFile(platformKey, results, options = {}) {
|
|
614
|
+
const platform = PLATFORMS[platformKey];
|
|
615
|
+
const shouldOverwriteManagedFiles = Boolean(options.upgrade);
|
|
616
|
+
const rulesDir = path.join(platform.folder, 'rules');
|
|
617
|
+
const source = path.join(__dirname, `../src/${platform.sourceDir}/GEMINI.md`);
|
|
618
|
+
const dest = path.join(rulesDir, 'GEMINI.md');
|
|
619
|
+
|
|
620
|
+
if (fs.existsSync(source)) {
|
|
621
|
+
// Create rules directory if not exists
|
|
622
|
+
if (!fs.existsSync(rulesDir)) {
|
|
623
|
+
fs.mkdirSync(rulesDir, { recursive: true });
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
const destinationExists = fs.existsSync(dest);
|
|
627
|
+
|
|
628
|
+
if (destinationExists && !shouldOverwriteManagedFiles) {
|
|
629
|
+
console.log(` → Skipped: rules/GEMINI.md (already exists)`);
|
|
630
|
+
results.skipped++;
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
fs.copyFileSync(source, dest);
|
|
635
|
+
|
|
636
|
+
if (destinationExists && shouldOverwriteManagedFiles) {
|
|
637
|
+
console.log(` ↻ Updated: rules/GEMINI.md`);
|
|
638
|
+
results.updated++;
|
|
639
|
+
} else {
|
|
640
|
+
console.log(` ✓ Copied: rules/GEMINI.md`);
|
|
641
|
+
results.copied++;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// ═══════════════════════════════════════════════════════════
|
|
647
|
+
// MAIN
|
|
648
|
+
// ═══════════════════════════════════════════════════════════
|
|
649
|
+
|
|
650
|
+
async function main() {
|
|
651
|
+
const installerOptions = parseInstallerArgs(process.argv);
|
|
652
|
+
|
|
653
|
+
console.log();
|
|
654
|
+
console.log('╔════════════════════════════════════════════════════════╗');
|
|
655
|
+
console.log(`║ CafeKit Installer v${String(packageJson.version).padEnd(5, ' ')} ║`);
|
|
656
|
+
console.log('║ Multi-platform SDD Workflow ║');
|
|
657
|
+
console.log('╚════════════════════════════════════════════════════════╝');
|
|
658
|
+
console.log();
|
|
659
|
+
|
|
660
|
+
let platforms = detectPlatforms();
|
|
661
|
+
|
|
662
|
+
if (platforms.length === 0) {
|
|
663
|
+
// No platforms detected - prompt user
|
|
664
|
+
platforms = await promptPlatformSelection();
|
|
665
|
+
|
|
666
|
+
if (platforms.length === 0) {
|
|
667
|
+
console.log('\nInstallation cancelled.');
|
|
668
|
+
process.exit(0);
|
|
669
|
+
}
|
|
670
|
+
} else if (platforms.length > 1) {
|
|
671
|
+
// Multiple platforms detected - confirm with user
|
|
672
|
+
const proceed = await promptMultiPlatformConfirm(platforms);
|
|
673
|
+
if (!proceed) {
|
|
674
|
+
platforms = await promptPlatformSelection();
|
|
675
|
+
if (platforms.length === 0) {
|
|
676
|
+
console.log('\nInstallation cancelled.');
|
|
677
|
+
process.exit(0);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// Show detected/selected platforms
|
|
683
|
+
const platformNames = platforms.map(key => PLATFORMS[key].name).join(', ');
|
|
684
|
+
console.log(`\nInstalling for: ${platformNames}`);
|
|
685
|
+
|
|
686
|
+
if (installerOptions.upgrade) {
|
|
687
|
+
console.log('Mode: upgrade (overwrite managed files)\n');
|
|
688
|
+
} else {
|
|
689
|
+
console.log('Mode: install (skip existing files)\n');
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
const results = {
|
|
693
|
+
copied: 0,
|
|
694
|
+
updated: 0,
|
|
695
|
+
skipped: 0,
|
|
696
|
+
installedSkills: 0,
|
|
697
|
+
dependencyChecks: 0,
|
|
698
|
+
installedDependencies: 0,
|
|
699
|
+
missingDependencies: 0,
|
|
700
|
+
errors: 0,
|
|
701
|
+
targets: []
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
try {
|
|
705
|
+
for (const platformKey of platforms) {
|
|
706
|
+
const platform = PLATFORMS[platformKey];
|
|
707
|
+
console.log(`${platform.name} (${platform.folder}/)`);
|
|
708
|
+
console.log('-'.repeat(40));
|
|
709
|
+
|
|
710
|
+
copyPlatformFiles(platformKey, results, installerOptions);
|
|
711
|
+
|
|
712
|
+
// Copy ROUTING.md for Claude Code platform
|
|
713
|
+
if (platformKey === 'claude') {
|
|
714
|
+
copyRoutingFile(platformKey, results, installerOptions);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// Copy GEMINI.md for Antigravity platform
|
|
718
|
+
if (platformKey === 'antigravity') {
|
|
719
|
+
copyGeminiFile(platformKey, results, installerOptions);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
results.targets.push(platform.commandsDir);
|
|
723
|
+
console.log();
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// Note: CLAUDE.md and docs/ are generated via /docs init command
|
|
727
|
+
|
|
728
|
+
// Summary
|
|
729
|
+
console.log('╔════════════════════════════════════════════════════════╗');
|
|
730
|
+
console.log('║ Installation Complete! ║');
|
|
731
|
+
console.log('╚════════════════════════════════════════════════════════╝');
|
|
732
|
+
console.log();
|
|
733
|
+
console.log(` Copied Files: ${results.copied}`);
|
|
734
|
+
console.log(` Updated Files: ${results.updated}`);
|
|
735
|
+
console.log(` Skipped Files: ${results.skipped}`);
|
|
736
|
+
console.log(` Installed Skills: ${results.installedSkills > 0 ? 'Yes ✓' : 'No'}`);
|
|
737
|
+
console.log(` Dependency Checks: ${results.dependencyChecks}`);
|
|
738
|
+
console.log(` Installed Deps: ${results.installedDependencies}`);
|
|
739
|
+
console.log(` Missing Deps: ${results.missingDependencies}`);
|
|
740
|
+
console.log(` Target Directories: ${results.targets.join(', ')}`);
|
|
741
|
+
if (results.errors > 0) {
|
|
742
|
+
console.log(` Errors: ${results.errors} ⚠`);
|
|
743
|
+
}
|
|
744
|
+
console.log();
|
|
745
|
+
console.log('Next steps:');
|
|
746
|
+
console.log(' 1. Start your AI editor');
|
|
747
|
+
|
|
748
|
+
// Show platform-specific commands
|
|
749
|
+
for (const platformKey of platforms) {
|
|
750
|
+
const platform = PLATFORMS[platformKey];
|
|
751
|
+
console.log(`\n For ${platform.name}:`);
|
|
752
|
+
console.log(` Run: ${platform.commandPrefix}spec-init <feature-name>`);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
console.log('\n 2. Follow the workflow: requirements - design - tasks - code - test - review');
|
|
756
|
+
if (!installerOptions.upgrade) {
|
|
757
|
+
console.log(' 3. To refresh managed templates later, run installer with --upgrade');
|
|
758
|
+
}
|
|
759
|
+
console.log();
|
|
760
|
+
console.log('Documentation: https://github.com/haposoft/cafekit');
|
|
761
|
+
if (results.missingDependencies > 0) {
|
|
762
|
+
console.log('Note: some dependency templates could not be installed. Please check command/agent directories.');
|
|
763
|
+
}
|
|
764
|
+
console.log();
|
|
765
|
+
|
|
766
|
+
process.exit(results.errors > 0 ? 1 : 0);
|
|
767
|
+
} catch (error) {
|
|
768
|
+
console.error(`\n✗ Installation failed: ${error.message}`);
|
|
769
|
+
process.exit(1);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
main();
|