knowzcode 0.5.2 → 0.6.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.
@@ -6,14 +6,14 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Official KnowzCode plugin marketplace - Platform-agnostic AI development methodology",
9
- "version": "0.5.2"
9
+ "version": "0.6.0"
10
10
  },
11
11
  "plugins": [
12
12
  {
13
13
  "name": "kc",
14
14
  "source": "./",
15
15
  "description": "KnowzCode - Platform-agnostic AI development methodology with TDD, quality gates, and structured workflows",
16
- "version": "0.5.2",
16
+ "version": "0.6.0",
17
17
  "author": {
18
18
  "name": "Alex Headscarf"
19
19
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kc",
3
3
  "description": "KnowzCode - Platform-agnostic AI development methodology with TDD, quality gates, and structured workflows",
4
- "version": "0.5.2",
4
+ "version": "0.6.0",
5
5
  "author": {
6
6
  "name": "Alex Headscarf"
7
7
  }
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  [![License: MIT + Commons Clause](https://img.shields.io/badge/License-MIT_+_Commons_Clause-yellow.svg)](LICENSE)
8
8
  [![Claude Code Plugin](https://img.shields.io/badge/Claude_Code-Plugin-purple)](https://github.com/knowz-io/knowzcode)
9
- [![Version](https://img.shields.io/badge/version-0.5.2-blue)](https://github.com/knowz-io/knowzcode/releases)
9
+ [![Version](https://img.shields.io/badge/version-0.6.0-blue)](https://github.com/knowz-io/knowzcode/releases)
10
10
 
11
11
  [Installation](#installation) · [Quick Start](#quick-start) · [When to Use It](#when-to-use-knowzcode) · [How It Works](#how-it-works) · [Commands](#commands) · [Docs](#documentation)
12
12
 
package/bin/knowzcode.mjs CHANGED
@@ -82,6 +82,7 @@ function parseArgs(argv) {
82
82
  target: process.cwd(),
83
83
  platforms: [],
84
84
  force: false,
85
+ clean: false,
85
86
  global: false,
86
87
  verbose: false,
87
88
  agentTeams: false,
@@ -96,6 +97,8 @@ function parseArgs(argv) {
96
97
  opts.platforms = args[++i].split(',').map((p) => p.trim().toLowerCase());
97
98
  } else if (arg === '--force') {
98
99
  opts.force = true;
100
+ } else if (arg === '--clean') {
101
+ opts.clean = true;
99
102
  } else if (arg === '--global') {
100
103
  opts.global = true;
101
104
  } else if (arg === '--agent-teams') {
@@ -619,151 +622,184 @@ function enableAgentTeams(claudeDir, isGlobal) {
619
622
  log.ok(`Agent Teams enabled in ${settingsFile}`);
620
623
  }
621
624
 
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}`}`);
625
+ // ─── Installation Scanner ────────────────────────────────────────────────────
626
+
627
+ function scanExistingInstallation(kcDir, dir) {
628
+ const result = {
629
+ version: null,
630
+ specs: [],
631
+ trackerEntries: 0,
632
+ logEntries: 0,
633
+ hasArchitecture: false,
634
+ hasProject: false,
635
+ hasPreferences: false,
636
+ hasOrchestration: false,
637
+ workgroups: [],
638
+ installedPlatforms: [],
639
+ customizedFiles: [],
640
+ };
636
641
 
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
- }
642
+ // Version
643
+ const versionFile = join(kcDir, '.knowzcode-version');
644
+ if (existsSync(versionFile)) {
645
+ result.version = readFileSync(versionFile, 'utf8').trim();
643
646
  }
644
647
 
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}`);
648
+ // Specs
649
+ const specsDir = join(kcDir, 'specs');
650
+ if (existsSync(specsDir)) {
651
+ result.specs = readdirSync(specsDir).filter(f => f.endsWith('.md') && f !== 'Readme.md');
652
652
  }
653
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.`);
654
+ // Tracker entries (look for non-empty table rows — rows starting with |)
655
+ const trackerFile = join(kcDir, 'knowzcode_tracker.md');
656
+ if (existsSync(trackerFile)) {
657
+ const content = readFileSync(trackerFile, 'utf8');
658
+ const rows = content.split('\n').filter(line => /^\|[^:|\-]/.test(line) && !/^\| Status/.test(line) && line.trim() !== '| | | | | | | | | |');
659
+ result.trackerEntries = rows.length;
659
660
  }
660
- console.log('');
661
- }
662
661
 
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);
662
+ // Log entries (count --- delimited entries beyond SystemInitialization)
663
+ const logFile = join(kcDir, 'knowzcode_log.md');
664
+ if (existsSync(logFile)) {
665
+ const content = readFileSync(logFile, 'utf8');
666
+ const typeMatches = content.match(/\*\*Type:\*\*/g);
667
+ result.logEntries = typeMatches ? Math.max(0, typeMatches.length - 1) : 0; // -1 for SystemInitialization
678
668
  }
679
669
 
680
- if (!existsSync(dir)) {
681
- log.err('Target directory does not exist: ' + dir);
682
- process.exit(1);
670
+ // Architecture — check if edited (compare size to source template)
671
+ const archFile = join(kcDir, 'knowzcode_architecture.md');
672
+ const srcArch = join(PKG_ROOT, 'knowzcode', 'knowzcode_architecture.md');
673
+ if (existsSync(archFile)) {
674
+ const installedSize = statSync(archFile).size;
675
+ const templateSize = existsSync(srcArch) ? statSync(srcArch).size : 0;
676
+ result.hasArchitecture = installedSize !== templateSize;
683
677
  }
684
678
 
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'));
679
+ // Project config
680
+ const projectFile = join(kcDir, 'knowzcode_project.md');
681
+ const srcProject = join(PKG_ROOT, 'knowzcode', 'knowzcode_project.md');
682
+ if (existsSync(projectFile)) {
683
+ const installedSize = statSync(projectFile).size;
684
+ const templateSize = existsSync(srcProject) ? statSync(srcProject).size : 0;
685
+ result.hasProject = installedSize !== templateSize;
686
+ }
692
687
 
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');
688
+ // Preferences
689
+ result.hasPreferences = existsSync(join(kcDir, 'user_preferences.md'));
695
690
 
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
- }
691
+ // Orchestration
692
+ const orchFile = join(kcDir, 'knowzcode_orchestration.md');
693
+ const srcOrch = join(PKG_ROOT, 'knowzcode', 'knowzcode_orchestration.md');
694
+ if (existsSync(orchFile)) {
695
+ const installedSize = statSync(orchFile).size;
696
+ const templateSize = existsSync(srcOrch) ? statSync(srcOrch).size : 0;
697
+ result.hasOrchestration = installedSize !== templateSize;
710
698
  }
711
699
 
712
- // Copy prompts/
713
- if (existsSync(join(srcKc, 'prompts'))) {
714
- copyDirContents(join(srcKc, 'prompts'), join(kcDir, 'prompts'));
700
+ // Workgroups
701
+ const wgDir = join(kcDir, 'workgroups');
702
+ if (existsSync(wgDir)) {
703
+ result.workgroups = readdirSync(wgDir).filter(f => f !== 'README.md');
715
704
  }
716
705
 
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')));
706
+ // Installed platforms
707
+ const adapterChecks = {
708
+ claude: () => existsSync(join(dir, '.claude', 'commands')) || existsSync(join(dir, '.claude', 'agents')),
709
+ codex: () => existsSync(join(dir, 'AGENTS.md')),
710
+ gemini: () => existsSync(join(dir, 'GEMINI.md')),
711
+ cursor: () => existsSync(join(dir, '.cursor', 'rules', 'knowzcode.mdc')),
712
+ copilot: () => existsSync(join(dir, '.github', 'copilot-instructions.md')),
713
+ windsurf: () => existsSync(join(dir, '.windsurf', 'rules', 'knowzcode.md')),
714
+ };
715
+ for (const [id, check] of Object.entries(adapterChecks)) {
716
+ if (check()) result.installedPlatforms.push(id);
720
717
  }
721
718
 
722
- // Copy enterprise/ if exists
723
- if (existsSync(join(srcKc, 'enterprise'))) {
724
- copyDirContents(join(srcKc, 'enterprise'), join(kcDir, 'enterprise'));
719
+ // Customized files compare framework .md files against source templates
720
+ const srcKc = join(PKG_ROOT, 'knowzcode');
721
+ const userEditable = ['knowzcode_architecture.md', 'knowzcode_project.md', 'environment_context.md', 'user_preferences.md', 'knowzcode_orchestration.md'];
722
+ for (const entry of userEditable) {
723
+ const installed = join(kcDir, entry);
724
+ const source = join(srcKc, entry);
725
+ if (existsSync(installed) && existsSync(source)) {
726
+ if (statSync(installed).size !== statSync(source).size) {
727
+ result.customizedFiles.push(entry);
728
+ }
729
+ }
725
730
  }
726
731
 
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');
732
+ return result;
733
+ }
733
734
 
734
- log.ok('Core framework installed');
735
+ function displayInstallationSummary(scan, dir) {
736
+ console.log(` ${c.bold}KnowzCode v${scan.version || 'unknown'} detected${c.reset}`);
737
+ console.log('');
735
738
 
736
- // 2. Platform detection + selection
737
- const detected = detectPlatforms(dir);
738
- let selectedPlatforms;
739
+ // User data
740
+ const hasData = scan.specs.length > 0 || scan.trackerEntries > 0 || scan.logEntries > 0 ||
741
+ scan.hasArchitecture || scan.hasProject || scan.hasPreferences || scan.workgroups.length > 0;
739
742
 
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);
743
+ if (hasData) {
744
+ console.log(' Your data:');
745
+ if (scan.specs.length > 0) {
746
+ const specNames = scan.specs.slice(0, 5).map(s => s.replace('.md', '')).join(', ');
747
+ const more = scan.specs.length > 5 ? `, +${scan.specs.length - 5} more` : '';
748
+ console.log(` ${String(scan.specs.length).padStart(2)} spec(s) (${specNames}${more})`);
745
749
  }
746
- } else if (opts.force) {
747
- // Non-interactive mode with --force: install for detected platforms only
748
- selectedPlatforms = detected;
750
+ if (scan.trackerEntries > 0) console.log(` ${String(scan.trackerEntries).padStart(2)} tracker entries`);
751
+ if (scan.logEntries > 0) console.log(` ${String(scan.logEntries).padStart(2)} log entries`);
752
+ if (scan.hasArchitecture) console.log(' Architecture customized');
753
+ if (scan.hasProject) console.log(' Project config customized');
754
+ if (scan.hasPreferences) console.log(' Preferences configured');
755
+ if (scan.hasOrchestration) console.log(' Orchestration customized');
756
+ if (scan.workgroups.length > 0) console.log(` ${String(scan.workgroups.length).padStart(2)} active workgroup(s)`);
749
757
  } else {
750
- selectedPlatforms = await promptPlatforms(detected);
758
+ console.log(' Your data: (no customizations detected)');
759
+ }
760
+
761
+ // Platforms
762
+ const detected = detectPlatforms(dir);
763
+ console.log('');
764
+ console.log(' Platforms:');
765
+ for (const [id, platform] of Object.entries(PLATFORMS)) {
766
+ const installed = scan.installedPlatforms.includes(id);
767
+ const det = detected.includes(id);
768
+ let status;
769
+ if (installed) status = `${c.green}installed${c.reset}`;
770
+ else if (det) status = `${c.yellow}detected (not installed)${c.reset}`;
771
+ else status = `${c.dim}not installed${c.reset}`;
772
+ console.log(` ${platform.name.padEnd(18)} ${status}`);
751
773
  }
774
+ }
775
+
776
+ function isAdapterInstalled(platformId, dir) {
777
+ const checks = {
778
+ claude: () => existsSync(join(dir, '.claude', 'commands')) || existsSync(join(dir, '.claude', 'agents')),
779
+ codex: () => existsSync(join(dir, 'AGENTS.md')),
780
+ gemini: () => existsSync(join(dir, 'GEMINI.md')),
781
+ cursor: () => existsSync(join(dir, '.cursor', 'rules', 'knowzcode.mdc')),
782
+ copilot: () => existsSync(join(dir, '.github', 'copilot-instructions.md')),
783
+ windsurf: () => existsSync(join(dir, '.windsurf', 'rules', 'knowzcode.md')),
784
+ };
785
+ return checks[platformId] ? checks[platformId]() : false;
786
+ }
752
787
 
753
- // 3. Generate adapters
788
+ // ─── Adapter Generation (shared helper) ──────────────────────────────────────
789
+
790
+ async function generateAdapters(dir, selectedPlatforms, opts) {
754
791
  const templates = parseAdapterTemplates();
755
792
  const adapterFiles = [];
793
+ let agentTeamsEnabled = false;
756
794
 
757
795
  for (const platformId of selectedPlatforms) {
758
796
  const platform = PLATFORMS[platformId];
759
797
 
760
798
  if (platformId === 'claude') {
761
- // Claude Code: copy agents, commands, skills
762
799
  const claudeDir = opts.global ? join(process.env.HOME || process.env.USERPROFILE || '~', '.claude') : join(dir, '.claude');
763
800
 
764
801
  log.info(`Installing Claude Code components to ${claudeDir}/`);
765
802
 
766
- // Remove stale files before copying on --force
767
803
  if (opts.force) {
768
804
  removeStaleFiles(join(PKG_ROOT, 'commands'), join(claudeDir, 'commands'));
769
805
  removeStaleFiles(join(PKG_ROOT, 'agents'), join(claudeDir, 'agents'));
@@ -773,27 +809,21 @@ async function cmdInstall(opts) {
773
809
  copyDirContents(join(PKG_ROOT, 'commands'), join(claudeDir, 'commands'));
774
810
  copyDirContents(join(PKG_ROOT, 'agents'), join(claudeDir, 'agents'));
775
811
  copyDirContents(join(PKG_ROOT, 'skills'), join(claudeDir, 'skills'));
776
-
777
- // Pre-register marketplace in settings.json
778
812
  setMarketplaceConfig(claudeDir);
779
-
780
813
  adapterFiles.push(claudeDir + '/commands/', claudeDir + '/agents/', claudeDir + '/skills/');
781
814
  } else {
782
- // Other platforms: extract template and write adapter + additional files
783
815
  const templateSet = templates.get(platformId);
784
816
  if (!templateSet) {
785
817
  log.warn(`No adapter template found for ${platform.name} — skipping`);
786
818
  continue;
787
819
  }
788
820
 
789
- // Write primary adapter file
790
821
  const adapterFile = platform.adapterPath(dir);
791
822
  ensureDir(dirname(adapterFile));
792
823
  writeFileSync(adapterFile, injectVersion(templateSet.primary));
793
824
  adapterFiles.push(adapterFile);
794
825
  log.ok(`${platform.name} adapter: ${adapterFile}`);
795
826
 
796
- // Write additional files (prompts, TOMLs, skills, subagents)
797
827
  for (const [relativePath, { content }] of templateSet.files) {
798
828
  let filePath;
799
829
  if (platformId === 'codex' && opts.global && relativePath.startsWith('.agents/skills/')) {
@@ -813,7 +843,6 @@ async function cmdInstall(opts) {
813
843
  log.ok(` + ${templateSet.files.size} additional file(s)`);
814
844
  }
815
845
 
816
- // Clean up legacy .codex/skills/kc/ if present (migrated to .agents/skills/)
817
846
  if (platformId === 'codex') {
818
847
  const legacySkillDir = join(dir, '.codex', 'skills', 'kc');
819
848
  if (existsSync(legacySkillDir)) {
@@ -832,7 +861,7 @@ async function cmdInstall(opts) {
832
861
  }
833
862
  }
834
863
 
835
- // 3.5. Gemini MCP config offer (when Gemini is selected and not --force)
864
+ // Gemini MCP config offer
836
865
  if (selectedPlatforms.includes('gemini') && !opts.global && !opts.force) {
837
866
  console.log('');
838
867
  console.log(`${c.bold}Gemini MCP Configuration${c.reset}`);
@@ -852,16 +881,14 @@ async function cmdInstall(opts) {
852
881
  }
853
882
  }
854
883
 
855
- // 4. Agent Teams enablement
884
+ // Agent Teams
856
885
  const agentTeamsClaudeDir = opts.global
857
886
  ? join(process.env.HOME || process.env.USERPROFILE || '~', '.claude')
858
887
  : join(dir, '.claude');
859
- let agentTeamsEnabled = false;
860
888
  if (opts.agentTeams) {
861
889
  enableAgentTeams(agentTeamsClaudeDir, opts.global);
862
890
  agentTeamsEnabled = true;
863
891
  } else if (selectedPlatforms.includes('claude') && !opts.force) {
864
- // Interactive prompt for Claude Code users
865
892
  console.log('');
866
893
  console.log(`${c.bold}Agent Teams${c.reset} enables multi-agent coordination where specialized`);
867
894
  console.log(`teammates handle each workflow phase. ${c.dim}(experimental)${c.reset}`);
@@ -872,7 +899,193 @@ async function cmdInstall(opts) {
872
899
  }
873
900
  }
874
901
 
875
- // 5. Summary
902
+ return { adapterFiles, agentTeamsEnabled };
903
+ }
904
+
905
+ // ─── Commands ────────────────────────────────────────────────────────────────
906
+
907
+ // DETECT
908
+ function cmdDetect(opts) {
909
+ const dir = opts.target;
910
+ console.log('');
911
+ console.log(`${c.bold}KnowzCode Platform Detection${c.reset}`);
912
+ console.log(`${c.dim}Scanning: ${dir}${c.reset}`);
913
+ console.log('');
914
+
915
+ const detected = detectPlatforms(dir);
916
+ const hasKnowzcode = existsSync(join(dir, 'knowzcode'));
917
+
918
+ console.log(` KnowzCode framework: ${hasKnowzcode ? `${c.green}installed${c.reset}` : `${c.dim}not found${c.reset}`}`);
919
+
920
+ if (hasKnowzcode) {
921
+ const versionFile = join(dir, 'knowzcode', '.knowzcode-version');
922
+ if (existsSync(versionFile)) {
923
+ const ver = readFileSync(versionFile, 'utf8').trim();
924
+ console.log(` Installed version: ${c.cyan}${ver}${c.reset}`);
925
+ }
926
+ }
927
+
928
+ console.log('');
929
+ console.log(` ${c.bold}Platforms:${c.reset}`);
930
+
931
+ for (const [id, platform] of Object.entries(PLATFORMS)) {
932
+ const found = detected.includes(id);
933
+ const indicator = found ? `${c.green}detected${c.reset}` : `${c.dim}not detected${c.reset}`;
934
+ console.log(` ${platform.name.padEnd(18)} ${indicator}`);
935
+ }
936
+
937
+ console.log('');
938
+ if (detected.length === 0) {
939
+ console.log(` No platforms detected. Run ${c.cyan}npx knowzcode install${c.reset} to set up.`);
940
+ } else {
941
+ console.log(` ${detected.length} platform(s) detected.`);
942
+ }
943
+ console.log('');
944
+ }
945
+
946
+ // INSTALL
947
+ async function cmdInstall(opts) {
948
+ const dir = opts.target;
949
+ const kcDir = join(dir, 'knowzcode');
950
+
951
+ console.log('');
952
+ console.log(`${c.bold}KnowzCode Install${c.reset}`);
953
+ console.log(`${c.dim}Target: ${dir}${c.reset}`);
954
+ console.log('');
955
+
956
+ // Check for existing installation — guided flow instead of hard exit
957
+ if (existsSync(kcDir) && !opts.force) {
958
+ const scan = scanExistingInstallation(kcDir, dir);
959
+ displayInstallationSummary(scan, dir);
960
+ console.log('');
961
+ console.log(' Options:');
962
+ console.log(' [1] Add/change platform adapters only');
963
+ console.log(' [2] Reinstall framework (preserves your data)');
964
+ console.log(' [3] Cancel');
965
+ console.log('');
966
+
967
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
968
+ const answer = await rl.question('Select: ');
969
+ rl.close();
970
+
971
+ const choice = answer.trim();
972
+ if (choice === '1') return cmdAddPlatforms(opts);
973
+ if (choice === '2') { opts.force = true; /* fall through to install */ }
974
+ else return;
975
+ }
976
+
977
+ if (!existsSync(dir)) {
978
+ log.err('Target directory does not exist: ' + dir);
979
+ process.exit(1);
980
+ }
981
+
982
+ // Detect reinstall — preserve user data unless --clean
983
+ const isReinstall = existsSync(join(kcDir, '.knowzcode-version'));
984
+ const preserveFiles = isReinstall && !opts.clean ? new Set([
985
+ 'knowzcode_tracker.md', 'knowzcode_log.md',
986
+ 'knowzcode_architecture.md', 'knowzcode_project.md',
987
+ 'environment_context.md', 'user_preferences.md',
988
+ 'knowzcode_orchestration.md',
989
+ ]) : new Set();
990
+
991
+ // 1. Copy knowzcode/ template directory
992
+ log.info(isReinstall ? 'Reinstalling core framework...' : 'Installing core framework...');
993
+ const srcKc = join(PKG_ROOT, 'knowzcode');
994
+ ensureDir(kcDir);
995
+ ensureDir(join(kcDir, 'specs'));
996
+ ensureDir(join(kcDir, 'workgroups'));
997
+ ensureDir(join(kcDir, 'prompts'));
998
+
999
+ // Create workgroups/README.md (workgroups/ is gitignored and excluded from npm)
1000
+ 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');
1001
+
1002
+ // Copy .md files (skip tracker and log — generate fresh; skip preserved files on reinstall)
1003
+ for (const entry of readdirSync(srcKc)) {
1004
+ const srcPath = join(srcKc, entry);
1005
+ const stat = statSync(srcPath);
1006
+ if (stat.isFile() && entry.endsWith('.md') && entry !== 'knowzcode_tracker.md' && entry !== 'knowzcode_log.md') {
1007
+ if (preserveFiles.has(entry) && existsSync(join(kcDir, entry))) {
1008
+ if (opts.verbose) log.info(`Preserved: ${entry}`);
1009
+ continue;
1010
+ }
1011
+ writeFileSync(join(kcDir, entry), readFileSync(srcPath));
1012
+ } else if (stat.isFile() && !entry.endsWith('.md')) {
1013
+ // Copy non-md files, handling gitignore.template → .gitignore rename
1014
+ if (entry === 'gitignore.template') {
1015
+ writeFileSync(join(kcDir, '.gitignore'), readFileSync(srcPath));
1016
+ } else {
1017
+ writeFileSync(join(kcDir, entry), readFileSync(srcPath));
1018
+ }
1019
+ }
1020
+ }
1021
+
1022
+ // Copy prompts/
1023
+ if (existsSync(join(srcKc, 'prompts'))) {
1024
+ copyDirContents(join(srcKc, 'prompts'), join(kcDir, 'prompts'));
1025
+ }
1026
+
1027
+ // Copy specs readme — don't overwrite if specs/ already has user content
1028
+ const specsDir = join(kcDir, 'specs');
1029
+ const userSpecs = existsSync(specsDir) ? readdirSync(specsDir).filter(f => f.endsWith('.md') && f !== 'Readme.md') : [];
1030
+ if (userSpecs.length === 0 && existsSync(join(srcKc, 'specs', 'Readme.md'))) {
1031
+ writeFileSync(join(kcDir, 'specs', 'Readme.md'), readFileSync(join(srcKc, 'specs', 'Readme.md')));
1032
+ }
1033
+
1034
+ // Copy enterprise/ if exists
1035
+ if (existsSync(join(srcKc, 'enterprise'))) {
1036
+ copyDirContents(join(srcKc, 'enterprise'), join(kcDir, 'enterprise'));
1037
+ }
1038
+
1039
+ // Initialize tracker and log — only create fresh if not preserving
1040
+ if (!preserveFiles.has('knowzcode_tracker.md') || !existsSync(join(kcDir, 'knowzcode_tracker.md'))) {
1041
+ initTracker(join(kcDir, 'knowzcode_tracker.md'));
1042
+ }
1043
+ if (!preserveFiles.has('knowzcode_log.md') || !existsSync(join(kcDir, 'knowzcode_log.md'))) {
1044
+ initLog(join(kcDir, 'knowzcode_log.md'));
1045
+ }
1046
+
1047
+ // Write version marker
1048
+ writeFileSync(join(kcDir, '.knowzcode-version'), VERSION + '\n');
1049
+
1050
+ if (isReinstall && preserveFiles.size > 0) {
1051
+ const preserved = [];
1052
+ if (preserveFiles.has('knowzcode_tracker.md') && existsSync(join(kcDir, 'knowzcode_tracker.md'))) preserved.push('tracker');
1053
+ if (preserveFiles.has('knowzcode_log.md') && existsSync(join(kcDir, 'knowzcode_log.md'))) preserved.push('log');
1054
+ if (preserveFiles.has('knowzcode_architecture.md') && existsSync(join(kcDir, 'knowzcode_architecture.md'))) preserved.push('architecture');
1055
+ if (preserveFiles.has('knowzcode_project.md') && existsSync(join(kcDir, 'knowzcode_project.md'))) preserved.push('project config');
1056
+ if (preserveFiles.has('environment_context.md') && existsSync(join(kcDir, 'environment_context.md'))) preserved.push('environment');
1057
+ if (preserveFiles.has('user_preferences.md') && existsSync(join(kcDir, 'user_preferences.md'))) preserved.push('preferences');
1058
+ if (preserveFiles.has('knowzcode_orchestration.md') && existsSync(join(kcDir, 'knowzcode_orchestration.md'))) preserved.push('orchestration');
1059
+ log.ok(`Core framework reinstalled (preserved: ${preserved.join(', ')})`);
1060
+ } else {
1061
+ log.ok('Core framework installed');
1062
+ }
1063
+
1064
+ // 2. Platform detection + selection
1065
+ const detected = detectPlatforms(dir);
1066
+ let selectedPlatforms;
1067
+
1068
+ if (opts.platforms.length > 0) {
1069
+ if (opts.platforms.includes('all')) {
1070
+ selectedPlatforms = Object.keys(PLATFORMS);
1071
+ } else {
1072
+ selectedPlatforms = opts.platforms.filter((p) => p in PLATFORMS);
1073
+ }
1074
+ } else if (opts.force && opts.platforms.length === 0 && !isReinstall) {
1075
+ // Non-interactive mode with --force on fresh install: install for detected platforms only
1076
+ selectedPlatforms = detected;
1077
+ } else if (opts.force && opts.platforms.length === 0 && isReinstall) {
1078
+ // Reinstall: re-generate for already-installed platforms
1079
+ const scan = scanExistingInstallation(kcDir, dir);
1080
+ selectedPlatforms = scan.installedPlatforms.length > 0 ? scan.installedPlatforms : detected;
1081
+ } else {
1082
+ selectedPlatforms = await promptPlatforms(detected);
1083
+ }
1084
+
1085
+ // 3. Generate adapters (using shared helper)
1086
+ const { adapterFiles, agentTeamsEnabled } = await generateAdapters(dir, selectedPlatforms, opts);
1087
+
1088
+ // 4. Summary
876
1089
  console.log('');
877
1090
  console.log(`${c.green}${c.bold}Installation complete!${c.reset}`);
878
1091
  console.log('');
@@ -888,21 +1101,116 @@ async function cmdInstall(opts) {
888
1101
  }
889
1102
  console.log('');
890
1103
  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');
1104
+ if (!isReinstall) {
1105
+ console.log(' 1. Edit knowzcode/knowzcode_project.md — set project name, stack, standards');
1106
+ console.log(' 2. Edit knowzcode/environment_context.md — configure build/test commands');
1107
+ }
893
1108
  if (selectedPlatforms.includes('claude')) {
894
- console.log(' 3. Install the KnowzCode plugin (recommended):');
1109
+ const step = isReinstall ? 1 : 3;
1110
+ console.log(` ${step}. Install the KnowzCode plugin (recommended):`);
895
1111
  console.log(' /plugin install kc@knowzcode');
896
- console.log(' 4. Start building:');
1112
+ console.log(` ${step + 1}. Start building:`);
897
1113
  console.log(' /kc:work "Your first feature"');
898
1114
  console.log('');
899
1115
  console.log(' Note: Commands also work without plugin as /work, /plan, /fix, etc.');
900
- } else {
1116
+ } else if (!isReinstall) {
901
1117
  console.log(' 3. Start building: use knowzcode/prompts/[LOOP_1A]__Propose_Change_Set.md');
902
1118
  }
903
1119
  console.log('');
904
1120
  }
905
1121
 
1122
+ // ADD-PLATFORMS
1123
+ async function cmdAddPlatforms(opts) {
1124
+ const dir = opts.target;
1125
+ const kcDir = join(dir, 'knowzcode');
1126
+
1127
+ console.log('');
1128
+ console.log(`${c.bold}KnowzCode — Add/Change Platforms${c.reset}`);
1129
+ console.log(`${c.dim}Target: ${dir}${c.reset}`);
1130
+ console.log('');
1131
+
1132
+ if (!existsSync(kcDir)) {
1133
+ log.err('No KnowzCode installation found. Run `npx knowzcode install` first.');
1134
+ process.exit(1);
1135
+ }
1136
+
1137
+ const scan = scanExistingInstallation(kcDir, dir);
1138
+ const detected = detectPlatforms(dir);
1139
+ const ids = Object.keys(PLATFORMS);
1140
+
1141
+ // Show platform status
1142
+ console.log(`${c.bold}Platform status:${c.reset}`);
1143
+ console.log('');
1144
+ ids.forEach((id, i) => {
1145
+ const p = PLATFORMS[id];
1146
+ const installed = scan.installedPlatforms.includes(id);
1147
+ const det = detected.includes(id);
1148
+ let tag = '';
1149
+ if (installed) tag = ` ${c.green}(installed)${c.reset}`;
1150
+ else if (det) tag = ` ${c.yellow}(detected)${c.reset}`;
1151
+ console.log(` [${i + 1}] ${p.name}${tag}`);
1152
+ });
1153
+ console.log(` [A] All platforms`);
1154
+ console.log(` [S] Cancel`);
1155
+ console.log('');
1156
+
1157
+ let selectedPlatforms;
1158
+ if (opts.platforms.length > 0) {
1159
+ if (opts.platforms.includes('all')) {
1160
+ selectedPlatforms = ids;
1161
+ } else {
1162
+ selectedPlatforms = opts.platforms.filter((p) => p in PLATFORMS);
1163
+ }
1164
+ } else {
1165
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
1166
+ const answer = await rl.question('Select platforms (comma-separated, e.g. 1,2): ');
1167
+ rl.close();
1168
+
1169
+ const trimmed = answer.trim().toUpperCase();
1170
+ if (trimmed === 'S' || trimmed === '') return;
1171
+ if (trimmed === 'A') {
1172
+ selectedPlatforms = ids;
1173
+ } else {
1174
+ selectedPlatforms = [];
1175
+ for (const part of trimmed.split(',')) {
1176
+ const num = parseInt(part.trim(), 10);
1177
+ if (num >= 1 && num <= ids.length) selectedPlatforms.push(ids[num - 1]);
1178
+ }
1179
+ selectedPlatforms = [...new Set(selectedPlatforms)];
1180
+ }
1181
+ }
1182
+
1183
+ if (selectedPlatforms.length === 0) return;
1184
+
1185
+ // Confirm regeneration for already-installed platforms
1186
+ const toRegenerate = selectedPlatforms.filter(id => scan.installedPlatforms.includes(id));
1187
+ const toAdd = selectedPlatforms.filter(id => !scan.installedPlatforms.includes(id));
1188
+
1189
+ if (toRegenerate.length > 0 && !opts.force) {
1190
+ const names = toRegenerate.map(id => PLATFORMS[id].name).join(', ');
1191
+ const confirmed = await promptConfirm(`${names} already installed. Regenerate adapter(s)?`);
1192
+ if (!confirmed) {
1193
+ // Only generate the new ones
1194
+ selectedPlatforms = toAdd;
1195
+ if (selectedPlatforms.length === 0) return;
1196
+ }
1197
+ }
1198
+
1199
+ const { adapterFiles, agentTeamsEnabled } = await generateAdapters(dir, selectedPlatforms, opts);
1200
+
1201
+ console.log('');
1202
+ log.ok('Platform adapters updated');
1203
+ if (adapterFiles.length > 0) {
1204
+ for (const f of adapterFiles) {
1205
+ console.log(' ' + f);
1206
+ }
1207
+ }
1208
+ if (agentTeamsEnabled) {
1209
+ console.log(' Agent Teams: enabled');
1210
+ }
1211
+ console.log('');
1212
+ }
1213
+
906
1214
  // UNINSTALL
907
1215
  async function cmdUninstall(opts) {
908
1216
  const dir = opts.target;
@@ -1329,6 +1637,18 @@ async function cmdUpgrade(opts) {
1329
1637
  log.info('Preserved: Gemini MCP config (.gemini/settings.json)');
1330
1638
  }
1331
1639
 
1640
+ // Offer to add detected-but-uninstalled platforms
1641
+ const uninstalled = detected.filter(id => !isAdapterInstalled(id, dir));
1642
+ if (uninstalled.length > 0 && !opts.force) {
1643
+ const names = uninstalled.map(id => PLATFORMS[id].name).join(', ');
1644
+ log.info(`New platforms detected: ${names}`);
1645
+ const addNew = await promptConfirm('Generate adapters for these platforms?');
1646
+ if (addNew) {
1647
+ await generateAdapters(dir, uninstalled, opts);
1648
+ regenerated.push(...uninstalled.map(id => PLATFORMS[id].name + ' (new)'));
1649
+ }
1650
+ }
1651
+
1332
1652
  // Write new version
1333
1653
  writeFileSync(versionFile, VERSION + '\n');
1334
1654
 
@@ -1351,7 +1671,8 @@ Platform-agnostic AI development methodology
1351
1671
 
1352
1672
  ${c.bold}Usage:${c.reset}
1353
1673
  npx knowzcode Interactive mode
1354
- npx knowzcode install [options] Install to current/target directory
1674
+ npx knowzcode install [options] Install (preserves data on reinstall)
1675
+ npx knowzcode add-platforms [options] Add/change platform adapters only
1355
1676
  npx knowzcode uninstall [options] Remove KnowzCode
1356
1677
  npx knowzcode upgrade [options] Upgrade preserving user data
1357
1678
  npx knowzcode detect Show detected platforms (dry run)
@@ -1360,6 +1681,7 @@ ${c.bold}Options:${c.reset}
1360
1681
  --target <path> Target directory (default: current directory)
1361
1682
  --platforms <list> Comma-separated: claude,codex,gemini,cursor,copilot,windsurf,all
1362
1683
  --force Skip confirmation prompts
1684
+ --clean Full reset on reinstall (disables data preservation)
1363
1685
  --global Install Claude Code to ~/.claude/, Codex skills to ~/.agents/skills/, Gemini skills to ~/.gemini/skills/
1364
1686
  --agent-teams Enable Agent Teams in .claude/settings.local.json
1365
1687
  --verbose Show detailed output
@@ -1369,6 +1691,7 @@ ${c.bold}Options:${c.reset}
1369
1691
  ${c.bold}Examples:${c.reset}
1370
1692
  npx knowzcode install --platforms claude,cursor
1371
1693
  npx knowzcode install --platforms all --force
1694
+ npx knowzcode add-platforms --platforms cursor
1372
1695
  npx knowzcode upgrade --target ./my-project
1373
1696
  npx knowzcode uninstall --force
1374
1697
  npx knowzcode detect
@@ -1393,31 +1716,50 @@ async function cmdInteractive(opts) {
1393
1716
  }
1394
1717
 
1395
1718
  if (existsSync(kcDir)) {
1719
+ // Always scan and display existing installation
1720
+ const scan = scanExistingInstallation(kcDir, dir);
1721
+ displayInstallationSummary(scan, dir);
1722
+ console.log('');
1723
+
1396
1724
  const versionFile = join(kcDir, '.knowzcode-version');
1397
1725
  const currentVersion = existsSync(versionFile) ? readFileSync(versionFile, 'utf8').trim() : 'unknown';
1398
1726
 
1399
1727
  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
- }
1728
+ // Version mismatch upgrade is the primary action
1729
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
1730
+ console.log(` ${c.yellow}Update available: ${currentVersion} → ${VERSION}${c.reset}`);
1731
+ console.log('');
1732
+ console.log(' [1] Upgrade to v' + VERSION + ' (preserves all your data)');
1733
+ console.log(' [2] Add/change platform adapters');
1734
+ console.log(' [3] Reinstall framework (preserves your data)');
1735
+ console.log(' [4] Uninstall');
1736
+ console.log(' [5] Exit');
1737
+ console.log('');
1738
+ const answer = await rl.question('Select action: ');
1739
+ rl.close();
1740
+
1741
+ const choice = answer.trim();
1742
+ if (choice === '1') return cmdUpgrade(opts);
1743
+ if (choice === '2') return cmdAddPlatforms(opts);
1744
+ if (choice === '3') return cmdInstall({ ...opts, force: true });
1745
+ if (choice === '4') return cmdUninstall(opts);
1746
+ return;
1405
1747
  } else {
1406
- log.info(`KnowzCode ${currentVersion} already installed.`);
1748
+ // Same version — add platforms is the primary action
1407
1749
  const rl = createInterface({ input: process.stdin, output: process.stdout });
1408
1750
  console.log('');
1409
- console.log(' [1] Reinstall (overwrite)');
1410
- console.log(' [2] Uninstall');
1411
- console.log(' [3] Detect platforms');
1751
+ console.log(' [1] Add/change platform adapters');
1752
+ console.log(' [2] Reinstall framework (preserves your data)');
1753
+ console.log(' [3] Uninstall');
1412
1754
  console.log(' [4] Exit');
1413
1755
  console.log('');
1414
1756
  const answer = await rl.question('Select action: ');
1415
1757
  rl.close();
1416
1758
 
1417
1759
  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);
1760
+ if (choice === '1') return cmdAddPlatforms(opts);
1761
+ if (choice === '2') return cmdInstall({ ...opts, force: true });
1762
+ if (choice === '3') return cmdUninstall(opts);
1421
1763
  return;
1422
1764
  }
1423
1765
  } else {
@@ -1435,6 +1777,8 @@ async function main() {
1435
1777
  switch (opts.command) {
1436
1778
  case 'install':
1437
1779
  return cmdInstall(opts);
1780
+ case 'add-platforms':
1781
+ return cmdAddPlatforms(opts);
1438
1782
  case 'uninstall':
1439
1783
  return cmdUninstall(opts);
1440
1784
  case 'upgrade':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knowzcode",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Platform-agnostic AI development methodology with TDD, quality gates, and structured workflows",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alias-resolver",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Resolves friendly natural-language aliases to KnowzCode canonical values (phase, audit, plan, workgroup_type).",
5
5
  "parameters": [
6
6
  {"name": "domain", "type": "string", "required": true},
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "architecture-diff",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Highlights differences between specs and the Mermaid flowchart in knowzcode_architecture.md.",
5
5
  "parameters": [],
6
6
  "actions": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "check-installation-status",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Checks if KnowzCode is initialized in the current project and reports current status",
5
5
 
6
6
  "parameters": [],
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "environment-guard",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Verifies that knowzcode/environment_context.md no longer contains placeholder brackets.",
5
5
  "parameters": [],
6
6
  "actions": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "generate-workgroup-id",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Generates a WorkGroupID following the KnowzCode convention [type]-[slug]-YYYYMMDD-HHMMSS. The slug is a 2-4 word descriptor extracted from the goal.",
5
5
  "parameters": [
6
6
  {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "install-knowzcode",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Initializes KnowzCode directory structure and required files in the current project",
5
5
 
6
6
  "parameters": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "load-core-context",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Loads the KnowzCode project overview, architecture, tracker, log header, and automation manifest for downstream steps.",
5
5
  "parameters": [],
6
6
  "actions": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "log-entry-builder",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Appends structured entries to knowzcode/knowzcode_log.md with KnowzCode formatting.",
5
5
  "parameters": [
6
6
  {"name": "entry_type", "type": "string", "required": true},
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-quality-check",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Validates KnowzCode spec files for mandatory sections. Supports new 4-section format and legacy numbered format.",
5
5
  "parameters": [
6
6
  {"name": "node_id", "type": "string", "required": true}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-template",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Seeds or repairs KnowzCode spec files with the lean 4-section template.",
5
5
  "parameters": [
6
6
  {"name": "node_id", "type": "string", "required": true},
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-validator",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Validates NodeID specification completeness and quality. Supports new 4-section format (preferred) and legacy numbered sections (deprecated).",
5
5
  "parameters": [
6
6
  {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tracker-scan",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Extracts NodeID statuses and WorkGroup assignments from the KnowzCode tracker.",
5
5
  "parameters": [],
6
6
  "actions": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tracker-update",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Applies validated updates to knowzcode/knowzcode_tracker.md while preserving table structure.",
5
5
  "parameters": [
6
6
  {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "validate-installation",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Validates that KnowzCode installation completed successfully with required directories and files",
5
5
 
6
6
  "parameters": [],