claude-code-workflow 6.3.34 → 6.3.37

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 (162) hide show
  1. package/.claude/agents/cli-execution-agent.md +61 -0
  2. package/.claude/agents/cli-lite-planning-agent.md +322 -36
  3. package/.claude/agents/issue-plan-agent.md +95 -11
  4. package/.claude/commands/cli/codex-review.md +8 -2
  5. package/.claude/commands/issue/plan.md +20 -6
  6. package/.claude/commands/workflow/lite-execute.md +56 -16
  7. package/.claude/commands/workflow/lite-fix.md +108 -9
  8. package/.claude/commands/workflow/lite-plan.md +1 -1
  9. package/.claude/skills/ccw-loop/README.md +303 -0
  10. package/.claude/skills/ccw-loop/SKILL.md +259 -0
  11. package/.claude/skills/ccw-loop/phases/actions/action-complete.md +320 -0
  12. package/.claude/skills/ccw-loop/phases/actions/action-debug-with-file.md +485 -0
  13. package/.claude/skills/ccw-loop/phases/actions/action-develop-with-file.md +365 -0
  14. package/.claude/skills/ccw-loop/phases/actions/action-init.md +200 -0
  15. package/.claude/skills/ccw-loop/phases/actions/action-menu.md +192 -0
  16. package/.claude/skills/ccw-loop/phases/actions/action-validate-with-file.md +307 -0
  17. package/.claude/skills/ccw-loop/phases/orchestrator.md +486 -0
  18. package/.claude/skills/ccw-loop/phases/state-schema.md +474 -0
  19. package/.claude/skills/ccw-loop/specs/action-catalog.md +300 -0
  20. package/.claude/skills/ccw-loop/specs/loop-requirements.md +192 -0
  21. package/.claude/skills/ccw-loop/templates/progress-template.md +175 -0
  22. package/.claude/skills/ccw-loop/templates/understanding-template.md +303 -0
  23. package/.claude/skills/ccw-loop/templates/validation-template.md +258 -0
  24. package/.claude/workflows/cli-templates/schemas/issues-jsonl-schema.json +29 -0
  25. package/.claude/workflows/cli-templates/schemas/plan-json-schema.json +200 -0
  26. package/.codex/prompts/debug-with-file.md +609 -0
  27. package/ccw/dist/cli.d.ts.map +1 -1
  28. package/ccw/dist/cli.js +8 -1
  29. package/ccw/dist/cli.js.map +1 -1
  30. package/ccw/dist/commands/cli.d.ts.map +1 -1
  31. package/ccw/dist/commands/cli.js +16 -4
  32. package/ccw/dist/commands/cli.js.map +1 -1
  33. package/ccw/dist/commands/issue.d.ts.map +1 -1
  34. package/ccw/dist/commands/issue.js +37 -4
  35. package/ccw/dist/commands/issue.js.map +1 -1
  36. package/ccw/dist/commands/loop.d.ts +10 -0
  37. package/ccw/dist/commands/loop.d.ts.map +1 -0
  38. package/ccw/dist/commands/loop.js +289 -0
  39. package/ccw/dist/commands/loop.js.map +1 -0
  40. package/ccw/dist/core/dashboard-generator.d.ts.map +1 -1
  41. package/ccw/dist/core/dashboard-generator.js +4 -1
  42. package/ccw/dist/core/dashboard-generator.js.map +1 -1
  43. package/ccw/dist/core/routes/claude-routes.d.ts.map +1 -1
  44. package/ccw/dist/core/routes/claude-routes.js +5 -3
  45. package/ccw/dist/core/routes/claude-routes.js.map +1 -1
  46. package/ccw/dist/core/routes/cli-routes.d.ts +6 -0
  47. package/ccw/dist/core/routes/cli-routes.d.ts.map +1 -1
  48. package/ccw/dist/core/routes/cli-routes.js +42 -13
  49. package/ccw/dist/core/routes/cli-routes.js.map +1 -1
  50. package/ccw/dist/core/routes/cli-settings-routes.d.ts.map +1 -1
  51. package/ccw/dist/core/routes/cli-settings-routes.js +44 -0
  52. package/ccw/dist/core/routes/cli-settings-routes.js.map +1 -1
  53. package/ccw/dist/core/routes/codexlens/semantic-handlers.d.ts.map +1 -1
  54. package/ccw/dist/core/routes/codexlens/semantic-handlers.js +3 -2
  55. package/ccw/dist/core/routes/codexlens/semantic-handlers.js.map +1 -1
  56. package/ccw/dist/core/routes/core-memory-routes.d.ts.map +1 -1
  57. package/ccw/dist/core/routes/core-memory-routes.js +4 -2
  58. package/ccw/dist/core/routes/core-memory-routes.js.map +1 -1
  59. package/ccw/dist/core/routes/files-routes.d.ts.map +1 -1
  60. package/ccw/dist/core/routes/files-routes.js +4 -2
  61. package/ccw/dist/core/routes/files-routes.js.map +1 -1
  62. package/ccw/dist/core/routes/hooks-routes.d.ts.map +1 -1
  63. package/ccw/dist/core/routes/hooks-routes.js +3 -0
  64. package/ccw/dist/core/routes/hooks-routes.js.map +1 -1
  65. package/ccw/dist/core/routes/loop-routes.d.ts +24 -0
  66. package/ccw/dist/core/routes/loop-routes.d.ts.map +1 -0
  67. package/ccw/dist/core/routes/loop-routes.js +334 -0
  68. package/ccw/dist/core/routes/loop-routes.js.map +1 -0
  69. package/ccw/dist/core/routes/loop-v2-routes.d.ts +35 -0
  70. package/ccw/dist/core/routes/loop-v2-routes.d.ts.map +1 -0
  71. package/ccw/dist/core/routes/loop-v2-routes.js +1208 -0
  72. package/ccw/dist/core/routes/loop-v2-routes.js.map +1 -0
  73. package/ccw/dist/core/routes/memory-routes.d.ts.map +1 -1
  74. package/ccw/dist/core/routes/memory-routes.js +2 -1
  75. package/ccw/dist/core/routes/memory-routes.js.map +1 -1
  76. package/ccw/dist/core/routes/task-routes.d.ts +12 -0
  77. package/ccw/dist/core/routes/task-routes.d.ts.map +1 -0
  78. package/ccw/dist/core/routes/task-routes.js +321 -0
  79. package/ccw/dist/core/routes/task-routes.js.map +1 -0
  80. package/ccw/dist/core/routes/test-loop-routes.d.ts +11 -0
  81. package/ccw/dist/core/routes/test-loop-routes.d.ts.map +1 -0
  82. package/ccw/dist/core/routes/test-loop-routes.js +298 -0
  83. package/ccw/dist/core/routes/test-loop-routes.js.map +1 -0
  84. package/ccw/dist/core/server.d.ts.map +1 -1
  85. package/ccw/dist/core/server.js +43 -3
  86. package/ccw/dist/core/server.js.map +1 -1
  87. package/ccw/dist/core/websocket.d.ts +59 -0
  88. package/ccw/dist/core/websocket.d.ts.map +1 -1
  89. package/ccw/dist/core/websocket.js +34 -0
  90. package/ccw/dist/core/websocket.js.map +1 -1
  91. package/ccw/dist/tools/claude-cli-tools.d.ts +40 -0
  92. package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -1
  93. package/ccw/dist/tools/claude-cli-tools.js +119 -0
  94. package/ccw/dist/tools/claude-cli-tools.js.map +1 -1
  95. package/ccw/dist/tools/codex-lens.d.ts.map +1 -1
  96. package/ccw/dist/tools/codex-lens.js +66 -47
  97. package/ccw/dist/tools/codex-lens.js.map +1 -1
  98. package/ccw/dist/tools/loop-manager.d.ts +84 -0
  99. package/ccw/dist/tools/loop-manager.d.ts.map +1 -0
  100. package/ccw/dist/tools/loop-manager.js +425 -0
  101. package/ccw/dist/tools/loop-manager.js.map +1 -0
  102. package/ccw/dist/tools/loop-state-manager.d.ts +47 -0
  103. package/ccw/dist/tools/loop-state-manager.d.ts.map +1 -0
  104. package/ccw/dist/tools/loop-state-manager.js +149 -0
  105. package/ccw/dist/tools/loop-state-manager.js.map +1 -0
  106. package/ccw/dist/tools/loop-task-manager.d.ts +138 -0
  107. package/ccw/dist/tools/loop-task-manager.d.ts.map +1 -0
  108. package/ccw/dist/tools/loop-task-manager.js +270 -0
  109. package/ccw/dist/tools/loop-task-manager.js.map +1 -0
  110. package/ccw/dist/types/index.d.ts +1 -0
  111. package/ccw/dist/types/index.d.ts.map +1 -1
  112. package/ccw/dist/types/index.js +1 -0
  113. package/ccw/dist/types/index.js.map +1 -1
  114. package/ccw/dist/types/loop.d.ts +257 -0
  115. package/ccw/dist/types/loop.d.ts.map +1 -0
  116. package/ccw/dist/types/loop.js +17 -0
  117. package/ccw/dist/types/loop.js.map +1 -0
  118. package/ccw/scripts/IMPLEMENTATION-SUMMARY.md +2 -2
  119. package/ccw/scripts/QUICK-REFERENCE.md +1 -1
  120. package/ccw/scripts/README-memory-embedder.md +1 -1
  121. package/ccw/scripts/memory_embedder.py +1 -1
  122. package/ccw/src/cli.ts +9 -1
  123. package/ccw/src/commands/cli.ts +16 -4
  124. package/ccw/src/commands/issue.ts +41 -5
  125. package/ccw/src/commands/loop.ts +344 -0
  126. package/ccw/src/core/dashboard-generator.ts +4 -1
  127. package/ccw/src/core/routes/claude-routes.ts +5 -3
  128. package/ccw/src/core/routes/cli-routes.ts +47 -15
  129. package/ccw/src/core/routes/cli-settings-routes.ts +47 -0
  130. package/ccw/src/core/routes/codexlens/semantic-handlers.ts +3 -2
  131. package/ccw/src/core/routes/core-memory-routes.ts +4 -2
  132. package/ccw/src/core/routes/files-routes.ts +4 -2
  133. package/ccw/src/core/routes/hooks-routes.ts +3 -0
  134. package/ccw/src/core/routes/loop-routes.ts +386 -0
  135. package/ccw/src/core/routes/loop-v2-routes.ts +1412 -0
  136. package/ccw/src/core/routes/memory-routes.ts +2 -1
  137. package/ccw/src/core/routes/task-routes.ts +361 -0
  138. package/ccw/src/core/routes/test-loop-routes.ts +312 -0
  139. package/ccw/src/core/server.ts +44 -3
  140. package/ccw/src/core/websocket.ts +104 -0
  141. package/ccw/src/templates/dashboard-css/12-cli-legacy.css +56 -0
  142. package/ccw/src/templates/dashboard-css/32-issue-manager.css +160 -0
  143. package/ccw/src/templates/dashboard-css/33-cli-stream-viewer.css +57 -2
  144. package/ccw/src/templates/dashboard-css/36-loop-monitor.css +1896 -0
  145. package/ccw/src/templates/dashboard-css/36-loop-monitor.css.backup +1877 -0
  146. package/ccw/src/templates/dashboard-js/components/cli-status.js +64 -3
  147. package/ccw/src/templates/dashboard-js/components/cli-stream-viewer.js +251 -110
  148. package/ccw/src/templates/dashboard-js/components/navigation.js +10 -0
  149. package/ccw/src/templates/dashboard-js/components/notifications.js +16 -0
  150. package/ccw/src/templates/dashboard-js/i18n.js +475 -1
  151. package/ccw/src/templates/dashboard-js/views/cli-manager.js +3 -2
  152. package/ccw/src/templates/dashboard-js/views/issue-manager.js +159 -0
  153. package/ccw/src/templates/dashboard-js/views/loop-monitor.js +3244 -0
  154. package/ccw/src/templates/dashboard.html +20 -2
  155. package/ccw/src/tools/claude-cli-tools.ts +143 -0
  156. package/ccw/src/tools/codex-lens.ts +71 -44
  157. package/ccw/src/tools/loop-manager.ts +519 -0
  158. package/ccw/src/tools/loop-state-manager.ts +173 -0
  159. package/ccw/src/tools/loop-task-manager.ts +380 -0
  160. package/ccw/src/types/index.ts +1 -0
  161. package/ccw/src/types/loop.ts +316 -0
  162. package/package.json +1 -1
