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