knowzcode 0.4.0 → 0.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.
- package/.claude-plugin/marketplace.json +61 -61
- package/.claude-plugin/plugin.json +8 -8
- package/LICENSE +121 -121
- package/README.md +379 -354
- package/agents/analyst.md +114 -114
- package/agents/architect.md +200 -200
- package/agents/builder.md +104 -104
- package/agents/closer.md +177 -177
- package/agents/context-scout.md +54 -54
- package/agents/knowledge-migrator.md +349 -349
- package/agents/knowz-scout.md +83 -83
- package/agents/knowz-scribe.md +180 -180
- package/agents/microfix-specialist.md +135 -135
- package/agents/project-advisor.md +111 -111
- package/agents/reviewer.md +172 -172
- package/agents/security-officer.md +194 -194
- package/agents/test-advisor.md +162 -162
- package/agents/update-coordinator.md +394 -394
- package/bin/knowzcode.mjs +1457 -1199
- package/commands/audit.md +328 -328
- package/commands/connect-mcp.md +574 -549
- package/commands/fix.md +107 -107
- package/commands/init.md +616 -500
- package/commands/learn.md +332 -332
- package/commands/plan.md +272 -272
- package/commands/register.md +757 -733
- package/commands/status.md +338 -309
- package/commands/telemetry-setup.md +368 -368
- package/commands/telemetry.md +188 -188
- package/commands/work.md +1204 -1204
- package/knowzcode/automation_manifest.md +59 -59
- package/knowzcode/claude_code_execution.md +431 -431
- package/knowzcode/copilot_execution.md +231 -231
- package/knowzcode/enterprise/compliance_manifest.md +137 -137
- package/knowzcode/enterprise/compliance_status.md +30 -30
- package/knowzcode/enterprise/guidelines/code-quality.md +67 -67
- package/knowzcode/enterprise/guidelines/security.md +355 -355
- package/knowzcode/enterprise/templates/guideline-template.md +55 -55
- package/knowzcode/gitignore.template +13 -13
- package/knowzcode/knowzcode_architecture.md +51 -51
- package/knowzcode/knowzcode_log.md +142 -142
- package/knowzcode/knowzcode_loop.md +601 -596
- package/knowzcode/knowzcode_orchestration.md +66 -66
- package/knowzcode/knowzcode_project.md +48 -48
- package/knowzcode/knowzcode_tracker.md +40 -40
- package/knowzcode/knowzcode_vaults.md +257 -257
- package/knowzcode/mcp_config.md +196 -191
- package/knowzcode/planning/Readme.md +6 -6
- package/knowzcode/platform_adapters.md +2577 -1260
- package/knowzcode/prompts/Execute_Micro_Fix.md +57 -57
- package/knowzcode/prompts/Investigate_Codebase.md +227 -227
- package/knowzcode/prompts/Migrate_Knowledge.md +301 -301
- package/knowzcode/prompts/Refactor_Node.md +72 -72
- package/knowzcode/prompts/Spec_Verification_Checkpoint.md +59 -59
- package/knowzcode/prompts/[LOOP_1A]__Propose_Change_Set.md +52 -52
- package/knowzcode/prompts/[LOOP_1B]__Draft_Specs.md +75 -75
- package/knowzcode/prompts/[LOOP_2A]__Implement_Change_Set.md +55 -55
- package/knowzcode/prompts/[LOOP_2B]__Verify_Implementation.md +72 -72
- package/knowzcode/prompts/[LOOP_3]__Finalize_And_Commit.md +67 -67
- package/knowzcode/specs/Readme.md +10 -10
- package/knowzcode/telemetry_config.md +89 -89
- package/knowzcode/user_preferences.md +120 -120
- package/package.json +53 -53
- package/skills/alias-resolver.json +15 -15
- package/skills/architecture-diff.json +12 -12
- package/skills/check-installation-status.json +14 -14
- package/skills/continue.md +126 -126
- package/skills/environment-guard.json +12 -12
- package/skills/generate-workgroup-id.json +25 -25
- package/skills/install-knowzcode.json +21 -21
- package/skills/load-core-context.json +18 -18
- package/skills/log-entry-builder.json +15 -15
- package/skills/spec-quality-check.json +14 -14
- package/skills/spec-template.json +15 -15
- package/skills/spec-validator.json +25 -25
- package/skills/start-work.md +224 -224
- package/skills/tracker-scan.json +12 -12
- package/skills/tracker-update.json +28 -28
- package/skills/validate-installation.json +14 -14
package/bin/knowzcode.mjs
CHANGED
|
@@ -1,1199 +1,1457 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// KnowzCode CLI — Zero-dependency Node.js installer
|
|
4
|
-
// Usage: npx knowzcode [install|uninstall|upgrade|detect] [options]
|
|
5
|
-
|
|
6
|
-
import { existsSync, mkdirSync, cpSync, readFileSync, writeFileSync, readdirSync, rmSync, statSync } from 'fs';
|
|
7
|
-
import { join, resolve, dirname, basename } from 'path';
|
|
8
|
-
import { fileURLToPath } from 'url';
|
|
9
|
-
import { createInterface } from 'readline/promises';
|
|
10
|
-
|
|
11
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
-
const __dirname = dirname(__filename);
|
|
13
|
-
const PKG_ROOT = resolve(__dirname, '..');
|
|
14
|
-
const VERSION = JSON.parse(readFileSync(join(PKG_ROOT, 'package.json'), 'utf8')).version;
|
|
15
|
-
|
|
16
|
-
// ─── Colors ──────────────────────────────────────────────────────────────────
|
|
17
|
-
|
|
18
|
-
const c = {
|
|
19
|
-
reset: '\x1b[0m',
|
|
20
|
-
bold: '\x1b[1m',
|
|
21
|
-
dim: '\x1b[2m',
|
|
22
|
-
red: '\x1b[31m',
|
|
23
|
-
green: '\x1b[32m',
|
|
24
|
-
yellow: '\x1b[33m',
|
|
25
|
-
blue: '\x1b[34m',
|
|
26
|
-
cyan: '\x1b[36m',
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const log = {
|
|
30
|
-
info: (msg) => console.log(`${c.blue}[INFO]${c.reset} ${msg}`),
|
|
31
|
-
ok: (msg) => console.log(`${c.green}[OK]${c.reset} ${msg}`),
|
|
32
|
-
warn: (msg) => console.log(`${c.yellow}[WARN]${c.reset} ${msg}`),
|
|
33
|
-
err: (msg) => console.error(`${c.red}[ERROR]${c.reset} ${msg}`),
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
// ─── Platform Definitions ────────────────────────────────────────────────────
|
|
37
|
-
|
|
38
|
-
const PLATFORMS = {
|
|
39
|
-
claude: {
|
|
40
|
-
name: 'Claude Code',
|
|
41
|
-
detect: (dir) => existsSync(join(dir, '.claude')) || existsSync(join(dir, '.claude-plugin')),
|
|
42
|
-
adapterPath: null, // Claude uses .claude/ dir structure, not a single adapter file
|
|
43
|
-
},
|
|
44
|
-
codex: {
|
|
45
|
-
name: 'OpenAI Codex',
|
|
46
|
-
detect: (dir) => existsSync(join(dir, 'AGENTS.md')) || existsSync(join(dir, 'AGENTS.override.md')) || existsSync(join(dir, '.codex')),
|
|
47
|
-
adapterPath: (dir) => join(dir, 'AGENTS.md'),
|
|
48
|
-
templateHeader: '## OpenAI Codex (AGENTS.md)',
|
|
49
|
-
},
|
|
50
|
-
gemini: {
|
|
51
|
-
name: 'Gemini CLI',
|
|
52
|
-
detect: (dir) => existsSync(join(dir, 'GEMINI.md')) || existsSync(join(dir, '.gemini')),
|
|
53
|
-
adapterPath: (dir) => join(dir, 'GEMINI.md'),
|
|
54
|
-
templateHeader: '## Google Gemini CLI (GEMINI.md)',
|
|
55
|
-
},
|
|
56
|
-
cursor: {
|
|
57
|
-
name: 'Cursor',
|
|
58
|
-
detect: (dir) => existsSync(join(dir, '.cursor', 'rules')) || existsSync(join(dir, '.cursorrules')),
|
|
59
|
-
adapterPath: (dir) => join(dir, '.cursor', 'rules', 'knowzcode.mdc'),
|
|
60
|
-
templateHeader: '## Cursor (`.cursor/rules/knowzcode.mdc`)',
|
|
61
|
-
},
|
|
62
|
-
copilot: {
|
|
63
|
-
name: 'GitHub Copilot',
|
|
64
|
-
detect: (dir) => existsSync(join(dir, '.github', 'copilot-instructions.md')) || existsSync(join(dir, '.github')),
|
|
65
|
-
adapterPath: (dir) => join(dir, '.github', 'copilot-instructions.md'),
|
|
66
|
-
templateHeader: '## GitHub Copilot',
|
|
67
|
-
},
|
|
68
|
-
windsurf: {
|
|
69
|
-
name: 'Windsurf',
|
|
70
|
-
detect: (dir) => existsSync(join(dir, '.windsurf', 'rules')) || existsSync(join(dir, '.windsurfrules')),
|
|
71
|
-
adapterPath: (dir) => join(dir, '.windsurf', 'rules', 'knowzcode.md'),
|
|
72
|
-
templateHeader: '## Windsurf (`.windsurf/rules/knowzcode.md`)',
|
|
73
|
-
},
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
// ─── CLI Argument Parser ─────────────────────────────────────────────────────
|
|
77
|
-
|
|
78
|
-
function parseArgs(argv) {
|
|
79
|
-
const args = argv.slice(2);
|
|
80
|
-
const opts = {
|
|
81
|
-
command: null,
|
|
82
|
-
target: process.cwd(),
|
|
83
|
-
platforms: [],
|
|
84
|
-
force: false,
|
|
85
|
-
global: false,
|
|
86
|
-
verbose: false,
|
|
87
|
-
agentTeams: false,
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
let i = 0;
|
|
91
|
-
while (i < args.length) {
|
|
92
|
-
const arg = args[i];
|
|
93
|
-
if (arg === '--target' && i + 1 < args.length) {
|
|
94
|
-
opts.target = resolve(args[++i]);
|
|
95
|
-
} else if (arg === '--platforms' && i + 1 < args.length) {
|
|
96
|
-
opts.platforms = args[++i].split(',').map((p) => p.trim().toLowerCase());
|
|
97
|
-
} else if (arg === '--force') {
|
|
98
|
-
opts.force = true;
|
|
99
|
-
} else if (arg === '--global') {
|
|
100
|
-
opts.global = true;
|
|
101
|
-
} else if (arg === '--agent-teams') {
|
|
102
|
-
opts.agentTeams = true;
|
|
103
|
-
} else if (arg === '--verbose') {
|
|
104
|
-
opts.verbose = true;
|
|
105
|
-
} else if (arg === '--help' || arg === '-h') {
|
|
106
|
-
opts.command = 'help';
|
|
107
|
-
} else if (arg === '--version' || arg === '-v') {
|
|
108
|
-
opts.command = 'version';
|
|
109
|
-
} else if (!arg.startsWith('-') && !opts.command) {
|
|
110
|
-
opts.command = arg.toLowerCase();
|
|
111
|
-
}
|
|
112
|
-
i++;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return opts;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// ─── Platform Detection ──────────────────────────────────────────────────────
|
|
119
|
-
|
|
120
|
-
function detectPlatforms(dir) {
|
|
121
|
-
const detected = [];
|
|
122
|
-
for (const [id, platform] of Object.entries(PLATFORMS)) {
|
|
123
|
-
if (platform.detect(dir)) {
|
|
124
|
-
detected.push(id);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return detected;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// ─── Adapter Template Parser ─────────────────────────────────────────────────
|
|
131
|
-
// Returns Map<platformId, { primary: string, files: Map<relativePath, { content, lang }> }>
|
|
132
|
-
|
|
133
|
-
function injectVersion(content) {
|
|
134
|
-
return content.replace(/vX\.Y\.Z/g, `v${VERSION}`);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function extractSection(content, headerIdx) {
|
|
138
|
-
const afterHeader = content.slice(headerIdx);
|
|
139
|
-
const nextSection = afterHeader.search(/\r?\n---\r?\n\r?\n## /);
|
|
140
|
-
return nextSection !== -1 ? afterHeader.slice(0, nextSection) : afterHeader;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function extractFence(text, lang, startFrom = 0) {
|
|
144
|
-
const marker = '```' + lang;
|
|
145
|
-
const fenceStart = text.indexOf(marker, startFrom);
|
|
146
|
-
if (fenceStart === -1) return null;
|
|
147
|
-
const contentStart = text.indexOf('\n', fenceStart) + 1;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const
|
|
213
|
-
const
|
|
214
|
-
if (
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
if (!
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
console.log(
|
|
573
|
-
console.log(
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
if (
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
//
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
if
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
console.log(
|
|
888
|
-
}
|
|
889
|
-
console.log('');
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
const
|
|
909
|
-
const
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
//
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
const
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
log
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
console.log(
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// KnowzCode CLI — Zero-dependency Node.js installer
|
|
4
|
+
// Usage: npx knowzcode [install|uninstall|upgrade|detect] [options]
|
|
5
|
+
|
|
6
|
+
import { existsSync, mkdirSync, cpSync, readFileSync, writeFileSync, readdirSync, rmSync, statSync } from 'fs';
|
|
7
|
+
import { join, resolve, dirname, basename } from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
import { createInterface } from 'readline/promises';
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(__filename);
|
|
13
|
+
const PKG_ROOT = resolve(__dirname, '..');
|
|
14
|
+
const VERSION = JSON.parse(readFileSync(join(PKG_ROOT, 'package.json'), 'utf8')).version;
|
|
15
|
+
|
|
16
|
+
// ─── Colors ──────────────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
const c = {
|
|
19
|
+
reset: '\x1b[0m',
|
|
20
|
+
bold: '\x1b[1m',
|
|
21
|
+
dim: '\x1b[2m',
|
|
22
|
+
red: '\x1b[31m',
|
|
23
|
+
green: '\x1b[32m',
|
|
24
|
+
yellow: '\x1b[33m',
|
|
25
|
+
blue: '\x1b[34m',
|
|
26
|
+
cyan: '\x1b[36m',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const log = {
|
|
30
|
+
info: (msg) => console.log(`${c.blue}[INFO]${c.reset} ${msg}`),
|
|
31
|
+
ok: (msg) => console.log(`${c.green}[OK]${c.reset} ${msg}`),
|
|
32
|
+
warn: (msg) => console.log(`${c.yellow}[WARN]${c.reset} ${msg}`),
|
|
33
|
+
err: (msg) => console.error(`${c.red}[ERROR]${c.reset} ${msg}`),
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// ─── Platform Definitions ────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
const PLATFORMS = {
|
|
39
|
+
claude: {
|
|
40
|
+
name: 'Claude Code',
|
|
41
|
+
detect: (dir) => existsSync(join(dir, '.claude')) || existsSync(join(dir, '.claude-plugin')),
|
|
42
|
+
adapterPath: null, // Claude uses .claude/ dir structure, not a single adapter file
|
|
43
|
+
},
|
|
44
|
+
codex: {
|
|
45
|
+
name: 'OpenAI Codex',
|
|
46
|
+
detect: (dir) => existsSync(join(dir, 'AGENTS.md')) || existsSync(join(dir, 'AGENTS.override.md')) || existsSync(join(dir, '.codex')) || existsSync(join(dir, '.agents')),
|
|
47
|
+
adapterPath: (dir) => join(dir, 'AGENTS.md'),
|
|
48
|
+
templateHeader: '## OpenAI Codex (AGENTS.md)',
|
|
49
|
+
},
|
|
50
|
+
gemini: {
|
|
51
|
+
name: 'Gemini CLI',
|
|
52
|
+
detect: (dir) => existsSync(join(dir, 'GEMINI.md')) || existsSync(join(dir, '.gemini')),
|
|
53
|
+
adapterPath: (dir) => join(dir, 'GEMINI.md'),
|
|
54
|
+
templateHeader: '## Google Gemini CLI (GEMINI.md)',
|
|
55
|
+
},
|
|
56
|
+
cursor: {
|
|
57
|
+
name: 'Cursor',
|
|
58
|
+
detect: (dir) => existsSync(join(dir, '.cursor', 'rules')) || existsSync(join(dir, '.cursorrules')),
|
|
59
|
+
adapterPath: (dir) => join(dir, '.cursor', 'rules', 'knowzcode.mdc'),
|
|
60
|
+
templateHeader: '## Cursor (`.cursor/rules/knowzcode.mdc`)',
|
|
61
|
+
},
|
|
62
|
+
copilot: {
|
|
63
|
+
name: 'GitHub Copilot',
|
|
64
|
+
detect: (dir) => existsSync(join(dir, '.github', 'copilot-instructions.md')) || existsSync(join(dir, '.github')),
|
|
65
|
+
adapterPath: (dir) => join(dir, '.github', 'copilot-instructions.md'),
|
|
66
|
+
templateHeader: '## GitHub Copilot',
|
|
67
|
+
},
|
|
68
|
+
windsurf: {
|
|
69
|
+
name: 'Windsurf',
|
|
70
|
+
detect: (dir) => existsSync(join(dir, '.windsurf', 'rules')) || existsSync(join(dir, '.windsurfrules')),
|
|
71
|
+
adapterPath: (dir) => join(dir, '.windsurf', 'rules', 'knowzcode.md'),
|
|
72
|
+
templateHeader: '## Windsurf (`.windsurf/rules/knowzcode.md`)',
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// ─── CLI Argument Parser ─────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
function parseArgs(argv) {
|
|
79
|
+
const args = argv.slice(2);
|
|
80
|
+
const opts = {
|
|
81
|
+
command: null,
|
|
82
|
+
target: process.cwd(),
|
|
83
|
+
platforms: [],
|
|
84
|
+
force: false,
|
|
85
|
+
global: false,
|
|
86
|
+
verbose: false,
|
|
87
|
+
agentTeams: false,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
let i = 0;
|
|
91
|
+
while (i < args.length) {
|
|
92
|
+
const arg = args[i];
|
|
93
|
+
if (arg === '--target' && i + 1 < args.length) {
|
|
94
|
+
opts.target = resolve(args[++i]);
|
|
95
|
+
} else if (arg === '--platforms' && i + 1 < args.length) {
|
|
96
|
+
opts.platforms = args[++i].split(',').map((p) => p.trim().toLowerCase());
|
|
97
|
+
} else if (arg === '--force') {
|
|
98
|
+
opts.force = true;
|
|
99
|
+
} else if (arg === '--global') {
|
|
100
|
+
opts.global = true;
|
|
101
|
+
} else if (arg === '--agent-teams') {
|
|
102
|
+
opts.agentTeams = true;
|
|
103
|
+
} else if (arg === '--verbose') {
|
|
104
|
+
opts.verbose = true;
|
|
105
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
106
|
+
opts.command = 'help';
|
|
107
|
+
} else if (arg === '--version' || arg === '-v') {
|
|
108
|
+
opts.command = 'version';
|
|
109
|
+
} else if (!arg.startsWith('-') && !opts.command) {
|
|
110
|
+
opts.command = arg.toLowerCase();
|
|
111
|
+
}
|
|
112
|
+
i++;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return opts;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ─── Platform Detection ──────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
function detectPlatforms(dir) {
|
|
121
|
+
const detected = [];
|
|
122
|
+
for (const [id, platform] of Object.entries(PLATFORMS)) {
|
|
123
|
+
if (platform.detect(dir)) {
|
|
124
|
+
detected.push(id);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return detected;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ─── Adapter Template Parser ─────────────────────────────────────────────────
|
|
131
|
+
// Returns Map<platformId, { primary: string, files: Map<relativePath, { content, lang }> }>
|
|
132
|
+
|
|
133
|
+
function injectVersion(content) {
|
|
134
|
+
return content.replace(/vX\.Y\.Z/g, `v${VERSION}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function extractSection(content, headerIdx) {
|
|
138
|
+
const afterHeader = content.slice(headerIdx);
|
|
139
|
+
const nextSection = afterHeader.search(/\r?\n---\r?\n\r?\n## /);
|
|
140
|
+
return nextSection !== -1 ? afterHeader.slice(0, nextSection) : afterHeader;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function extractFence(text, lang, startFrom = 0) {
|
|
144
|
+
const marker = '```' + lang;
|
|
145
|
+
const fenceStart = text.indexOf(marker, startFrom);
|
|
146
|
+
if (fenceStart === -1) return null;
|
|
147
|
+
const contentStart = text.indexOf('\n', fenceStart) + 1;
|
|
148
|
+
// Track nested fences to find the matching closing fence
|
|
149
|
+
let depth = 0;
|
|
150
|
+
let pos = contentStart;
|
|
151
|
+
while (pos < text.length) {
|
|
152
|
+
const nextFence = text.indexOf('\n```', pos);
|
|
153
|
+
if (nextFence === -1) return null;
|
|
154
|
+
const afterBackticks = nextFence + 4;
|
|
155
|
+
const charAfter = afterBackticks < text.length ? text[afterBackticks] : undefined;
|
|
156
|
+
if (charAfter && /\w/.test(charAfter)) {
|
|
157
|
+
// Opening fence (```bash, ```json, etc.)
|
|
158
|
+
depth++;
|
|
159
|
+
} else {
|
|
160
|
+
// Closing fence (``` followed by whitespace/newline/EOF)
|
|
161
|
+
if (depth === 0) {
|
|
162
|
+
return { content: text.slice(contentStart, nextFence), endIdx: afterBackticks };
|
|
163
|
+
}
|
|
164
|
+
depth--;
|
|
165
|
+
}
|
|
166
|
+
pos = afterBackticks;
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function parseCopilotSection(section) {
|
|
172
|
+
const files = new Map();
|
|
173
|
+
|
|
174
|
+
// Section A: copilot-instructions.md (first ```markdown before ### B.)
|
|
175
|
+
const sectionBIdx = section.indexOf('### B.');
|
|
176
|
+
const sectionA = sectionBIdx !== -1 ? section.slice(0, sectionBIdx) : section;
|
|
177
|
+
const primaryFence = extractFence(sectionA, 'markdown');
|
|
178
|
+
if (!primaryFence) return null;
|
|
179
|
+
|
|
180
|
+
// Section B: prompt files (#### kc-*.prompt.md headers)
|
|
181
|
+
const headerRegex = /#### (kc-[\w-]+\.prompt\.md)/g;
|
|
182
|
+
const headers = [];
|
|
183
|
+
let match;
|
|
184
|
+
while ((match = headerRegex.exec(section)) !== null) {
|
|
185
|
+
headers.push({ filename: match[1], index: match.index });
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const sectionCIdx = section.indexOf('### C.');
|
|
189
|
+
for (let i = 0; i < headers.length; i++) {
|
|
190
|
+
const start = headers[i].index;
|
|
191
|
+
const end = i + 1 < headers.length
|
|
192
|
+
? headers[i + 1].index
|
|
193
|
+
: (sectionCIdx !== -1 && sectionCIdx > start ? sectionCIdx : section.length);
|
|
194
|
+
const subSection = section.slice(start, end);
|
|
195
|
+
|
|
196
|
+
const fenceOpen = subSection.indexOf('```markdown');
|
|
197
|
+
if (fenceOpen === -1) continue;
|
|
198
|
+
const contentStart = subSection.indexOf('\n', fenceOpen) + 1;
|
|
199
|
+
// Use lastIndexOf to handle prompt files that contain inner code fences
|
|
200
|
+
const lastFenceClose = subSection.lastIndexOf('\n```');
|
|
201
|
+
if (lastFenceClose <= contentStart) continue;
|
|
202
|
+
|
|
203
|
+
files.set(`.github/prompts/${headers[i].filename}`, {
|
|
204
|
+
content: subSection.slice(contentStart, lastFenceClose),
|
|
205
|
+
lang: 'markdown',
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Section C: .vscode/mcp.json
|
|
210
|
+
if (sectionCIdx !== -1) {
|
|
211
|
+
const sectionDIdx = section.indexOf('### D.', sectionCIdx);
|
|
212
|
+
const sectionC = section.slice(sectionCIdx, sectionDIdx !== -1 ? sectionDIdx : section.length);
|
|
213
|
+
const jsonFence = extractFence(sectionC, 'json');
|
|
214
|
+
if (jsonFence) {
|
|
215
|
+
files.set('.vscode/mcp.json', { content: jsonFence.content, lang: 'json' });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return { primary: primaryFence.content, files };
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function parseGeminiSection(section) {
|
|
223
|
+
const files = new Map();
|
|
224
|
+
|
|
225
|
+
// Extract TOML blocks: ```toml fences with # .gemini/commands/kc/{name}.toml comment
|
|
226
|
+
let searchFrom = 0;
|
|
227
|
+
while (true) {
|
|
228
|
+
const fenceStart = section.indexOf('```toml', searchFrom);
|
|
229
|
+
if (fenceStart === -1) break;
|
|
230
|
+
const contentStart = section.indexOf('\n', fenceStart) + 1;
|
|
231
|
+
const fenceEnd = section.indexOf('\n```', contentStart);
|
|
232
|
+
if (fenceEnd === -1) break;
|
|
233
|
+
const tomlContent = section.slice(contentStart, fenceEnd);
|
|
234
|
+
const pathMatch = tomlContent.match(/^# (\.gemini\/commands\/kc\/[\w-]+\.toml)/);
|
|
235
|
+
if (pathMatch) {
|
|
236
|
+
files.set(pathMatch[1], { content: tomlContent, lang: 'toml' });
|
|
237
|
+
}
|
|
238
|
+
searchFrom = fenceEnd + 4;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Skill files: #### .gemini/skills/kc-{name}/SKILL.md headers
|
|
242
|
+
const skillRegex = /#### (\.gemini\/skills\/kc-[\w-]+\/SKILL\.md)/g;
|
|
243
|
+
const skillHeaders = [];
|
|
244
|
+
let skillMatch;
|
|
245
|
+
while ((skillMatch = skillRegex.exec(section)) !== null) {
|
|
246
|
+
skillHeaders.push({ filepath: skillMatch[1], index: skillMatch.index });
|
|
247
|
+
}
|
|
248
|
+
// Subagent files: #### .gemini/agents/kc-{name}.md headers
|
|
249
|
+
const agentRegex = /#### (\.gemini\/agents\/kc-[\w-]+\.md)/g;
|
|
250
|
+
const agentHeaders = [];
|
|
251
|
+
let agentMatch;
|
|
252
|
+
while ((agentMatch = agentRegex.exec(section)) !== null) {
|
|
253
|
+
agentHeaders.push({ filepath: agentMatch[1], index: agentMatch.index });
|
|
254
|
+
}
|
|
255
|
+
// Combine all subsection headers for boundary detection
|
|
256
|
+
const allSubHeaders = [...skillHeaders, ...agentHeaders].sort((a, b) => a.index - b.index);
|
|
257
|
+
|
|
258
|
+
for (let i = 0; i < allSubHeaders.length; i++) {
|
|
259
|
+
const start = allSubHeaders[i].index;
|
|
260
|
+
const end = i + 1 < allSubHeaders.length ? allSubHeaders[i + 1].index : section.length;
|
|
261
|
+
const subSection = section.slice(start, end);
|
|
262
|
+
const fence = extractFence(subSection, 'markdown');
|
|
263
|
+
if (fence) {
|
|
264
|
+
files.set(allSubHeaders[i].filepath, { content: fence.content, lang: 'markdown' });
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Primary: ```markdown fence (GEMINI.md) — extract from content BEFORE first skill/subagent header
|
|
269
|
+
const firstSubHeader = allSubHeaders.length > 0 ? allSubHeaders[0].index : section.length;
|
|
270
|
+
const primarySection = section.slice(0, firstSubHeader);
|
|
271
|
+
const primaryFence = extractFence(primarySection, 'markdown');
|
|
272
|
+
if (!primaryFence) return null;
|
|
273
|
+
|
|
274
|
+
return { primary: primaryFence.content, files };
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function parseCodexSection(section) {
|
|
278
|
+
const files = new Map();
|
|
279
|
+
|
|
280
|
+
// Primary: first ```markdown fence (AGENTS.md)
|
|
281
|
+
const primaryFence = extractFence(section, 'markdown');
|
|
282
|
+
if (!primaryFence) return null;
|
|
283
|
+
|
|
284
|
+
// Skill files: #### .agents/skills/kc-{name}/SKILL.md headers
|
|
285
|
+
const headerRegex = /#### (\.agents\/skills\/kc-[\w-]+\/SKILL\.md)/g;
|
|
286
|
+
const headers = [];
|
|
287
|
+
let match;
|
|
288
|
+
while ((match = headerRegex.exec(section)) !== null) {
|
|
289
|
+
headers.push({ filepath: match[1], index: match.index });
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
for (let i = 0; i < headers.length; i++) {
|
|
293
|
+
const start = headers[i].index;
|
|
294
|
+
const end = i + 1 < headers.length ? headers[i + 1].index : section.length;
|
|
295
|
+
const subSection = section.slice(start, end);
|
|
296
|
+
const fence = extractFence(subSection, 'markdown');
|
|
297
|
+
if (fence) {
|
|
298
|
+
files.set(headers[i].filepath, { content: fence.content, lang: 'markdown' });
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return { primary: primaryFence.content, files };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function parseSimpleSection(section) {
|
|
306
|
+
const primaryFence = extractFence(section, 'markdown');
|
|
307
|
+
if (!primaryFence) return null;
|
|
308
|
+
return { primary: primaryFence.content, files: new Map() };
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function parseAdapterTemplates() {
|
|
312
|
+
const adaptersPath = join(PKG_ROOT, 'knowzcode', 'platform_adapters.md');
|
|
313
|
+
if (!existsSync(adaptersPath)) {
|
|
314
|
+
log.warn('platform_adapters.md not found — adapter generation will be skipped');
|
|
315
|
+
return new Map();
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const content = readFileSync(adaptersPath, 'utf8');
|
|
319
|
+
const templates = new Map();
|
|
320
|
+
|
|
321
|
+
for (const [id, platform] of Object.entries(PLATFORMS)) {
|
|
322
|
+
if (!platform.templateHeader) continue;
|
|
323
|
+
|
|
324
|
+
const headerIdx = content.indexOf(platform.templateHeader);
|
|
325
|
+
if (headerIdx === -1) continue;
|
|
326
|
+
|
|
327
|
+
const section = extractSection(content, headerIdx);
|
|
328
|
+
let result;
|
|
329
|
+
switch (id) {
|
|
330
|
+
case 'copilot': result = parseCopilotSection(section); break;
|
|
331
|
+
case 'gemini': result = parseGeminiSection(section); break;
|
|
332
|
+
case 'codex': result = parseCodexSection(section); break;
|
|
333
|
+
default: result = parseSimpleSection(section); break;
|
|
334
|
+
}
|
|
335
|
+
if (result) templates.set(id, result);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return templates;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// ─── File Copy Helpers ───────────────────────────────────────────────────────
|
|
342
|
+
|
|
343
|
+
function ensureDir(dir) {
|
|
344
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function copyDirContents(src, dst) {
|
|
348
|
+
ensureDir(dst);
|
|
349
|
+
if (!existsSync(src)) return;
|
|
350
|
+
|
|
351
|
+
for (const entry of readdirSync(src, { withFileTypes: true })) {
|
|
352
|
+
const srcPath = join(src, entry.name);
|
|
353
|
+
const dstPath = join(dst, entry.name);
|
|
354
|
+
if (entry.isDirectory()) {
|
|
355
|
+
copyDirContents(srcPath, dstPath);
|
|
356
|
+
} else {
|
|
357
|
+
writeFileSync(dstPath, readFileSync(srcPath));
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function listFilesRecursive(dir, base = dir) {
|
|
363
|
+
const files = [];
|
|
364
|
+
if (!existsSync(dir)) return files;
|
|
365
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
366
|
+
const full = join(dir, entry.name);
|
|
367
|
+
if (entry.isDirectory()) {
|
|
368
|
+
files.push(...listFilesRecursive(full, base));
|
|
369
|
+
} else {
|
|
370
|
+
files.push(full);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return files;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ─── Marketplace Config ──────────────────────────────────────────────────────
|
|
377
|
+
|
|
378
|
+
function setMarketplaceConfig(claudeDir) {
|
|
379
|
+
ensureDir(claudeDir);
|
|
380
|
+
const settingsFile = join(claudeDir, 'settings.json');
|
|
381
|
+
let settings = {};
|
|
382
|
+
|
|
383
|
+
if (existsSync(settingsFile)) {
|
|
384
|
+
try {
|
|
385
|
+
settings = JSON.parse(readFileSync(settingsFile, 'utf8'));
|
|
386
|
+
} catch {
|
|
387
|
+
settings = {};
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (!settings.extraKnownMarketplaces) settings.extraKnownMarketplaces = {};
|
|
392
|
+
settings.extraKnownMarketplaces.knowzcode = {
|
|
393
|
+
source: { source: 'github', repo: 'knowz-io/knowzcode' },
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
writeFileSync(settingsFile, JSON.stringify(settings, null, 2) + '\n');
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function removeMarketplaceConfig(claudeDir) {
|
|
400
|
+
const settingsFile = join(claudeDir, 'settings.json');
|
|
401
|
+
if (!existsSync(settingsFile)) return;
|
|
402
|
+
|
|
403
|
+
try {
|
|
404
|
+
const settings = JSON.parse(readFileSync(settingsFile, 'utf8'));
|
|
405
|
+
if (settings.extraKnownMarketplaces && settings.extraKnownMarketplaces.knowzcode) {
|
|
406
|
+
delete settings.extraKnownMarketplaces.knowzcode;
|
|
407
|
+
writeFileSync(settingsFile, JSON.stringify(settings, null, 2) + '\n');
|
|
408
|
+
}
|
|
409
|
+
} catch {
|
|
410
|
+
// Ignore parse errors
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// ─── Gemini MCP Config Helpers ────────────────────────────────────────────────
|
|
415
|
+
|
|
416
|
+
function writeGeminiMcpConfig(settingsPath, apiKey, projectPath, endpoint = 'https://mcp.knowz.io/mcp') {
|
|
417
|
+
ensureDir(dirname(settingsPath));
|
|
418
|
+
let settings = {};
|
|
419
|
+
if (existsSync(settingsPath)) {
|
|
420
|
+
try {
|
|
421
|
+
settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
|
|
422
|
+
} catch {
|
|
423
|
+
settings = {};
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
if (!settings.mcpServers) settings.mcpServers = {};
|
|
427
|
+
settings.mcpServers.knowz = {
|
|
428
|
+
url: endpoint,
|
|
429
|
+
headers: {
|
|
430
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
431
|
+
'X-Project-Path': projectPath,
|
|
432
|
+
},
|
|
433
|
+
};
|
|
434
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function removeGeminiMcpConfig(settingsPath) {
|
|
438
|
+
if (!existsSync(settingsPath)) return false;
|
|
439
|
+
try {
|
|
440
|
+
const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
|
|
441
|
+
if (settings.mcpServers && settings.mcpServers.knowz) {
|
|
442
|
+
delete settings.mcpServers.knowz;
|
|
443
|
+
if (Object.keys(settings.mcpServers).length === 0) {
|
|
444
|
+
delete settings.mcpServers;
|
|
445
|
+
}
|
|
446
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
449
|
+
} catch {
|
|
450
|
+
// Ignore parse errors
|
|
451
|
+
}
|
|
452
|
+
return false;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function hasGeminiMcpConfig(settingsPath) {
|
|
456
|
+
if (!existsSync(settingsPath)) return false;
|
|
457
|
+
try {
|
|
458
|
+
const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
|
|
459
|
+
return !!(settings.mcpServers && settings.mcpServers.knowz);
|
|
460
|
+
} catch {
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// ─── Stale File Cleanup ─────────────────────────────────────────────────────
|
|
466
|
+
|
|
467
|
+
function removeStaleFiles(sourceDir, targetDir) {
|
|
468
|
+
if (!existsSync(targetDir) || !existsSync(sourceDir)) return;
|
|
469
|
+
|
|
470
|
+
const sourceFiles = new Set(
|
|
471
|
+
readdirSync(sourceDir)
|
|
472
|
+
.filter((f) => f.endsWith('.md'))
|
|
473
|
+
);
|
|
474
|
+
|
|
475
|
+
for (const entry of readdirSync(targetDir)) {
|
|
476
|
+
if (entry.endsWith('.md') && !sourceFiles.has(entry)) {
|
|
477
|
+
const stale = join(targetDir, entry);
|
|
478
|
+
if (existsSync(stale) && statSync(stale).isFile()) {
|
|
479
|
+
log.info(`Removing stale file: ${stale}`);
|
|
480
|
+
rmSync(stale, { force: true });
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// ─── Tracker & Log Initializers ──────────────────────────────────────────────
|
|
487
|
+
|
|
488
|
+
function initTracker(filePath) {
|
|
489
|
+
writeFileSync(filePath, `# KnowzCode - Status Map
|
|
490
|
+
|
|
491
|
+
**Purpose:** This document tracks the development status of all implementable components (NodeIDs) defined in \`knowzcode_architecture.md\`.
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
**Progress: 0%**
|
|
495
|
+
---
|
|
496
|
+
|
|
497
|
+
| Status | WorkGroupID | Node ID | Label | Dependencies | Logical Grouping | Spec Link | Classification | Notes / Issues |
|
|
498
|
+
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
|
499
|
+
| | | | | | | | | |
|
|
500
|
+
|
|
501
|
+
---
|
|
502
|
+
### Status Legend:
|
|
503
|
+
|
|
504
|
+
* ⚪️ **\`[TODO]\`**: Task is defined and ready to be picked up if dependencies are met.
|
|
505
|
+
* 📝 **\`[NEEDS_SPEC]\`**: Node has been identified but requires a detailed specification.
|
|
506
|
+
* ◆ **\`[WIP]\`**: Work In Progress. The KnowzCode AI Agent is currently working on this node.
|
|
507
|
+
* 🟢 **\`[VERIFIED]\`**: Node has been implemented and verified.
|
|
508
|
+
* ❗ **\`[ISSUE]\`**: A significant issue or blocker has been identified.
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
*(This table will be populated as you define your architecture and NodeIDs.)*
|
|
512
|
+
`);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function initLog(filePath) {
|
|
516
|
+
const ts = new Date().toISOString().replace('T', ' ').slice(0, 19);
|
|
517
|
+
writeFileSync(filePath, `# KnowzCode - Operational Record
|
|
518
|
+
|
|
519
|
+
**Purpose:** Chronological record of significant events, decisions, and verification outcomes.
|
|
520
|
+
|
|
521
|
+
---
|
|
522
|
+
|
|
523
|
+
## Section 1: Operational Log
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
**[NEWEST ENTRIES APPEAR HERE - DO NOT REMOVE THIS MARKER]**
|
|
527
|
+
---
|
|
528
|
+
**Type:** SystemInitialization
|
|
529
|
+
**Timestamp:** ${ts}
|
|
530
|
+
**NodeID(s):** Project-Wide
|
|
531
|
+
**Logged By:** knowzcode-cli
|
|
532
|
+
**Details:**
|
|
533
|
+
KnowzCode framework installed via \`npx knowzcode\`.
|
|
534
|
+
- Framework files initialized
|
|
535
|
+
- Ready for first feature
|
|
536
|
+
---
|
|
537
|
+
|
|
538
|
+
## Section 2: Reference Quality Criteria (ARC-Based Verification)
|
|
539
|
+
|
|
540
|
+
### Core Quality Criteria
|
|
541
|
+
1. **Maintainability:** Ease of modification, clarity of code and design.
|
|
542
|
+
2. **Reliability:** Robustness of error handling, fault tolerance.
|
|
543
|
+
3. **Testability:** Adequacy of unit test coverage, ease of testing.
|
|
544
|
+
4. **Performance:** Responsiveness, efficiency in resource utilization.
|
|
545
|
+
5. **Security:** Resistance to common vulnerabilities.
|
|
546
|
+
|
|
547
|
+
### Structural Criteria
|
|
548
|
+
6. **Readability:** Code clarity, adherence to naming conventions.
|
|
549
|
+
7. **Complexity Management:** Avoidance of overly complex logic.
|
|
550
|
+
8. **Modularity:** Adherence to Single Responsibility Principle.
|
|
551
|
+
9. **Code Duplication (DRY):** Minimization of redundant code.
|
|
552
|
+
10. **Standards Compliance:** Adherence to language best practices.
|
|
553
|
+
|
|
554
|
+
*(Refer to these criteria during ARC-Based Verification.)*
|
|
555
|
+
`);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// ─── Interactive Prompt ──────────────────────────────────────────────────────
|
|
559
|
+
|
|
560
|
+
async function promptPlatforms(detected) {
|
|
561
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
562
|
+
const ids = Object.keys(PLATFORMS);
|
|
563
|
+
|
|
564
|
+
console.log('');
|
|
565
|
+
console.log(`${c.bold}Select platforms to generate adapters for:${c.reset}`);
|
|
566
|
+
console.log('');
|
|
567
|
+
ids.forEach((id, i) => {
|
|
568
|
+
const p = PLATFORMS[id];
|
|
569
|
+
const tag = detected.includes(id) ? ` ${c.green}(detected)${c.reset}` : '';
|
|
570
|
+
console.log(` [${i + 1}] ${p.name}${tag}`);
|
|
571
|
+
});
|
|
572
|
+
console.log(` [A] All platforms`);
|
|
573
|
+
console.log(` [S] Skip adapters (core framework only)`);
|
|
574
|
+
console.log('');
|
|
575
|
+
|
|
576
|
+
const answer = await rl.question('Select platforms (comma-separated, e.g. 1,2): ');
|
|
577
|
+
rl.close();
|
|
578
|
+
|
|
579
|
+
const trimmed = answer.trim().toUpperCase();
|
|
580
|
+
if (trimmed === 'S' || trimmed === '') return [];
|
|
581
|
+
if (trimmed === 'A') return ids;
|
|
582
|
+
|
|
583
|
+
const selected = [];
|
|
584
|
+
for (const part of trimmed.split(',')) {
|
|
585
|
+
const num = parseInt(part.trim(), 10);
|
|
586
|
+
if (num >= 1 && num <= ids.length) {
|
|
587
|
+
selected.push(ids[num - 1]);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return [...new Set(selected)];
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
async function promptConfirm(message) {
|
|
594
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
595
|
+
const answer = await rl.question(`${message} [y/N]: `);
|
|
596
|
+
rl.close();
|
|
597
|
+
return answer.trim().toLowerCase() === 'y' || answer.trim().toLowerCase() === 'yes';
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// ─── Agent Teams Enablement ──────────────────────────────────────────────────
|
|
601
|
+
|
|
602
|
+
function enableAgentTeams(claudeDir, isGlobal) {
|
|
603
|
+
ensureDir(claudeDir);
|
|
604
|
+
const settingsFile = join(claudeDir, isGlobal ? 'settings.json' : 'settings.local.json');
|
|
605
|
+
|
|
606
|
+
let settings = {};
|
|
607
|
+
if (existsSync(settingsFile)) {
|
|
608
|
+
try {
|
|
609
|
+
settings = JSON.parse(readFileSync(settingsFile, 'utf8'));
|
|
610
|
+
} catch {
|
|
611
|
+
settings = {};
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if (!settings.env) settings.env = {};
|
|
616
|
+
settings.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = '1';
|
|
617
|
+
|
|
618
|
+
writeFileSync(settingsFile, JSON.stringify(settings, null, 2) + '\n');
|
|
619
|
+
log.ok(`Agent Teams enabled in ${settingsFile}`);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// ─── Commands ────────────────────────────────────────────────────────────────
|
|
623
|
+
|
|
624
|
+
// DETECT
|
|
625
|
+
function cmdDetect(opts) {
|
|
626
|
+
const dir = opts.target;
|
|
627
|
+
console.log('');
|
|
628
|
+
console.log(`${c.bold}KnowzCode Platform Detection${c.reset}`);
|
|
629
|
+
console.log(`${c.dim}Scanning: ${dir}${c.reset}`);
|
|
630
|
+
console.log('');
|
|
631
|
+
|
|
632
|
+
const detected = detectPlatforms(dir);
|
|
633
|
+
const hasKnowzcode = existsSync(join(dir, 'knowzcode'));
|
|
634
|
+
|
|
635
|
+
console.log(` KnowzCode framework: ${hasKnowzcode ? `${c.green}installed${c.reset}` : `${c.dim}not found${c.reset}`}`);
|
|
636
|
+
|
|
637
|
+
if (hasKnowzcode) {
|
|
638
|
+
const versionFile = join(dir, 'knowzcode', '.knowzcode-version');
|
|
639
|
+
if (existsSync(versionFile)) {
|
|
640
|
+
const ver = readFileSync(versionFile, 'utf8').trim();
|
|
641
|
+
console.log(` Installed version: ${c.cyan}${ver}${c.reset}`);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
console.log('');
|
|
646
|
+
console.log(` ${c.bold}Platforms:${c.reset}`);
|
|
647
|
+
|
|
648
|
+
for (const [id, platform] of Object.entries(PLATFORMS)) {
|
|
649
|
+
const found = detected.includes(id);
|
|
650
|
+
const indicator = found ? `${c.green}detected${c.reset}` : `${c.dim}not detected${c.reset}`;
|
|
651
|
+
console.log(` ${platform.name.padEnd(18)} ${indicator}`);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
console.log('');
|
|
655
|
+
if (detected.length === 0) {
|
|
656
|
+
console.log(` No platforms detected. Run ${c.cyan}npx knowzcode install${c.reset} to set up.`);
|
|
657
|
+
} else {
|
|
658
|
+
console.log(` ${detected.length} platform(s) detected.`);
|
|
659
|
+
}
|
|
660
|
+
console.log('');
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// INSTALL
|
|
664
|
+
async function cmdInstall(opts) {
|
|
665
|
+
const dir = opts.target;
|
|
666
|
+
const kcDir = join(dir, 'knowzcode');
|
|
667
|
+
|
|
668
|
+
console.log('');
|
|
669
|
+
console.log(`${c.bold}KnowzCode Install${c.reset}`);
|
|
670
|
+
console.log(`${c.dim}Target: ${dir}${c.reset}`);
|
|
671
|
+
console.log('');
|
|
672
|
+
|
|
673
|
+
// Check for existing installation
|
|
674
|
+
if (existsSync(kcDir) && !opts.force) {
|
|
675
|
+
log.warn('KnowzCode already installed at ' + kcDir);
|
|
676
|
+
log.warn('Use --force to overwrite.');
|
|
677
|
+
process.exit(1);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
if (!existsSync(dir)) {
|
|
681
|
+
log.err('Target directory does not exist: ' + dir);
|
|
682
|
+
process.exit(1);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// 1. Copy knowzcode/ template directory
|
|
686
|
+
log.info('Installing core framework...');
|
|
687
|
+
const srcKc = join(PKG_ROOT, 'knowzcode');
|
|
688
|
+
ensureDir(kcDir);
|
|
689
|
+
ensureDir(join(kcDir, 'specs'));
|
|
690
|
+
ensureDir(join(kcDir, 'workgroups'));
|
|
691
|
+
ensureDir(join(kcDir, 'prompts'));
|
|
692
|
+
|
|
693
|
+
// Create workgroups/README.md (workgroups/ is gitignored and excluded from npm)
|
|
694
|
+
writeFileSync(join(kcDir, 'workgroups', 'README.md'), '# WorkGroups\n\nSession-specific WorkGroup files are stored here.\nThis directory is gitignored — contents are local to each checkout.\n');
|
|
695
|
+
|
|
696
|
+
// Copy .md files (skip tracker and log — generate fresh)
|
|
697
|
+
for (const entry of readdirSync(srcKc)) {
|
|
698
|
+
const srcPath = join(srcKc, entry);
|
|
699
|
+
const stat = statSync(srcPath);
|
|
700
|
+
if (stat.isFile() && entry.endsWith('.md') && entry !== 'knowzcode_tracker.md' && entry !== 'knowzcode_log.md') {
|
|
701
|
+
writeFileSync(join(kcDir, entry), readFileSync(srcPath));
|
|
702
|
+
} else if (stat.isFile() && !entry.endsWith('.md')) {
|
|
703
|
+
// Copy non-md files, handling gitignore.template → .gitignore rename
|
|
704
|
+
if (entry === 'gitignore.template') {
|
|
705
|
+
writeFileSync(join(kcDir, '.gitignore'), readFileSync(srcPath));
|
|
706
|
+
} else {
|
|
707
|
+
writeFileSync(join(kcDir, entry), readFileSync(srcPath));
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// Copy prompts/
|
|
713
|
+
if (existsSync(join(srcKc, 'prompts'))) {
|
|
714
|
+
copyDirContents(join(srcKc, 'prompts'), join(kcDir, 'prompts'));
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// Copy specs readme
|
|
718
|
+
if (existsSync(join(srcKc, 'specs', 'Readme.md'))) {
|
|
719
|
+
writeFileSync(join(kcDir, 'specs', 'Readme.md'), readFileSync(join(srcKc, 'specs', 'Readme.md')));
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// Copy enterprise/ if exists
|
|
723
|
+
if (existsSync(join(srcKc, 'enterprise'))) {
|
|
724
|
+
copyDirContents(join(srcKc, 'enterprise'), join(kcDir, 'enterprise'));
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// Initialize fresh tracker and log
|
|
728
|
+
initTracker(join(kcDir, 'knowzcode_tracker.md'));
|
|
729
|
+
initLog(join(kcDir, 'knowzcode_log.md'));
|
|
730
|
+
|
|
731
|
+
// Write version marker
|
|
732
|
+
writeFileSync(join(kcDir, '.knowzcode-version'), VERSION + '\n');
|
|
733
|
+
|
|
734
|
+
log.ok('Core framework installed');
|
|
735
|
+
|
|
736
|
+
// 2. Platform detection + selection
|
|
737
|
+
const detected = detectPlatforms(dir);
|
|
738
|
+
let selectedPlatforms;
|
|
739
|
+
|
|
740
|
+
if (opts.platforms.length > 0) {
|
|
741
|
+
if (opts.platforms.includes('all')) {
|
|
742
|
+
selectedPlatforms = Object.keys(PLATFORMS);
|
|
743
|
+
} else {
|
|
744
|
+
selectedPlatforms = opts.platforms.filter((p) => p in PLATFORMS);
|
|
745
|
+
}
|
|
746
|
+
} else if (opts.force) {
|
|
747
|
+
// Non-interactive mode with --force: install for detected platforms only
|
|
748
|
+
selectedPlatforms = detected;
|
|
749
|
+
} else {
|
|
750
|
+
selectedPlatforms = await promptPlatforms(detected);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// 3. Generate adapters
|
|
754
|
+
const templates = parseAdapterTemplates();
|
|
755
|
+
const adapterFiles = [];
|
|
756
|
+
|
|
757
|
+
for (const platformId of selectedPlatforms) {
|
|
758
|
+
const platform = PLATFORMS[platformId];
|
|
759
|
+
|
|
760
|
+
if (platformId === 'claude') {
|
|
761
|
+
// Claude Code: copy agents, commands, skills
|
|
762
|
+
const claudeDir = opts.global ? join(process.env.HOME || process.env.USERPROFILE || '~', '.claude') : join(dir, '.claude');
|
|
763
|
+
|
|
764
|
+
log.info(`Installing Claude Code components to ${claudeDir}/`);
|
|
765
|
+
|
|
766
|
+
// Remove stale files before copying on --force
|
|
767
|
+
if (opts.force) {
|
|
768
|
+
removeStaleFiles(join(PKG_ROOT, 'commands'), join(claudeDir, 'commands'));
|
|
769
|
+
removeStaleFiles(join(PKG_ROOT, 'agents'), join(claudeDir, 'agents'));
|
|
770
|
+
removeStaleFiles(join(PKG_ROOT, 'skills'), join(claudeDir, 'skills'));
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
copyDirContents(join(PKG_ROOT, 'commands'), join(claudeDir, 'commands'));
|
|
774
|
+
copyDirContents(join(PKG_ROOT, 'agents'), join(claudeDir, 'agents'));
|
|
775
|
+
copyDirContents(join(PKG_ROOT, 'skills'), join(claudeDir, 'skills'));
|
|
776
|
+
|
|
777
|
+
// Pre-register marketplace in settings.json
|
|
778
|
+
setMarketplaceConfig(claudeDir);
|
|
779
|
+
|
|
780
|
+
adapterFiles.push(claudeDir + '/commands/', claudeDir + '/agents/', claudeDir + '/skills/');
|
|
781
|
+
} else {
|
|
782
|
+
// Other platforms: extract template and write adapter + additional files
|
|
783
|
+
const templateSet = templates.get(platformId);
|
|
784
|
+
if (!templateSet) {
|
|
785
|
+
log.warn(`No adapter template found for ${platform.name} — skipping`);
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// Write primary adapter file
|
|
790
|
+
const adapterFile = platform.adapterPath(dir);
|
|
791
|
+
ensureDir(dirname(adapterFile));
|
|
792
|
+
writeFileSync(adapterFile, injectVersion(templateSet.primary));
|
|
793
|
+
adapterFiles.push(adapterFile);
|
|
794
|
+
log.ok(`${platform.name} adapter: ${adapterFile}`);
|
|
795
|
+
|
|
796
|
+
// Write additional files (prompts, TOMLs, skills, subagents)
|
|
797
|
+
for (const [relativePath, { content }] of templateSet.files) {
|
|
798
|
+
let filePath;
|
|
799
|
+
if (platformId === 'codex' && opts.global && relativePath.startsWith('.agents/skills/')) {
|
|
800
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || '~';
|
|
801
|
+
filePath = join(homeDir, relativePath);
|
|
802
|
+
} else if (platformId === 'gemini' && opts.global && relativePath.startsWith('.gemini/skills/')) {
|
|
803
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || '~';
|
|
804
|
+
filePath = join(homeDir, relativePath);
|
|
805
|
+
} else {
|
|
806
|
+
filePath = join(dir, relativePath);
|
|
807
|
+
}
|
|
808
|
+
ensureDir(dirname(filePath));
|
|
809
|
+
writeFileSync(filePath, injectVersion(content));
|
|
810
|
+
adapterFiles.push(filePath);
|
|
811
|
+
}
|
|
812
|
+
if (templateSet.files.size > 0) {
|
|
813
|
+
log.ok(` + ${templateSet.files.size} additional file(s)`);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// Clean up legacy .codex/skills/kc/ if present (migrated to .agents/skills/)
|
|
817
|
+
if (platformId === 'codex') {
|
|
818
|
+
const legacySkillDir = join(dir, '.codex', 'skills', 'kc');
|
|
819
|
+
if (existsSync(legacySkillDir)) {
|
|
820
|
+
log.info('Removing legacy .codex/skills/kc/ (migrated to .agents/skills/)');
|
|
821
|
+
rmSync(legacySkillDir, { recursive: true, force: true });
|
|
822
|
+
}
|
|
823
|
+
if (opts.global) {
|
|
824
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || '~';
|
|
825
|
+
const legacyGlobal = join(homeDir, '.codex', 'skills', 'kc');
|
|
826
|
+
if (existsSync(legacyGlobal)) {
|
|
827
|
+
log.info('Removing legacy global ~/.codex/skills/kc/');
|
|
828
|
+
rmSync(legacyGlobal, { recursive: true, force: true });
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// 3.5. Gemini MCP config offer (when Gemini is selected and not --force)
|
|
836
|
+
if (selectedPlatforms.includes('gemini') && !opts.global && !opts.force) {
|
|
837
|
+
console.log('');
|
|
838
|
+
console.log(`${c.bold}Gemini MCP Configuration${c.reset}`);
|
|
839
|
+
console.log(`MCP enables vector search, vault access, and AI-powered Q&A.`);
|
|
840
|
+
const wantMcp = await promptConfirm('Configure MCP for Gemini CLI? (requires API key)');
|
|
841
|
+
if (wantMcp) {
|
|
842
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
843
|
+
const apiKey = await rl.question('Enter your KnowzCode API key: ');
|
|
844
|
+
rl.close();
|
|
845
|
+
if (apiKey.trim()) {
|
|
846
|
+
const settingsPath = join(dir, '.gemini', 'settings.json');
|
|
847
|
+
writeGeminiMcpConfig(settingsPath, apiKey.trim(), dir);
|
|
848
|
+
log.ok('Gemini MCP configured in .gemini/settings.json');
|
|
849
|
+
} else {
|
|
850
|
+
log.warn('No API key provided — skipping MCP config. Use /kc:connect-mcp later.');
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
// 4. Agent Teams enablement
|
|
856
|
+
const agentTeamsClaudeDir = opts.global
|
|
857
|
+
? join(process.env.HOME || process.env.USERPROFILE || '~', '.claude')
|
|
858
|
+
: join(dir, '.claude');
|
|
859
|
+
let agentTeamsEnabled = false;
|
|
860
|
+
if (opts.agentTeams) {
|
|
861
|
+
enableAgentTeams(agentTeamsClaudeDir, opts.global);
|
|
862
|
+
agentTeamsEnabled = true;
|
|
863
|
+
} else if (selectedPlatforms.includes('claude') && !opts.force) {
|
|
864
|
+
// Interactive prompt for Claude Code users
|
|
865
|
+
console.log('');
|
|
866
|
+
console.log(`${c.bold}Agent Teams${c.reset} enables multi-agent coordination where specialized`);
|
|
867
|
+
console.log(`teammates handle each workflow phase. ${c.dim}(experimental)${c.reset}`);
|
|
868
|
+
const wantTeams = await promptConfirm('Enable Agent Teams? (recommended for Claude Code)');
|
|
869
|
+
if (wantTeams) {
|
|
870
|
+
enableAgentTeams(agentTeamsClaudeDir, opts.global);
|
|
871
|
+
agentTeamsEnabled = true;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// 5. Summary
|
|
876
|
+
console.log('');
|
|
877
|
+
console.log(`${c.green}${c.bold}Installation complete!${c.reset}`);
|
|
878
|
+
console.log('');
|
|
879
|
+
console.log(' Framework: ' + kcDir + '/');
|
|
880
|
+
if (adapterFiles.length > 0) {
|
|
881
|
+
console.log(' Adapters:');
|
|
882
|
+
for (const f of adapterFiles) {
|
|
883
|
+
console.log(' ' + f);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
if (agentTeamsEnabled) {
|
|
887
|
+
console.log(' Agent Teams: enabled');
|
|
888
|
+
}
|
|
889
|
+
console.log('');
|
|
890
|
+
console.log(`${c.bold}Next steps:${c.reset}`);
|
|
891
|
+
console.log(' 1. Edit knowzcode/knowzcode_project.md — set project name, stack, standards');
|
|
892
|
+
console.log(' 2. Edit knowzcode/environment_context.md — configure build/test commands');
|
|
893
|
+
if (selectedPlatforms.includes('claude')) {
|
|
894
|
+
console.log(' 3. Install the KnowzCode plugin (recommended):');
|
|
895
|
+
console.log(' /plugin install kc@knowzcode');
|
|
896
|
+
console.log(' 4. Start building:');
|
|
897
|
+
console.log(' /kc:work "Your first feature"');
|
|
898
|
+
console.log('');
|
|
899
|
+
console.log(' Note: Commands also work without plugin as /work, /plan, /fix, etc.');
|
|
900
|
+
} else {
|
|
901
|
+
console.log(' 3. Start building: use knowzcode/prompts/[LOOP_1A]__Propose_Change_Set.md');
|
|
902
|
+
}
|
|
903
|
+
console.log('');
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// UNINSTALL
|
|
907
|
+
async function cmdUninstall(opts) {
|
|
908
|
+
const dir = opts.target;
|
|
909
|
+
const kcDir = join(dir, 'knowzcode');
|
|
910
|
+
|
|
911
|
+
console.log('');
|
|
912
|
+
console.log(`${c.bold}KnowzCode Uninstall${c.reset}`);
|
|
913
|
+
console.log(`${c.dim}Target: ${dir}${c.reset}`);
|
|
914
|
+
console.log('');
|
|
915
|
+
|
|
916
|
+
// Scan for installed components
|
|
917
|
+
const components = [];
|
|
918
|
+
|
|
919
|
+
if (existsSync(kcDir)) {
|
|
920
|
+
components.push({ label: 'Core framework', path: kcDir });
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// Claude Code components
|
|
924
|
+
const claudeDir = join(dir, '.claude');
|
|
925
|
+
for (const sub of ['commands', 'agents', 'skills']) {
|
|
926
|
+
const p = join(claudeDir, sub);
|
|
927
|
+
if (existsSync(p)) {
|
|
928
|
+
components.push({ label: `Claude Code ${sub}`, path: p });
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// Platform adapter files
|
|
933
|
+
const adapterChecks = {
|
|
934
|
+
codex: join(dir, 'AGENTS.md'),
|
|
935
|
+
gemini: join(dir, 'GEMINI.md'),
|
|
936
|
+
cursor: join(dir, '.cursor', 'rules', 'knowzcode.mdc'),
|
|
937
|
+
copilot: join(dir, '.github', 'copilot-instructions.md'),
|
|
938
|
+
windsurf: join(dir, '.windsurf', 'rules', 'knowzcode.md'),
|
|
939
|
+
};
|
|
940
|
+
|
|
941
|
+
for (const [id, path] of Object.entries(adapterChecks)) {
|
|
942
|
+
if (existsSync(path)) {
|
|
943
|
+
components.push({ label: `${PLATFORMS[id].name} adapter`, path });
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// Additional platform-specific files/directories
|
|
948
|
+
const copilotPromptsDir = join(dir, '.github', 'prompts');
|
|
949
|
+
if (existsSync(copilotPromptsDir)) {
|
|
950
|
+
for (const f of readdirSync(copilotPromptsDir)) {
|
|
951
|
+
if (f.startsWith('kc-') && f.endsWith('.prompt.md')) {
|
|
952
|
+
components.push({ label: `Copilot prompt: ${f}`, path: join(copilotPromptsDir, f) });
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
const vscodeMcp = join(dir, '.vscode', 'mcp.json');
|
|
957
|
+
if (existsSync(vscodeMcp)) {
|
|
958
|
+
components.push({ label: 'VS Code MCP config', path: vscodeMcp });
|
|
959
|
+
}
|
|
960
|
+
const geminiCmdDir = join(dir, '.gemini', 'commands', 'kc');
|
|
961
|
+
if (existsSync(geminiCmdDir)) {
|
|
962
|
+
components.push({ label: 'Gemini commands (kc/)', path: geminiCmdDir });
|
|
963
|
+
}
|
|
964
|
+
// Gemini skills: .gemini/skills/kc-*
|
|
965
|
+
const geminiSkillDir = join(dir, '.gemini', 'skills');
|
|
966
|
+
if (existsSync(geminiSkillDir)) {
|
|
967
|
+
for (const entry of readdirSync(geminiSkillDir)) {
|
|
968
|
+
if (entry.startsWith('kc-')) {
|
|
969
|
+
components.push({ label: `Gemini skill (${entry}/)`, path: join(geminiSkillDir, entry) });
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
const globalGeminiSkillDir = join(process.env.HOME || process.env.USERPROFILE || '~', '.gemini', 'skills');
|
|
974
|
+
if (existsSync(globalGeminiSkillDir)) {
|
|
975
|
+
for (const entry of readdirSync(globalGeminiSkillDir)) {
|
|
976
|
+
if (entry.startsWith('kc-')) {
|
|
977
|
+
components.push({ label: `Gemini skill — global (~/.gemini/skills/${entry}/)`, path: join(globalGeminiSkillDir, entry) });
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
// Gemini subagents: .gemini/agents/kc-*.md
|
|
982
|
+
const geminiAgentDir = join(dir, '.gemini', 'agents');
|
|
983
|
+
if (existsSync(geminiAgentDir)) {
|
|
984
|
+
for (const entry of readdirSync(geminiAgentDir)) {
|
|
985
|
+
if (entry.startsWith('kc-') && entry.endsWith('.md')) {
|
|
986
|
+
components.push({ label: `Gemini subagent (${entry})`, path: join(geminiAgentDir, entry) });
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
// New path: .agents/skills/kc-*
|
|
991
|
+
const agentsSkillDir = join(dir, '.agents', 'skills');
|
|
992
|
+
if (existsSync(agentsSkillDir)) {
|
|
993
|
+
for (const entry of readdirSync(agentsSkillDir)) {
|
|
994
|
+
if (entry.startsWith('kc-')) {
|
|
995
|
+
components.push({ label: `Codex skill (${entry}/)`, path: join(agentsSkillDir, entry) });
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
const globalAgentsSkillDir = join(process.env.HOME || process.env.USERPROFILE || '~', '.agents', 'skills');
|
|
1000
|
+
if (existsSync(globalAgentsSkillDir)) {
|
|
1001
|
+
for (const entry of readdirSync(globalAgentsSkillDir)) {
|
|
1002
|
+
if (entry.startsWith('kc-')) {
|
|
1003
|
+
components.push({ label: `Codex skill — global (~/.agents/skills/${entry}/)`, path: join(globalAgentsSkillDir, entry) });
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
// Legacy path: .codex/skills/kc (remove on uninstall)
|
|
1008
|
+
const legacyCodexSkillDir = join(dir, '.codex', 'skills', 'kc');
|
|
1009
|
+
if (existsSync(legacyCodexSkillDir)) {
|
|
1010
|
+
components.push({ label: 'Codex skills — legacy (.codex/skills/kc/)', path: legacyCodexSkillDir });
|
|
1011
|
+
}
|
|
1012
|
+
const legacyGlobalCodexSkillDir = join(process.env.HOME || process.env.USERPROFILE || '~', '.codex', 'skills', 'kc');
|
|
1013
|
+
if (existsSync(legacyGlobalCodexSkillDir)) {
|
|
1014
|
+
components.push({ label: 'Codex skills — legacy global (~/.codex/skills/kc/)', path: legacyGlobalCodexSkillDir });
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
if (components.length === 0) {
|
|
1018
|
+
log.info('No KnowzCode installation found.');
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
console.log(' Components found:');
|
|
1023
|
+
for (const comp of components) {
|
|
1024
|
+
console.log(` ${comp.label}: ${comp.path}`);
|
|
1025
|
+
}
|
|
1026
|
+
console.log('');
|
|
1027
|
+
|
|
1028
|
+
// Ask about preserving user data
|
|
1029
|
+
let preserveUserData = false;
|
|
1030
|
+
if (existsSync(kcDir) && !opts.force) {
|
|
1031
|
+
preserveUserData = await promptConfirm('Preserve user data (specs/, architecture, tracker, log)?');
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
if (!opts.force) {
|
|
1035
|
+
const confirmed = await promptConfirm('Remove all listed components?');
|
|
1036
|
+
if (!confirmed) {
|
|
1037
|
+
log.info('Uninstall cancelled.');
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
const removed = [];
|
|
1043
|
+
|
|
1044
|
+
// Remove components
|
|
1045
|
+
for (const comp of components) {
|
|
1046
|
+
if (comp.path === kcDir && preserveUserData) {
|
|
1047
|
+
// Selective removal — keep user data
|
|
1048
|
+
const preserve = ['specs', 'knowzcode_architecture.md', 'knowzcode_tracker.md', 'knowzcode_log.md', 'knowzcode_project.md'];
|
|
1049
|
+
|
|
1050
|
+
for (const entry of readdirSync(kcDir)) {
|
|
1051
|
+
if (preserve.includes(entry)) continue;
|
|
1052
|
+
const entryPath = join(kcDir, entry);
|
|
1053
|
+
rmSync(entryPath, { recursive: true, force: true });
|
|
1054
|
+
}
|
|
1055
|
+
removed.push(comp.label + ' (user data preserved)');
|
|
1056
|
+
} else {
|
|
1057
|
+
rmSync(comp.path, { recursive: true, force: true });
|
|
1058
|
+
removed.push(comp.label);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// Clean up marketplace config from settings.json
|
|
1063
|
+
removeMarketplaceConfig(claudeDir);
|
|
1064
|
+
|
|
1065
|
+
// Clean up Gemini MCP config (remove only knowz entry, preserve other settings)
|
|
1066
|
+
const geminiSettingsProject = join(dir, '.gemini', 'settings.json');
|
|
1067
|
+
if (removeGeminiMcpConfig(geminiSettingsProject)) {
|
|
1068
|
+
removed.push('Gemini MCP config (.gemini/settings.json)');
|
|
1069
|
+
}
|
|
1070
|
+
const homeDir2 = process.env.HOME || process.env.USERPROFILE || '~';
|
|
1071
|
+
const geminiSettingsUser = join(homeDir2, '.gemini', 'settings.json');
|
|
1072
|
+
if (removeGeminiMcpConfig(geminiSettingsUser)) {
|
|
1073
|
+
removed.push('Gemini MCP config (~/.gemini/settings.json)');
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
console.log('');
|
|
1077
|
+
log.ok('Uninstall complete');
|
|
1078
|
+
console.log(' Removed:');
|
|
1079
|
+
for (const r of removed) {
|
|
1080
|
+
console.log(` ${r}`);
|
|
1081
|
+
}
|
|
1082
|
+
console.log('');
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// UPGRADE
|
|
1086
|
+
async function cmdUpgrade(opts) {
|
|
1087
|
+
const dir = opts.target;
|
|
1088
|
+
const kcDir = join(dir, 'knowzcode');
|
|
1089
|
+
|
|
1090
|
+
console.log('');
|
|
1091
|
+
console.log(`${c.bold}KnowzCode Upgrade${c.reset}`);
|
|
1092
|
+
console.log(`${c.dim}Target: ${dir}${c.reset}`);
|
|
1093
|
+
console.log('');
|
|
1094
|
+
|
|
1095
|
+
if (!existsSync(kcDir)) {
|
|
1096
|
+
log.err('No KnowzCode installation found. Run `npx knowzcode install` first.');
|
|
1097
|
+
process.exit(1);
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
// Read current version
|
|
1101
|
+
const versionFile = join(kcDir, '.knowzcode-version');
|
|
1102
|
+
const currentVersion = existsSync(versionFile) ? readFileSync(versionFile, 'utf8').trim() : 'unknown';
|
|
1103
|
+
|
|
1104
|
+
if (currentVersion === VERSION && !opts.force) {
|
|
1105
|
+
log.info(`Already at version ${VERSION}. Use --force to reinstall.`);
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
log.info(`Upgrading: ${currentVersion} → ${VERSION}`);
|
|
1110
|
+
|
|
1111
|
+
// Files to preserve (never overwrite)
|
|
1112
|
+
const preserveFiles = new Set([
|
|
1113
|
+
'knowzcode_tracker.md',
|
|
1114
|
+
'knowzcode_log.md',
|
|
1115
|
+
'knowzcode_architecture.md',
|
|
1116
|
+
'knowzcode_project.md',
|
|
1117
|
+
'environment_context.md',
|
|
1118
|
+
'user_preferences.md',
|
|
1119
|
+
]);
|
|
1120
|
+
const preserveDirs = new Set(['specs', 'workgroups']);
|
|
1121
|
+
|
|
1122
|
+
// Files to replace (always update)
|
|
1123
|
+
const srcKc = join(PKG_ROOT, 'knowzcode');
|
|
1124
|
+
|
|
1125
|
+
// Update .md files
|
|
1126
|
+
for (const entry of readdirSync(srcKc)) {
|
|
1127
|
+
const srcPath = join(srcKc, entry);
|
|
1128
|
+
const dstPath = join(kcDir, entry);
|
|
1129
|
+
const stat = statSync(srcPath);
|
|
1130
|
+
|
|
1131
|
+
if (stat.isFile()) {
|
|
1132
|
+
if (preserveFiles.has(entry)) {
|
|
1133
|
+
if (opts.verbose) log.info(`Preserved: ${entry}`);
|
|
1134
|
+
continue;
|
|
1135
|
+
}
|
|
1136
|
+
// Handle gitignore.template → .gitignore rename
|
|
1137
|
+
if (entry === 'gitignore.template') {
|
|
1138
|
+
writeFileSync(join(kcDir, '.gitignore'), readFileSync(srcPath));
|
|
1139
|
+
if (opts.verbose) log.info('Updated: .gitignore (from gitignore.template)');
|
|
1140
|
+
} else {
|
|
1141
|
+
writeFileSync(dstPath, readFileSync(srcPath));
|
|
1142
|
+
if (opts.verbose) log.info(`Updated: ${entry}`);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
// Update prompts/ (always replace)
|
|
1148
|
+
if (existsSync(join(srcKc, 'prompts'))) {
|
|
1149
|
+
const promptsDst = join(kcDir, 'prompts');
|
|
1150
|
+
// Remove old prompts, copy new ones
|
|
1151
|
+
if (existsSync(promptsDst)) rmSync(promptsDst, { recursive: true, force: true });
|
|
1152
|
+
copyDirContents(join(srcKc, 'prompts'), promptsDst);
|
|
1153
|
+
if (opts.verbose) log.info('Updated: prompts/');
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
// Update enterprise/ (always replace)
|
|
1157
|
+
if (existsSync(join(srcKc, 'enterprise'))) {
|
|
1158
|
+
const entDst = join(kcDir, 'enterprise');
|
|
1159
|
+
if (existsSync(entDst)) rmSync(entDst, { recursive: true, force: true });
|
|
1160
|
+
copyDirContents(join(srcKc, 'enterprise'), entDst);
|
|
1161
|
+
if (opts.verbose) log.info('Updated: enterprise/');
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
// Update Claude Code components if present
|
|
1165
|
+
const claudeDir = join(dir, '.claude');
|
|
1166
|
+
if (existsSync(join(claudeDir, 'commands')) || existsSync(join(claudeDir, 'agents'))) {
|
|
1167
|
+
log.info('Updating Claude Code components...');
|
|
1168
|
+
// Remove stale files before copying
|
|
1169
|
+
removeStaleFiles(join(PKG_ROOT, 'commands'), join(claudeDir, 'commands'));
|
|
1170
|
+
removeStaleFiles(join(PKG_ROOT, 'agents'), join(claudeDir, 'agents'));
|
|
1171
|
+
removeStaleFiles(join(PKG_ROOT, 'skills'), join(claudeDir, 'skills'));
|
|
1172
|
+
copyDirContents(join(PKG_ROOT, 'commands'), join(claudeDir, 'commands'));
|
|
1173
|
+
copyDirContents(join(PKG_ROOT, 'agents'), join(claudeDir, 'agents'));
|
|
1174
|
+
copyDirContents(join(PKG_ROOT, 'skills'), join(claudeDir, 'skills'));
|
|
1175
|
+
// Ensure marketplace config is up to date
|
|
1176
|
+
setMarketplaceConfig(claudeDir);
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
// Regenerate adapters for detected platforms
|
|
1180
|
+
const detected = detectPlatforms(dir);
|
|
1181
|
+
const templates = parseAdapterTemplates();
|
|
1182
|
+
const regenerated = [];
|
|
1183
|
+
|
|
1184
|
+
for (const platformId of detected) {
|
|
1185
|
+
if (platformId === 'claude') continue; // Already handled above
|
|
1186
|
+
const platform = PLATFORMS[platformId];
|
|
1187
|
+
if (!platform.adapterPath) continue;
|
|
1188
|
+
|
|
1189
|
+
const adapterFile = platform.adapterPath(dir);
|
|
1190
|
+
if (!existsSync(adapterFile)) continue; // Only update existing adapters
|
|
1191
|
+
|
|
1192
|
+
const templateSet = templates.get(platformId);
|
|
1193
|
+
if (!templateSet) continue;
|
|
1194
|
+
|
|
1195
|
+
// Update primary adapter file
|
|
1196
|
+
writeFileSync(adapterFile, injectVersion(templateSet.primary));
|
|
1197
|
+
regenerated.push(platform.name);
|
|
1198
|
+
|
|
1199
|
+
// Regenerate additional files
|
|
1200
|
+
const currentPaths = new Set();
|
|
1201
|
+
for (const [relativePath, { content }] of templateSet.files) {
|
|
1202
|
+
const filePath = join(dir, relativePath);
|
|
1203
|
+
ensureDir(dirname(filePath));
|
|
1204
|
+
writeFileSync(filePath, injectVersion(content));
|
|
1205
|
+
currentPaths.add(relativePath);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
// Stale file cleanup for platform-owned directories
|
|
1209
|
+
if (platformId === 'copilot') {
|
|
1210
|
+
const promptsDir = join(dir, '.github', 'prompts');
|
|
1211
|
+
if (existsSync(promptsDir)) {
|
|
1212
|
+
for (const f of readdirSync(promptsDir)) {
|
|
1213
|
+
if (f.startsWith('kc-') && f.endsWith('.prompt.md') && !currentPaths.has(`.github/prompts/${f}`)) {
|
|
1214
|
+
log.info(`Removing stale prompt: ${f}`);
|
|
1215
|
+
rmSync(join(promptsDir, f), { force: true });
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
} else if (platformId === 'gemini') {
|
|
1220
|
+
const tomlDir = join(dir, '.gemini', 'commands', 'kc');
|
|
1221
|
+
if (existsSync(tomlDir)) {
|
|
1222
|
+
for (const f of readdirSync(tomlDir)) {
|
|
1223
|
+
if (f.endsWith('.toml') && !currentPaths.has(`.gemini/commands/kc/${f}`)) {
|
|
1224
|
+
log.info(`Removing stale command: ${f}`);
|
|
1225
|
+
rmSync(join(tomlDir, f), { force: true });
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
// Stale skill cleanup: .gemini/skills/kc-*
|
|
1230
|
+
const geminiSkillDir = join(dir, '.gemini', 'skills');
|
|
1231
|
+
if (existsSync(geminiSkillDir)) {
|
|
1232
|
+
for (const entry of readdirSync(geminiSkillDir)) {
|
|
1233
|
+
if (entry.startsWith('kc-') && !currentPaths.has(`.gemini/skills/${entry}/SKILL.md`)) {
|
|
1234
|
+
log.info(`Removing stale Gemini skill: ${entry}/`);
|
|
1235
|
+
rmSync(join(geminiSkillDir, entry), { recursive: true, force: true });
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
// Stale subagent cleanup: .gemini/agents/kc-*.md
|
|
1240
|
+
const geminiAgentDir = join(dir, '.gemini', 'agents');
|
|
1241
|
+
if (existsSync(geminiAgentDir)) {
|
|
1242
|
+
for (const entry of readdirSync(geminiAgentDir)) {
|
|
1243
|
+
if (entry.startsWith('kc-') && entry.endsWith('.md') && !currentPaths.has(`.gemini/agents/${entry}`)) {
|
|
1244
|
+
log.info(`Removing stale Gemini subagent: ${entry}`);
|
|
1245
|
+
rmSync(join(geminiAgentDir, entry), { force: true });
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
} else if (platformId === 'codex') {
|
|
1250
|
+
const skillDir = join(dir, '.agents', 'skills');
|
|
1251
|
+
if (existsSync(skillDir)) {
|
|
1252
|
+
for (const entry of readdirSync(skillDir)) {
|
|
1253
|
+
if (entry.startsWith('kc-') && !currentPaths.has(`.agents/skills/${entry}/SKILL.md`)) {
|
|
1254
|
+
log.info(`Removing stale skill: ${entry}/`);
|
|
1255
|
+
rmSync(join(skillDir, entry), { recursive: true, force: true });
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
// Migration: remove legacy .codex/skills/kc/ if present
|
|
1260
|
+
const legacySkillDir = join(dir, '.codex', 'skills', 'kc');
|
|
1261
|
+
if (existsSync(legacySkillDir)) {
|
|
1262
|
+
log.info('Removing legacy .codex/skills/kc/ (migrated to .agents/skills/)');
|
|
1263
|
+
rmSync(legacySkillDir, { recursive: true, force: true });
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
// Check for global codex skills
|
|
1269
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || '~';
|
|
1270
|
+
const globalAgentsSkillDir = join(homeDir, '.agents', 'skills');
|
|
1271
|
+
if (existsSync(globalAgentsSkillDir)) {
|
|
1272
|
+
const codexTemplateSet = templates.get('codex');
|
|
1273
|
+
if (codexTemplateSet) {
|
|
1274
|
+
const currentPaths = new Set([...codexTemplateSet.files.keys()]);
|
|
1275
|
+
// Stale cleanup
|
|
1276
|
+
for (const entry of readdirSync(globalAgentsSkillDir)) {
|
|
1277
|
+
if (entry.startsWith('kc-') && !currentPaths.has(`.agents/skills/${entry}/SKILL.md`)) {
|
|
1278
|
+
log.info(`Removing stale global skill: ${entry}/`);
|
|
1279
|
+
rmSync(join(globalAgentsSkillDir, entry), { recursive: true, force: true });
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
// Regenerate global skills
|
|
1283
|
+
for (const [relativePath, { content }] of codexTemplateSet.files) {
|
|
1284
|
+
if (relativePath.startsWith('.agents/skills/')) {
|
|
1285
|
+
const filePath = join(homeDir, relativePath);
|
|
1286
|
+
ensureDir(dirname(filePath));
|
|
1287
|
+
writeFileSync(filePath, injectVersion(content));
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
log.info('Updated global Codex skills');
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
// Migration: remove legacy global .codex/skills/kc/ if present
|
|
1294
|
+
const legacyGlobalSkillDir = join(homeDir, '.codex', 'skills', 'kc');
|
|
1295
|
+
if (existsSync(legacyGlobalSkillDir)) {
|
|
1296
|
+
log.info('Removing legacy global ~/.codex/skills/kc/ (migrated to ~/.agents/skills/)');
|
|
1297
|
+
rmSync(legacyGlobalSkillDir, { recursive: true, force: true });
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
// Check for global Gemini skills
|
|
1301
|
+
const globalGeminiSkillDir = join(homeDir, '.gemini', 'skills');
|
|
1302
|
+
if (existsSync(globalGeminiSkillDir)) {
|
|
1303
|
+
const geminiTemplateSet = templates.get('gemini');
|
|
1304
|
+
if (geminiTemplateSet) {
|
|
1305
|
+
const currentPaths = new Set([...geminiTemplateSet.files.keys()]);
|
|
1306
|
+
// Stale cleanup
|
|
1307
|
+
for (const entry of readdirSync(globalGeminiSkillDir)) {
|
|
1308
|
+
if (entry.startsWith('kc-') && !currentPaths.has(`.gemini/skills/${entry}/SKILL.md`)) {
|
|
1309
|
+
log.info(`Removing stale global Gemini skill: ${entry}/`);
|
|
1310
|
+
rmSync(join(globalGeminiSkillDir, entry), { recursive: true, force: true });
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
// Regenerate global skills
|
|
1314
|
+
for (const [relativePath, { content }] of geminiTemplateSet.files) {
|
|
1315
|
+
if (relativePath.startsWith('.gemini/skills/')) {
|
|
1316
|
+
const filePath = join(homeDir, relativePath);
|
|
1317
|
+
ensureDir(dirname(filePath));
|
|
1318
|
+
writeFileSync(filePath, injectVersion(content));
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
log.info('Updated global Gemini skills');
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
// Preserve Gemini MCP config during upgrade (don't overwrite user's API key)
|
|
1326
|
+
const geminiSettingsPath = join(dir, '.gemini', 'settings.json');
|
|
1327
|
+
const geminiMcpPreserved = hasGeminiMcpConfig(geminiSettingsPath);
|
|
1328
|
+
if (geminiMcpPreserved && opts.verbose) {
|
|
1329
|
+
log.info('Preserved: Gemini MCP config (.gemini/settings.json)');
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
// Write new version
|
|
1333
|
+
writeFileSync(versionFile, VERSION + '\n');
|
|
1334
|
+
|
|
1335
|
+
console.log('');
|
|
1336
|
+
log.ok(`Upgraded to ${VERSION}`);
|
|
1337
|
+
console.log('');
|
|
1338
|
+
console.log(` ${c.bold}Preserved:${c.reset} specs/, tracker, log, architecture, project config${geminiMcpPreserved ? ', Gemini MCP config' : ''}`);
|
|
1339
|
+
console.log(` ${c.bold}Updated:${c.reset} loop, prompts, adapters, enterprise templates`);
|
|
1340
|
+
if (regenerated.length > 0) {
|
|
1341
|
+
console.log(` ${c.bold}Adapters:${c.reset} ${regenerated.join(', ')}`);
|
|
1342
|
+
}
|
|
1343
|
+
console.log('');
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
// HELP
|
|
1347
|
+
function cmdHelp() {
|
|
1348
|
+
console.log(`
|
|
1349
|
+
${c.bold}KnowzCode CLI${c.reset} v${VERSION}
|
|
1350
|
+
Platform-agnostic AI development methodology
|
|
1351
|
+
|
|
1352
|
+
${c.bold}Usage:${c.reset}
|
|
1353
|
+
npx knowzcode Interactive mode
|
|
1354
|
+
npx knowzcode install [options] Install to current/target directory
|
|
1355
|
+
npx knowzcode uninstall [options] Remove KnowzCode
|
|
1356
|
+
npx knowzcode upgrade [options] Upgrade preserving user data
|
|
1357
|
+
npx knowzcode detect Show detected platforms (dry run)
|
|
1358
|
+
|
|
1359
|
+
${c.bold}Options:${c.reset}
|
|
1360
|
+
--target <path> Target directory (default: current directory)
|
|
1361
|
+
--platforms <list> Comma-separated: claude,codex,gemini,cursor,copilot,windsurf,all
|
|
1362
|
+
--force Skip confirmation prompts
|
|
1363
|
+
--global Install Claude Code to ~/.claude/, Codex skills to ~/.agents/skills/, Gemini skills to ~/.gemini/skills/
|
|
1364
|
+
--agent-teams Enable Agent Teams in .claude/settings.local.json
|
|
1365
|
+
--verbose Show detailed output
|
|
1366
|
+
-h, --help Show this help
|
|
1367
|
+
-v, --version Show version
|
|
1368
|
+
|
|
1369
|
+
${c.bold}Examples:${c.reset}
|
|
1370
|
+
npx knowzcode install --platforms claude,cursor
|
|
1371
|
+
npx knowzcode install --platforms all --force
|
|
1372
|
+
npx knowzcode upgrade --target ./my-project
|
|
1373
|
+
npx knowzcode uninstall --force
|
|
1374
|
+
npx knowzcode detect
|
|
1375
|
+
`);
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
// INTERACTIVE
|
|
1379
|
+
async function cmdInteractive(opts) {
|
|
1380
|
+
console.log('');
|
|
1381
|
+
console.log(`${c.bold} ╔═══════════════════════════════════════╗${c.reset}`);
|
|
1382
|
+
console.log(`${c.bold} ║ KnowzCode v${VERSION.padEnd(14)} ║${c.reset}`);
|
|
1383
|
+
console.log(`${c.bold} ║ AI Development Methodology Installer ║${c.reset}`);
|
|
1384
|
+
console.log(`${c.bold} ╚═══════════════════════════════════════╝${c.reset}`);
|
|
1385
|
+
console.log('');
|
|
1386
|
+
|
|
1387
|
+
const dir = opts.target;
|
|
1388
|
+
const kcDir = join(dir, 'knowzcode');
|
|
1389
|
+
const detected = detectPlatforms(dir);
|
|
1390
|
+
|
|
1391
|
+
if (detected.length > 0) {
|
|
1392
|
+
log.info('Detected platforms: ' + detected.map((d) => PLATFORMS[d].name).join(', '));
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
if (existsSync(kcDir)) {
|
|
1396
|
+
const versionFile = join(kcDir, '.knowzcode-version');
|
|
1397
|
+
const currentVersion = existsSync(versionFile) ? readFileSync(versionFile, 'utf8').trim() : 'unknown';
|
|
1398
|
+
|
|
1399
|
+
if (currentVersion !== VERSION) {
|
|
1400
|
+
log.info(`Installed version: ${currentVersion}, available: ${VERSION}`);
|
|
1401
|
+
const doUpgrade = await promptConfirm('Upgrade to latest version?');
|
|
1402
|
+
if (doUpgrade) {
|
|
1403
|
+
return cmdUpgrade(opts);
|
|
1404
|
+
}
|
|
1405
|
+
} else {
|
|
1406
|
+
log.info(`KnowzCode ${currentVersion} already installed.`);
|
|
1407
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1408
|
+
console.log('');
|
|
1409
|
+
console.log(' [1] Reinstall (overwrite)');
|
|
1410
|
+
console.log(' [2] Uninstall');
|
|
1411
|
+
console.log(' [3] Detect platforms');
|
|
1412
|
+
console.log(' [4] Exit');
|
|
1413
|
+
console.log('');
|
|
1414
|
+
const answer = await rl.question('Select action: ');
|
|
1415
|
+
rl.close();
|
|
1416
|
+
|
|
1417
|
+
const choice = answer.trim();
|
|
1418
|
+
if (choice === '1') return cmdInstall({ ...opts, force: true });
|
|
1419
|
+
if (choice === '2') return cmdUninstall(opts);
|
|
1420
|
+
if (choice === '3') return cmdDetect(opts);
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
} else {
|
|
1424
|
+
log.info('No existing installation found. Starting install...');
|
|
1425
|
+
console.log('');
|
|
1426
|
+
return cmdInstall(opts);
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
// ─── Main ────────────────────────────────────────────────────────────────────
|
|
1431
|
+
|
|
1432
|
+
async function main() {
|
|
1433
|
+
const opts = parseArgs(process.argv);
|
|
1434
|
+
|
|
1435
|
+
switch (opts.command) {
|
|
1436
|
+
case 'install':
|
|
1437
|
+
return cmdInstall(opts);
|
|
1438
|
+
case 'uninstall':
|
|
1439
|
+
return cmdUninstall(opts);
|
|
1440
|
+
case 'upgrade':
|
|
1441
|
+
return cmdUpgrade(opts);
|
|
1442
|
+
case 'detect':
|
|
1443
|
+
return cmdDetect(opts);
|
|
1444
|
+
case 'help':
|
|
1445
|
+
return cmdHelp();
|
|
1446
|
+
case 'version':
|
|
1447
|
+
console.log(VERSION);
|
|
1448
|
+
return;
|
|
1449
|
+
default:
|
|
1450
|
+
return cmdInteractive(opts);
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
main().catch((err) => {
|
|
1455
|
+
log.err(err.message);
|
|
1456
|
+
process.exit(1);
|
|
1457
|
+
});
|