@@ -525,6 +525,22 @@
525
525
  </ul>
526
526
  </div>
527
527
 
528
+ <!-- Loops Section -->
529
+ <div class="mb-2" id="loopsNav">
530
+ <div class="flex items-center px-4 py-2 text-sm font-semibold text-muted-foreground uppercase tracking-wide">
531
+ <i data-lucide="repeat" class="nav-section-icon mr-2"></i>
532
+ <span class="nav-section-title" data-i18n="nav.loops">Loops</span>
533
+ </div>
534
+ <ul class="space-y-0.5">
535
+ <li class="nav-item flex items-center gap-2 px-3 py-2.5 text-sm text-muted-foreground hover:bg-hover hover:text-foreground rounded cursor-pointer transition-colors" data-view="loop-monitor" data-tooltip="Loop Monitor">
536
+ <i data-lucide="activity" class="nav-icon text-cyan"></i>
537
+ <span class="nav-text flex-1" data-i18n="nav.loopMonitor">Monitor</span>
538
+ <span class="badge px-1.5 py-0.5 text-xs font-semibold rounded bg-yellow-light text-yellow" data-i18n="nav.inDevelopment">In Dev</span>
539
+ <span class="badge px-2 py-0.5 text-xs font-semibold rounded-full bg-cyan-light text-cyan" id="badgeLoops">0</span>
540
+ </li>
541
+ </ul>
542
+ </div>
543
+
528
544
  <!-- Issues Section -->
529
545
  <div class="mb-2" id="issuesNav">
530
546
  <div class="flex items-center px-4 py-2 text-sm font-semibold text-muted-foreground uppercase tracking-wide">
@@ -752,9 +768,11 @@
752
768
  <button class="cli-stream-search-clear" onclick="clearSearch()" title="Clear search">&times;</button>
753
769
  </div>
754
770
  <div class="cli-stream-actions">
755
- <button class="cli-stream-action-btn" onclick="clearCompletedStreams()" data-i18n="cliStream.clearCompleted">
771
+ <button class="cli-stream-icon-btn" onclick="clearCompletedStreams()" title="Clear completed">
772
+ <i data-lucide="check-circle"></i>
773
+ </button>
774
+ <button class="cli-stream-icon-btn" onclick="clearAllStreams()" title="Clear all">
756
775
  <i data-lucide="trash-2"></i>
757
- <span>Clear</span>
758
776
  </button>
759
777
  <button class="cli-stream-close-btn" onclick="toggleCliStreamViewer()" title="Close">&times;</button>
760
778
  </div>
@@ -610,6 +610,19 @@ export function updateClaudeDefaultTool(
610
610
  return settings;
611
611
  }
612
612
 
613
+ /**
614
+ * Get the default tool from config
615
+ * Returns the configured defaultTool or 'gemini' as fallback
616
+ */
617
+ export function getDefaultTool(projectDir: string): string {
618
+ try {
619
+ const settings = loadClaudeCliSettings(projectDir);
620
+ return settings.defaultTool || 'gemini';
621
+ } catch {
622
+ return 'gemini';
623
+ }
624
+ }
625
+
613
626
  /**
614
627
  * Add API endpoint as a tool with type: 'api-endpoint'
615
628
  * Usage: --tool <name> or --tool custom --model <id>
@@ -943,3 +956,133 @@ export function getFullConfigResponse(projectDir: string): {
943
956
  predefinedModels: { ...PREDEFINED_MODELS }
944
957
  };
945
958
  }
959
+
960
+ // ========== Tool Detection & Sync Functions ==========
961
+
962
+ /**
963
+ * Sync builtin tools availability with cli-tools.json
964
+ *
965
+ * For builtin tools (gemini, qwen, codex, claude, opencode):
966
+ * - Checks actual tool availability using system PATH
967
+ * - Updates enabled status based on actual availability
968
+ *
969
+ * For non-builtin tools (cli-wrapper, api-endpoint):
970
+ * - Leaves them unchanged as they have different availability mechanisms
971
+ *
972
+ * @returns Updated config and sync results
973
+ */
974
+ export async function syncBuiltinToolsAvailability(projectDir: string): Promise<{
975
+ config: ClaudeCliToolsConfig;
976
+ changes: {
977
+ enabled: string[]; // Tools that were enabled
978
+ disabled: string[]; // Tools that were disabled
979
+ unchanged: string[]; // Tools that stayed the same
980
+ };
981
+ }> {
982
+ // Import getCliToolsStatus dynamically to avoid circular dependency
983
+ const { getCliToolsStatus } = await import('./cli-executor.js');
984
+
985
+ // Get actual tool availability
986
+ const actualStatus = await getCliToolsStatus();
987
+
988
+ // Load current config
989
+ const config = loadClaudeCliTools(projectDir);
990
+ const changes = {
991
+ enabled: [] as string[],
992
+ disabled: [] as string[],
993
+ unchanged: [] as string[]
994
+ };
995
+
996
+ // Builtin tools that need sync
997
+ const builtinTools = ['gemini', 'qwen', 'codex', 'claude', 'opencode'];
998
+
999
+ for (const toolName of builtinTools) {
1000
+ const isAvailable = actualStatus[toolName]?.available ?? false;
1001
+ const currentConfig = config.tools[toolName];
1002
+ const wasEnabled = currentConfig?.enabled ?? true;
1003
+
1004
+ // Update based on actual availability
1005
+ if (isAvailable && !wasEnabled) {
1006
+ // Tool exists but was disabled - enable it
1007
+ if (!currentConfig) {
1008
+ config.tools[toolName] = {
1009
+ enabled: true,
1010
+ primaryModel: DEFAULT_TOOLS_CONFIG.tools[toolName]?.primaryModel || '',
1011
+ secondaryModel: DEFAULT_TOOLS_CONFIG.tools[toolName]?.secondaryModel || '',
1012
+ tags: [],
1013
+ type: 'builtin'
1014
+ };
1015
+ } else {
1016
+ currentConfig.enabled = true;
1017
+ }
1018
+ changes.enabled.push(toolName);
1019
+ } else if (!isAvailable && wasEnabled) {
1020
+ // Tool doesn't exist but was enabled - disable it
1021
+ if (currentConfig) {
1022
+ currentConfig.enabled = false;
1023
+ }
1024
+ changes.disabled.push(toolName);
1025
+ } else {
1026
+ // No change needed
1027
+ changes.unchanged.push(toolName);
1028
+ }
1029
+ }
1030
+
1031
+ // Save updated config
1032
+ saveClaudeCliTools(projectDir, config);
1033
+
1034
+ console.log('[claude-cli-tools] Synced builtin tools availability:', {
1035
+ enabled: changes.enabled,
1036
+ disabled: changes.disabled,
1037
+ unchanged: changes.unchanged
1038
+ });
1039
+
1040
+ return { config, changes };
1041
+ }
1042
+
1043
+ /**
1044
+ * Get sync status report without actually modifying config
1045
+ *
1046
+ * @returns Report showing what would change if sync were run
1047
+ */
1048
+ export async function getBuiltinToolsSyncReport(projectDir: string): Promise<{
1049
+ current: Record<string, { available: boolean; enabled: boolean }>;
1050
+ recommended: Record<string, { shouldEnable: boolean; reason: string }>;
1051
+ }> {
1052
+ // Import getCliToolsStatus dynamically to avoid circular dependency
1053
+ const { getCliToolsStatus } = await import('./cli-executor.js');
1054
+
1055
+ // Get actual tool availability
1056
+ const actualStatus = await getCliToolsStatus();
1057
+
1058
+ // Load current config
1059
+ const config = loadClaudeCliTools(projectDir);
1060
+ const builtinTools = ['gemini', 'qwen', 'codex', 'claude', 'opencode'];
1061
+
1062
+ const current: Record<string, { available: boolean; enabled: boolean }> = {};
1063
+ const recommended: Record<string, { shouldEnable: boolean; reason: string }> = {};
1064
+
1065
+ for (const toolName of builtinTools) {
1066
+ const isAvailable = actualStatus[toolName]?.available ?? false;
1067
+ const isEnabled = config.tools[toolName]?.enabled ?? true;
1068
+
1069
+ current[toolName] = {
1070
+ available: isAvailable,
1071
+ enabled: isEnabled
1072
+ };
1073
+
1074
+ if (isAvailable && !isEnabled) {
1075
+ recommended[toolName] = {
1076
+ shouldEnable: true,
1077
+ reason: 'Tool is installed but disabled in config'
1078
+ };
1079
+ } else if (!isAvailable && isEnabled) {
1080
+ recommended[toolName] = {
1081
+ shouldEnable: false,
1082
+ reason: 'Tool is not installed but enabled in config'
1083
+ };
1084
+ }
1085
+ }
1086
+
1087
+ return { current, recommended };
1088
+ }
@@ -60,11 +60,9 @@ function isDevEnvironment(): boolean {
60
60
  * breaking any editable (-e) pip installs that reference them.
61
61
  */
62
62
  function findLocalPackagePath(packageName: string): string | null {
63
- // If running from node_modules, skip local paths entirely - use PyPI
64
- if (!isDevEnvironment()) {
65
- console.log(`[CodexLens] Running from node_modules - will use PyPI for ${packageName}`);
66
- return null;
67
- }
63
+ // Always try to find local paths first, even when running from node_modules.
64
+ // codex-lens is a local development package not published to PyPI,
65
+ // so we must find it locally regardless of execution context.
68
66
 
69
67
  const possiblePaths = [
70
68
  join(process.cwd(), packageName),
@@ -72,16 +70,28 @@ function findLocalPackagePath(packageName: string): string | null {
72
70
  join(homedir(), packageName),
73
71
  ];
74
72
 
73
+ // Also check common workspace locations
74
+ const cwd = process.cwd();
75
+ const cwdParent = dirname(cwd);
76
+ if (cwdParent !== cwd) {
77
+ possiblePaths.push(join(cwdParent, packageName));
78
+ }
79
+
75
80
  for (const localPath of possiblePaths) {
76
81
  // Skip paths inside node_modules
77
82
  if (isInsideNodeModules(localPath)) {
78
83
  continue;
79
84
  }
80
85
  if (existsSync(join(localPath, 'pyproject.toml'))) {
86
+ console.log(`[CodexLens] Found local ${packageName} at: ${localPath}`);
81
87
  return localPath;
82
88
  }
83
89
  }
84
90
 
91
+ if (!isDevEnvironment()) {
92
+ console.log(`[CodexLens] Running from node_modules - will try PyPI for ${packageName}`);
93
+ }
94
+
85
95
  return null;
86
96
  }
87
97
 
@@ -662,21 +672,24 @@ async function bootstrapWithUv(gpuMode: GpuMode = 'cpu'): Promise<BootstrapResul
662
672
  // Determine extras based on GPU mode
663
673
  const extras = GPU_MODE_EXTRAS[gpuMode];
664
674
 
665
- if (codexLensPath) {
666
- console.log(`[CodexLens] Installing from local path with UV: ${codexLensPath}`);
667
- console.log(`[CodexLens] Extras: ${extras.join(', ')}`);
668
- const installResult = await uv.installFromProject(codexLensPath, extras);
669
- if (!installResult.success) {
670
- return { success: false, error: `Failed to install codexlens: ${installResult.error}` };
671
- }
672
- } else {
673
- // Install from PyPI with extras
674
- console.log('[CodexLens] Installing from PyPI with UV...');
675
- const packageSpec = `codexlens[${extras.join(',')}]`;
676
- const installResult = await uv.install([packageSpec]);
677
- if (!installResult.success) {
678
- return { success: false, error: `Failed to install codexlens: ${installResult.error}` };
679
- }
675
+ if (!codexLensPath) {
676
+ // codex-lens is a local-only package, not published to PyPI
677
+ const errorMsg = `Cannot find codex-lens directory for local installation.\n\n` +
678
+ `codex-lens is a local development package (not published to PyPI) and must be installed from local files.\n\n` +
679
+ `To fix this:\n` +
680
+ `1. Ensure the 'codex-lens' directory exists in your project root\n` +
681
+ ` Expected location: D:\\Claude_dms3\\codex-lens\n` +
682
+ `2. Verify pyproject.toml exists: D:\\Claude_dms3\\codex-lens\\pyproject.toml\n` +
683
+ `3. Run ccw from the correct working directory (e.g., D:\\Claude_dms3)\n` +
684
+ `4. Or manually install: cd D:\\Claude_dms3\\codex-lens && pip install -e .[${extras.join(',')}]`;
685
+ return { success: false, error: errorMsg };
686
+ }
687
+
688
+ console.log(`[CodexLens] Installing from local path with UV: ${codexLensPath}`);
689
+ console.log(`[CodexLens] Extras: ${extras.join(', ')}`);
690
+ const installResult = await uv.installFromProject(codexLensPath, extras);
691
+ if (!installResult.success) {
692
+ return { success: false, error: `Failed to install codex-lens: ${installResult.error}` };
680
693
  }
681
694
 
682
695
  // Clear cache after successful installation
@@ -733,20 +746,22 @@ async function installSemanticWithUv(gpuMode: GpuMode = 'cpu'): Promise<Bootstra
733
746
  console.log(`[CodexLens] Extras: ${extras.join(', ')}`);
734
747
 
735
748
  // Install with extras - UV handles dependency conflicts automatically
736
- if (codexLensPath) {
737
- console.log(`[CodexLens] Reinstalling from local path with semantic extras...`);
738
- const installResult = await uv.installFromProject(codexLensPath, extras);
739
- if (!installResult.success) {
740
- return { success: false, error: `Installation failed: ${installResult.error}` };
741
- }
742
- } else {
743
- // Install from PyPI
744
- const packageSpec = `codexlens[${extras.join(',')}]`;
745
- console.log(`[CodexLens] Installing ${packageSpec} from PyPI...`);
746
- const installResult = await uv.install([packageSpec]);
747
- if (!installResult.success) {
748
- return { success: false, error: `Installation failed: ${installResult.error}` };
749
- }
749
+ if (!codexLensPath) {
750
+ // codex-lens is a local-only package, not published to PyPI
751
+ const errorMsg = `Cannot find codex-lens directory for local installation.\n\n` +
752
+ `codex-lens is a local development package (not published to PyPI) and must be installed from local files.\n\n` +
753
+ `To fix this:\n` +
754
+ `1. Ensure the 'codex-lens' directory exists in your project root\n` +
755
+ `2. Verify pyproject.toml exists in codex-lens directory\n` +
756
+ `3. Run ccw from the correct working directory\n` +
757
+ `4. Or manually install: cd codex-lens && pip install -e .[${extras.join(',')}]`;
758
+ return { success: false, error: errorMsg };
759
+ }
760
+
761
+ console.log(`[CodexLens] Reinstalling from local path with semantic extras...`);
762
+ const installResult = await uv.installFromProject(codexLensPath, extras);
763
+ if (!installResult.success) {
764
+ return { success: false, error: `Installation failed: ${installResult.error}` };
750
765
  }
751
766
 
752
767
  console.log(`[CodexLens] Semantic dependencies installed successfully (${gpuMode} mode)`);
@@ -933,31 +948,43 @@ async function bootstrapVenv(): Promise<BootstrapResult> {
933
948
  }
934
949
  }
935
950
 
936
- // Install codexlens with semantic extras
951
+ // Install codex-lens
937
952
  try {
938
- console.log('[CodexLens] Installing codexlens package...');
953
+ console.log('[CodexLens] Installing codex-lens package...');
939
954
  const pipPath =
940
955
  process.platform === 'win32'
941
956
  ? join(CODEXLENS_VENV, 'Scripts', 'pip.exe')
942
957
  : join(CODEXLENS_VENV, 'bin', 'pip');
943
958
 
944
- // Try local path if in development (not from node_modules), then fall back to PyPI
959
+ // Try local path - codex-lens is local-only, not published to PyPI
945
960
  const codexLensPath = findLocalCodexLensPath();
946
961
 
947
- if (codexLensPath) {
948
- console.log(`[CodexLens] Installing from local path: ${codexLensPath}`);
949
- execSync(`"${pipPath}" install -e "${codexLensPath}"`, { stdio: 'inherit', timeout: EXEC_TIMEOUTS.PACKAGE_INSTALL });
950
- } else {
951
- console.log('[CodexLens] Installing from PyPI...');
952
- execSync(`"${pipPath}" install codexlens`, { stdio: 'inherit', timeout: EXEC_TIMEOUTS.PACKAGE_INSTALL });
962
+ if (!codexLensPath) {
963
+ // codex-lens is a local-only package, not published to PyPI
964
+ const errorMsg = `Cannot find codex-lens directory for local installation.\n\n` +
965
+ `codex-lens is a local development package (not published to PyPI) and must be installed from local files.\n\n` +
966
+ `To fix this:\n` +
967
+ `1. Ensure the 'codex-lens' directory exists in your project root\n` +
968
+ `2. Verify pyproject.toml exists in codex-lens directory\n` +
969
+ `3. Run ccw from the correct working directory\n` +
970
+ `4. Or manually install: cd codex-lens && pip install -e .`;
971
+ throw new Error(errorMsg);
953
972
  }
954
973
 
974
+ console.log(`[CodexLens] Installing from local path: ${codexLensPath}`);
975
+ execSync(`"${pipPath}" install -e "${codexLensPath}"`, { stdio: 'inherit', timeout: EXEC_TIMEOUTS.PACKAGE_INSTALL });
976
+
955
977
  // Clear cache after successful installation
956
978
  clearVenvStatusCache();
957
979
  clearSemanticStatusCache();
958
980
  return { success: true };
959
981
  } catch (err) {
960
- return { success: false, error: `Failed to install codexlens: ${(err as Error).message}` };
982
+ const errorMsg = `Failed to install codex-lens: ${(err as Error).message}\n\n` +
983
+ `codex-lens is a local development package. To fix this:\n` +
984
+ `1. Ensure the 'codex-lens' directory exists in your project root\n` +
985
+ `2. Run the installation from the correct working directory\n` +
986
+ `3. Or manually install: cd codex-lens && pip install -e .`;
987
+ return { success: false, error: errorMsg };
961
988
  }
962
989
  }
963
990