claude-code-workflow 6.2.7 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/.claude/CLAUDE.md +16 -1
  2. package/.claude/workflows/cli-templates/protocols/analysis-protocol.md +11 -4
  3. package/.claude/workflows/cli-templates/protocols/write-protocol.md +10 -75
  4. package/.claude/workflows/cli-tools-usage.md +14 -24
  5. package/.codex/AGENTS.md +51 -1
  6. package/.codex/prompts/compact.md +378 -0
  7. package/.gemini/GEMINI.md +57 -20
  8. package/ccw/dist/cli.d.ts.map +1 -1
  9. package/ccw/dist/cli.js +21 -8
  10. package/ccw/dist/cli.js.map +1 -1
  11. package/ccw/dist/commands/cli.d.ts +2 -0
  12. package/ccw/dist/commands/cli.d.ts.map +1 -1
  13. package/ccw/dist/commands/cli.js +129 -8
  14. package/ccw/dist/commands/cli.js.map +1 -1
  15. package/ccw/dist/commands/hook.d.ts.map +1 -1
  16. package/ccw/dist/commands/hook.js +3 -2
  17. package/ccw/dist/commands/hook.js.map +1 -1
  18. package/ccw/dist/config/litellm-api-config-manager.d.ts +180 -0
  19. package/ccw/dist/config/litellm-api-config-manager.d.ts.map +1 -0
  20. package/ccw/dist/config/litellm-api-config-manager.js +770 -0
  21. package/ccw/dist/config/litellm-api-config-manager.js.map +1 -0
  22. package/ccw/dist/config/provider-models.d.ts +73 -0
  23. package/ccw/dist/config/provider-models.d.ts.map +1 -0
  24. package/ccw/dist/config/provider-models.js +172 -0
  25. package/ccw/dist/config/provider-models.js.map +1 -0
  26. package/ccw/dist/core/cache-manager.d.ts.map +1 -1
  27. package/ccw/dist/core/cache-manager.js +3 -5
  28. package/ccw/dist/core/cache-manager.js.map +1 -1
  29. package/ccw/dist/core/dashboard-generator.d.ts.map +1 -1
  30. package/ccw/dist/core/dashboard-generator.js +3 -1
  31. package/ccw/dist/core/dashboard-generator.js.map +1 -1
  32. package/ccw/dist/core/routes/cli-routes.d.ts.map +1 -1
  33. package/ccw/dist/core/routes/cli-routes.js +169 -0
  34. package/ccw/dist/core/routes/cli-routes.js.map +1 -1
  35. package/ccw/dist/core/routes/codexlens-routes.d.ts.map +1 -1
  36. package/ccw/dist/core/routes/codexlens-routes.js +234 -18
  37. package/ccw/dist/core/routes/codexlens-routes.js.map +1 -1
  38. package/ccw/dist/core/routes/hooks-routes.d.ts.map +1 -1
  39. package/ccw/dist/core/routes/hooks-routes.js +30 -32
  40. package/ccw/dist/core/routes/hooks-routes.js.map +1 -1
  41. package/ccw/dist/core/routes/litellm-api-routes.d.ts +21 -0
  42. package/ccw/dist/core/routes/litellm-api-routes.d.ts.map +1 -0
  43. package/ccw/dist/core/routes/litellm-api-routes.js +780 -0
  44. package/ccw/dist/core/routes/litellm-api-routes.js.map +1 -0
  45. package/ccw/dist/core/routes/litellm-routes.d.ts +20 -0
  46. package/ccw/dist/core/routes/litellm-routes.d.ts.map +1 -0
  47. package/ccw/dist/core/routes/litellm-routes.js +85 -0
  48. package/ccw/dist/core/routes/litellm-routes.js.map +1 -0
  49. package/ccw/dist/core/routes/mcp-routes.js +2 -2
  50. package/ccw/dist/core/routes/mcp-routes.js.map +1 -1
  51. package/ccw/dist/core/routes/status-routes.d.ts.map +1 -1
  52. package/ccw/dist/core/routes/status-routes.js +39 -0
  53. package/ccw/dist/core/routes/status-routes.js.map +1 -1
  54. package/ccw/dist/core/routes/system-routes.js +1 -1
  55. package/ccw/dist/core/routes/system-routes.js.map +1 -1
  56. package/ccw/dist/core/server.d.ts.map +1 -1
  57. package/ccw/dist/core/server.js +15 -1
  58. package/ccw/dist/core/server.js.map +1 -1
  59. package/ccw/dist/mcp-server/index.js +1 -1
  60. package/ccw/dist/mcp-server/index.js.map +1 -1
  61. package/ccw/dist/tools/claude-cli-tools.d.ts +82 -0
  62. package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -0
  63. package/ccw/dist/tools/claude-cli-tools.js +216 -0
  64. package/ccw/dist/tools/claude-cli-tools.js.map +1 -0
  65. package/ccw/dist/tools/cli-executor.d.ts.map +1 -1
  66. package/ccw/dist/tools/cli-executor.js +76 -14
  67. package/ccw/dist/tools/cli-executor.js.map +1 -1
  68. package/ccw/dist/tools/codex-lens.d.ts +9 -2
  69. package/ccw/dist/tools/codex-lens.d.ts.map +1 -1
  70. package/ccw/dist/tools/codex-lens.js +114 -9
  71. package/ccw/dist/tools/codex-lens.js.map +1 -1
  72. package/ccw/dist/tools/context-cache-store.d.ts +136 -0
  73. package/ccw/dist/tools/context-cache-store.d.ts.map +1 -0
  74. package/ccw/dist/tools/context-cache-store.js +256 -0
  75. package/ccw/dist/tools/context-cache-store.js.map +1 -0
  76. package/ccw/dist/tools/context-cache.d.ts +56 -0
  77. package/ccw/dist/tools/context-cache.d.ts.map +1 -0
  78. package/ccw/dist/tools/context-cache.js +294 -0
  79. package/ccw/dist/tools/context-cache.js.map +1 -0
  80. package/ccw/dist/tools/core-memory.d.ts.map +1 -1
  81. package/ccw/dist/tools/core-memory.js +33 -19
  82. package/ccw/dist/tools/core-memory.js.map +1 -1
  83. package/ccw/dist/tools/index.d.ts.map +1 -1
  84. package/ccw/dist/tools/index.js +2 -0
  85. package/ccw/dist/tools/index.js.map +1 -1
  86. package/ccw/dist/tools/litellm-client.d.ts +85 -0
  87. package/ccw/dist/tools/litellm-client.d.ts.map +1 -0
  88. package/ccw/dist/tools/litellm-client.js +188 -0
  89. package/ccw/dist/tools/litellm-client.js.map +1 -0
  90. package/ccw/dist/tools/litellm-executor.d.ts +34 -0
  91. package/ccw/dist/tools/litellm-executor.d.ts.map +1 -0
  92. package/ccw/dist/tools/litellm-executor.js +192 -0
  93. package/ccw/dist/tools/litellm-executor.js.map +1 -0
  94. package/ccw/dist/tools/pattern-parser.d.ts +55 -0
  95. package/ccw/dist/tools/pattern-parser.d.ts.map +1 -0
  96. package/ccw/dist/tools/pattern-parser.js +237 -0
  97. package/ccw/dist/tools/pattern-parser.js.map +1 -0
  98. package/ccw/dist/tools/smart-search.d.ts +1 -0
  99. package/ccw/dist/tools/smart-search.d.ts.map +1 -1
  100. package/ccw/dist/tools/smart-search.js +117 -41
  101. package/ccw/dist/tools/smart-search.js.map +1 -1
  102. package/ccw/dist/types/litellm-api-config.d.ts +294 -0
  103. package/ccw/dist/types/litellm-api-config.d.ts.map +1 -0
  104. package/ccw/dist/types/litellm-api-config.js +8 -0
  105. package/ccw/dist/types/litellm-api-config.js.map +1 -0
  106. package/ccw/src/cli.ts +258 -244
  107. package/ccw/src/commands/cli.ts +153 -9
  108. package/ccw/src/commands/hook.ts +3 -2
  109. package/ccw/src/config/.litellm-api-config-manager.ts.2025-12-23T11-57-43-727Z.bak +441 -0
  110. package/ccw/src/config/litellm-api-config-manager.ts +1012 -0
  111. package/ccw/src/config/provider-models.ts +222 -0
  112. package/ccw/src/core/cache-manager.ts +292 -294
  113. package/ccw/src/core/dashboard-generator.ts +3 -1
  114. package/ccw/src/core/routes/cli-routes.ts +192 -0
  115. package/ccw/src/core/routes/codexlens-routes.ts +241 -19
  116. package/ccw/src/core/routes/hooks-routes.ts +399 -405
  117. package/ccw/src/core/routes/litellm-api-routes.ts +930 -0
  118. package/ccw/src/core/routes/litellm-routes.ts +107 -0
  119. package/ccw/src/core/routes/mcp-routes.ts +1271 -1271
  120. package/ccw/src/core/routes/status-routes.ts +51 -0
  121. package/ccw/src/core/routes/system-routes.ts +1 -1
  122. package/ccw/src/core/server.ts +15 -1
  123. package/ccw/src/mcp-server/index.ts +1 -1
  124. package/ccw/src/templates/dashboard-css/12-cli-legacy.css +44 -0
  125. package/ccw/src/templates/dashboard-css/31-api-settings.css +2265 -0
  126. package/ccw/src/templates/dashboard-js/components/cli-history.js +15 -8
  127. package/ccw/src/templates/dashboard-js/components/cli-status.js +323 -9
  128. package/ccw/src/templates/dashboard-js/components/navigation.js +329 -313
  129. package/ccw/src/templates/dashboard-js/i18n.js +583 -1
  130. package/ccw/src/templates/dashboard-js/views/api-settings.js +3362 -0
  131. package/ccw/src/templates/dashboard-js/views/cli-manager.js +199 -24
  132. package/ccw/src/templates/dashboard-js/views/codexlens-manager.js +1265 -27
  133. package/ccw/src/templates/dashboard.html +840 -831
  134. package/ccw/src/tools/claude-cli-tools.ts +300 -0
  135. package/ccw/src/tools/cli-executor.ts +83 -14
  136. package/ccw/src/tools/codex-lens.ts +146 -9
  137. package/ccw/src/tools/context-cache-store.ts +368 -0
  138. package/ccw/src/tools/context-cache.ts +393 -0
  139. package/ccw/src/tools/core-memory.ts +33 -19
  140. package/ccw/src/tools/index.ts +2 -0
  141. package/ccw/src/tools/litellm-client.ts +246 -0
  142. package/ccw/src/tools/litellm-executor.ts +241 -0
  143. package/ccw/src/tools/pattern-parser.ts +329 -0
  144. package/ccw/src/tools/smart-search.ts +142 -41
  145. package/ccw/src/types/litellm-api-config.ts +402 -0
  146. package/ccw-litellm/README.md +180 -0
  147. package/ccw-litellm/pyproject.toml +35 -0
  148. package/ccw-litellm/src/ccw_litellm/__init__.py +47 -0
  149. package/ccw-litellm/src/ccw_litellm/__pycache__/__init__.cpython-313.pyc +0 -0
  150. package/ccw-litellm/src/ccw_litellm/__pycache__/cli.cpython-313.pyc +0 -0
  151. package/ccw-litellm/src/ccw_litellm/cli.py +108 -0
  152. package/ccw-litellm/src/ccw_litellm/clients/__init__.py +12 -0
  153. package/ccw-litellm/src/ccw_litellm/clients/__pycache__/__init__.cpython-313.pyc +0 -0
  154. package/ccw-litellm/src/ccw_litellm/clients/__pycache__/litellm_embedder.cpython-313.pyc +0 -0
  155. package/ccw-litellm/src/ccw_litellm/clients/__pycache__/litellm_llm.cpython-313.pyc +0 -0
  156. package/ccw-litellm/src/ccw_litellm/clients/litellm_embedder.py +251 -0
  157. package/ccw-litellm/src/ccw_litellm/clients/litellm_llm.py +165 -0
  158. package/ccw-litellm/src/ccw_litellm/config/__init__.py +22 -0
  159. package/ccw-litellm/src/ccw_litellm/config/__pycache__/__init__.cpython-313.pyc +0 -0
  160. package/ccw-litellm/src/ccw_litellm/config/__pycache__/loader.cpython-313.pyc +0 -0
  161. package/ccw-litellm/src/ccw_litellm/config/__pycache__/models.cpython-313.pyc +0 -0
  162. package/ccw-litellm/src/ccw_litellm/config/loader.py +316 -0
  163. package/ccw-litellm/src/ccw_litellm/config/models.py +130 -0
  164. package/ccw-litellm/src/ccw_litellm/interfaces/__init__.py +14 -0
  165. package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/__init__.cpython-313.pyc +0 -0
  166. package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/embedder.cpython-313.pyc +0 -0
  167. package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/llm.cpython-313.pyc +0 -0
  168. package/ccw-litellm/src/ccw_litellm/interfaces/embedder.py +52 -0
  169. package/ccw-litellm/src/ccw_litellm/interfaces/llm.py +45 -0
  170. package/codex-lens/src/codexlens/__pycache__/config.cpython-313.pyc +0 -0
  171. package/codex-lens/src/codexlens/cli/__pycache__/commands.cpython-313.pyc +0 -0
  172. package/codex-lens/src/codexlens/cli/__pycache__/embedding_manager.cpython-313.pyc +0 -0
  173. package/codex-lens/src/codexlens/cli/__pycache__/model_manager.cpython-313.pyc +0 -0
  174. package/codex-lens/src/codexlens/cli/__pycache__/output.cpython-313.pyc +0 -0
  175. package/codex-lens/src/codexlens/cli/commands.py +378 -23
  176. package/codex-lens/src/codexlens/cli/embedding_manager.py +660 -56
  177. package/codex-lens/src/codexlens/cli/model_manager.py +31 -18
  178. package/codex-lens/src/codexlens/cli/output.py +12 -1
  179. package/codex-lens/src/codexlens/config.py +93 -0
  180. package/codex-lens/src/codexlens/search/__pycache__/chain_search.cpython-313.pyc +0 -0
  181. package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-313.pyc +0 -0
  182. package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-313.pyc +0 -0
  183. package/codex-lens/src/codexlens/search/chain_search.py +6 -2
  184. package/codex-lens/src/codexlens/search/hybrid_search.py +44 -21
  185. package/codex-lens/src/codexlens/search/ranking.py +1 -1
  186. package/codex-lens/src/codexlens/semantic/__init__.py +42 -0
  187. package/codex-lens/src/codexlens/semantic/__pycache__/__init__.cpython-313.pyc +0 -0
  188. package/codex-lens/src/codexlens/semantic/__pycache__/base.cpython-313.pyc +0 -0
  189. package/codex-lens/src/codexlens/semantic/__pycache__/chunker.cpython-313.pyc +0 -0
  190. package/codex-lens/src/codexlens/semantic/__pycache__/embedder.cpython-313.pyc +0 -0
  191. package/codex-lens/src/codexlens/semantic/__pycache__/factory.cpython-313.pyc +0 -0
  192. package/codex-lens/src/codexlens/semantic/__pycache__/gpu_support.cpython-313.pyc +0 -0
  193. package/codex-lens/src/codexlens/semantic/__pycache__/litellm_embedder.cpython-313.pyc +0 -0
  194. package/codex-lens/src/codexlens/semantic/__pycache__/vector_store.cpython-313.pyc +0 -0
  195. package/codex-lens/src/codexlens/semantic/base.py +61 -0
  196. package/codex-lens/src/codexlens/semantic/chunker.py +43 -20
  197. package/codex-lens/src/codexlens/semantic/embedder.py +60 -13
  198. package/codex-lens/src/codexlens/semantic/factory.py +98 -0
  199. package/codex-lens/src/codexlens/semantic/gpu_support.py +225 -3
  200. package/codex-lens/src/codexlens/semantic/litellm_embedder.py +144 -0
  201. package/codex-lens/src/codexlens/semantic/rotational_embedder.py +434 -0
  202. package/codex-lens/src/codexlens/semantic/vector_store.py +33 -8
  203. package/codex-lens/src/codexlens/storage/__pycache__/path_mapper.cpython-313.pyc +0 -0
  204. package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_004_dual_fts.cpython-313.pyc +0 -0
  205. package/codex-lens/src/codexlens/storage/path_mapper.py +27 -1
  206. package/package.json +15 -5
  207. package/.codex/prompts.zip +0 -0
  208. package/ccw/package.json +0 -65
@@ -33,6 +33,15 @@ import {
33
33
  getFullConfigResponse,
34
34
  PREDEFINED_MODELS
35
35
  } from '../../tools/cli-config-manager.js';
36
+ import {
37
+ loadClaudeCliTools,
38
+ saveClaudeCliTools,
39
+ updateClaudeToolEnabled,
40
+ updateClaudeCacheSettings,
41
+ getClaudeCliToolsInfo,
42
+ addClaudeCustomEndpoint,
43
+ removeClaudeCustomEndpoint
44
+ } from '../../tools/claude-cli-tools.js';
36
45
 
37
46
  export interface RouteContext {
38
47
  pathname: string;
@@ -204,6 +213,93 @@ export async function handleCliRoutes(ctx: RouteContext): Promise<boolean> {
204
213
  }
205
214
  }
206
215
 
216
+ // API: Get all custom endpoints
217
+ if (pathname === '/api/cli/endpoints' && req.method === 'GET') {
218
+ try {
219
+ const config = loadClaudeCliTools(initialPath);
220
+ res.writeHead(200, { 'Content-Type': 'application/json' });
221
+ res.end(JSON.stringify({ endpoints: config.customEndpoints || [] }));
222
+ } catch (err) {
223
+ res.writeHead(500, { 'Content-Type': 'application/json' });
224
+ res.end(JSON.stringify({ error: (err as Error).message }));
225
+ }
226
+ return true;
227
+ }
228
+
229
+ // API: Add/Update custom endpoint
230
+ if (pathname === '/api/cli/endpoints' && req.method === 'POST') {
231
+ handlePostRequest(req, res, async (body: unknown) => {
232
+ try {
233
+ const { id, name, enabled } = body as { id: string; name: string; enabled: boolean };
234
+ if (!id || !name) {
235
+ return { error: 'id and name are required', status: 400 };
236
+ }
237
+ const config = addClaudeCustomEndpoint(initialPath, { id, name, enabled: enabled !== false });
238
+
239
+ broadcastToClients({
240
+ type: 'CLI_ENDPOINT_UPDATED',
241
+ payload: { endpoint: { id, name, enabled }, timestamp: new Date().toISOString() }
242
+ });
243
+
244
+ return { success: true, endpoints: config.customEndpoints };
245
+ } catch (err) {
246
+ return { error: (err as Error).message, status: 500 };
247
+ }
248
+ });
249
+ return true;
250
+ }
251
+
252
+ // API: Update custom endpoint enabled status
253
+ if (pathname.match(/^\/api\/cli\/endpoints\/[^/]+$/) && req.method === 'PUT') {
254
+ const endpointId = pathname.split('/').pop() || '';
255
+ handlePostRequest(req, res, async (body: unknown) => {
256
+ try {
257
+ const { enabled, name } = body as { enabled?: boolean; name?: string };
258
+ const config = loadClaudeCliTools(initialPath);
259
+ const endpoint = config.customEndpoints.find(e => e.id === endpointId);
260
+
261
+ if (!endpoint) {
262
+ return { error: 'Endpoint not found', status: 404 };
263
+ }
264
+
265
+ if (typeof enabled === 'boolean') endpoint.enabled = enabled;
266
+ if (name) endpoint.name = name;
267
+
268
+ saveClaudeCliTools(initialPath, config);
269
+
270
+ broadcastToClients({
271
+ type: 'CLI_ENDPOINT_UPDATED',
272
+ payload: { endpoint, timestamp: new Date().toISOString() }
273
+ });
274
+
275
+ return { success: true, endpoint };
276
+ } catch (err) {
277
+ return { error: (err as Error).message, status: 500 };
278
+ }
279
+ });
280
+ return true;
281
+ }
282
+
283
+ // API: Delete custom endpoint
284
+ if (pathname.match(/^\/api\/cli\/endpoints\/[^/]+$/) && req.method === 'DELETE') {
285
+ const endpointId = pathname.split('/').pop() || '';
286
+ try {
287
+ const config = removeClaudeCustomEndpoint(initialPath, endpointId);
288
+
289
+ broadcastToClients({
290
+ type: 'CLI_ENDPOINT_DELETED',
291
+ payload: { endpointId, timestamp: new Date().toISOString() }
292
+ });
293
+
294
+ res.writeHead(200, { 'Content-Type': 'application/json' });
295
+ res.end(JSON.stringify({ success: true, endpoints: config.customEndpoints }));
296
+ } catch (err) {
297
+ res.writeHead(500, { 'Content-Type': 'application/json' });
298
+ res.end(JSON.stringify({ error: (err as Error).message }));
299
+ }
300
+ return true;
301
+ }
302
+
207
303
  // API: CLI Execution History
208
304
  if (pathname === '/api/cli/history') {
209
305
  const projectPath = url.searchParams.get('path') || initialPath;
@@ -558,5 +654,101 @@ export async function handleCliRoutes(ctx: RouteContext): Promise<boolean> {
558
654
  return true;
559
655
  }
560
656
 
657
+ // API: Get CLI Tools Config from .claude/cli-tools.json (with fallback to global)
658
+ if (pathname === '/api/cli/tools-config' && req.method === 'GET') {
659
+ try {
660
+ const config = loadClaudeCliTools(initialPath);
661
+ const info = getClaudeCliToolsInfo(initialPath);
662
+ res.writeHead(200, { 'Content-Type': 'application/json' });
663
+ res.end(JSON.stringify({
664
+ ...config,
665
+ _configInfo: info
666
+ }));
667
+ } catch (err) {
668
+ res.writeHead(500, { 'Content-Type': 'application/json' });
669
+ res.end(JSON.stringify({ error: (err as Error).message }));
670
+ }
671
+ return true;
672
+ }
673
+
674
+ // API: Update CLI Tools Config
675
+ if (pathname === '/api/cli/tools-config' && req.method === 'PUT') {
676
+ handlePostRequest(req, res, async (body: unknown) => {
677
+ try {
678
+ const updates = body as Partial<any>;
679
+ const config = loadClaudeCliTools(initialPath);
680
+
681
+ // Merge updates
682
+ const updatedConfig = {
683
+ ...config,
684
+ ...updates,
685
+ tools: { ...config.tools, ...(updates.tools || {}) },
686
+ settings: {
687
+ ...config.settings,
688
+ ...(updates.settings || {}),
689
+ cache: {
690
+ ...config.settings.cache,
691
+ ...(updates.settings?.cache || {})
692
+ }
693
+ }
694
+ };
695
+
696
+ saveClaudeCliTools(initialPath, updatedConfig);
697
+
698
+ broadcastToClients({
699
+ type: 'CLI_TOOLS_CONFIG_UPDATED',
700
+ payload: { config: updatedConfig, timestamp: new Date().toISOString() }
701
+ });
702
+
703
+ return { success: true, config: updatedConfig };
704
+ } catch (err) {
705
+ return { error: (err as Error).message, status: 500 };
706
+ }
707
+ });
708
+ return true;
709
+ }
710
+
711
+ // API: Update specific tool enabled status
712
+ const toolsConfigMatch = pathname.match(/^\/api\/cli\/tools-config\/([a-zA-Z0-9_-]+)$/);
713
+ if (toolsConfigMatch && req.method === 'PUT') {
714
+ const toolName = toolsConfigMatch[1];
715
+ handlePostRequest(req, res, async (body: unknown) => {
716
+ try {
717
+ const { enabled } = body as { enabled: boolean };
718
+ const config = updateClaudeToolEnabled(initialPath, toolName, enabled);
719
+
720
+ broadcastToClients({
721
+ type: 'CLI_TOOL_TOGGLED',
722
+ payload: { tool: toolName, enabled, timestamp: new Date().toISOString() }
723
+ });
724
+
725
+ return { success: true, config };
726
+ } catch (err) {
727
+ return { error: (err as Error).message, status: 500 };
728
+ }
729
+ });
730
+ return true;
731
+ }
732
+
733
+ // API: Update cache settings
734
+ if (pathname === '/api/cli/tools-config/cache' && req.method === 'PUT') {
735
+ handlePostRequest(req, res, async (body: unknown) => {
736
+ try {
737
+ const cacheSettings = body as { injectionMode?: string; defaultPrefix?: string; defaultSuffix?: string };
738
+ const config = updateClaudeCacheSettings(initialPath, cacheSettings as any);
739
+
740
+ broadcastToClients({
741
+ type: 'CLI_CACHE_SETTINGS_UPDATED',
742
+ payload: { cache: config.settings.cache, timestamp: new Date().toISOString() }
743
+ });
744
+
745
+ return { success: true, config };
746
+ } catch (err) {
747
+ return { error: (err as Error).message, status: 500 };
748
+ }
749
+ });
750
+ return true;
751
+ }
752
+
561
753
  return false;
562
754
  }
@@ -9,6 +9,7 @@ import {
9
9
  bootstrapVenv,
10
10
  executeCodexLens,
11
11
  checkSemanticStatus,
12
+ ensureLiteLLMEmbedderReady,
12
13
  installSemantic,
13
14
  detectGpuSupport,
14
15
  uninstallCodexLens,
@@ -80,10 +81,22 @@ export async function handleCodexLensRoutes(ctx: RouteContext): Promise<boolean>
80
81
  // API: CodexLens Index List - Get all indexed projects with details
81
82
  if (pathname === '/api/codexlens/indexes') {
82
83
  try {
83
- // Get config for index directory path
84
- const configResult = await executeCodexLens(['config', '--json']);
84
+ // Check if CodexLens is installed first (without auto-installing)
85
+ const venvStatus = await checkVenvStatus();
86
+ if (!venvStatus.ready) {
87
+ res.writeHead(200, { 'Content-Type': 'application/json' });
88
+ res.end(JSON.stringify({ success: true, indexes: [], totalSize: 0, totalSizeFormatted: '0 B' }));
89
+ return true;
90
+ }
91
+
92
+ // Execute all CLI commands in parallel
93
+ const [configResult, projectsResult, statusResult] = await Promise.all([
94
+ executeCodexLens(['config', '--json']),
95
+ executeCodexLens(['projects', 'list', '--json']),
96
+ executeCodexLens(['status', '--json'])
97
+ ]);
98
+
85
99
  let indexDir = '';
86
-
87
100
  if (configResult.success) {
88
101
  try {
89
102
  const config = extractJSON(configResult.output);
@@ -96,8 +109,6 @@ export async function handleCodexLensRoutes(ctx: RouteContext): Promise<boolean>
96
109
  }
97
110
  }
98
111
 
99
- // Get project list using 'projects list' command
100
- const projectsResult = await executeCodexLens(['projects', 'list', '--json']);
101
112
  let indexes: any[] = [];
102
113
  let totalSize = 0;
103
114
  let vectorIndexCount = 0;
@@ -107,7 +118,8 @@ export async function handleCodexLensRoutes(ctx: RouteContext): Promise<boolean>
107
118
  try {
108
119
  const projectsData = extractJSON(projectsResult.output);
109
120
  if (projectsData.success && Array.isArray(projectsData.result)) {
110
- const { statSync, existsSync } = await import('fs');
121
+ const { stat, readdir } = await import('fs/promises');
122
+ const { existsSync } = await import('fs');
111
123
  const { basename, join } = await import('path');
112
124
 
113
125
  for (const project of projectsData.result) {
@@ -128,15 +140,14 @@ export async function handleCodexLensRoutes(ctx: RouteContext): Promise<boolean>
128
140
  // Try to get actual index size from index_root
129
141
  if (project.index_root && existsSync(project.index_root)) {
130
142
  try {
131
- const { readdirSync } = await import('fs');
132
- const files = readdirSync(project.index_root);
143
+ const files = await readdir(project.index_root);
133
144
  for (const file of files) {
134
145
  try {
135
146
  const filePath = join(project.index_root, file);
136
- const stat = statSync(filePath);
137
- projectSize += stat.size;
138
- if (!lastModified || stat.mtime > lastModified) {
139
- lastModified = stat.mtime;
147
+ const fileStat = await stat(filePath);
148
+ projectSize += fileStat.size;
149
+ if (!lastModified || fileStat.mtime > lastModified) {
150
+ lastModified = fileStat.mtime;
140
151
  }
141
152
  // Check for vector/embedding files
142
153
  if (file.includes('vector') || file.includes('embedding') ||
@@ -186,8 +197,7 @@ export async function handleCodexLensRoutes(ctx: RouteContext): Promise<boolean>
186
197
  }
187
198
  }
188
199
 
189
- // Also get summary stats from status command
190
- const statusResult = await executeCodexLens(['status', '--json']);
200
+ // Parse summary stats from status command (already fetched in parallel)
191
201
  let statusSummary: any = {};
192
202
 
193
203
  if (statusResult.success) {
@@ -242,6 +252,71 @@ export async function handleCodexLensRoutes(ctx: RouteContext): Promise<boolean>
242
252
  return true;
243
253
  }
244
254
 
255
+ // API: CodexLens Dashboard Init - Aggregated endpoint for page initialization
256
+ if (pathname === '/api/codexlens/dashboard-init') {
257
+ try {
258
+ const venvStatus = await checkVenvStatus();
259
+
260
+ if (!venvStatus.ready) {
261
+ res.writeHead(200, { 'Content-Type': 'application/json' });
262
+ res.end(JSON.stringify({
263
+ installed: false,
264
+ status: venvStatus,
265
+ config: { index_dir: '~/.codexlens/indexes', index_count: 0 },
266
+ semantic: { available: false }
267
+ }));
268
+ return true;
269
+ }
270
+
271
+ // Parallel fetch all initialization data
272
+ const [configResult, statusResult, semanticStatus] = await Promise.all([
273
+ executeCodexLens(['config', '--json']),
274
+ executeCodexLens(['status', '--json']),
275
+ checkSemanticStatus()
276
+ ]);
277
+
278
+ // Parse config
279
+ let config = { index_dir: '~/.codexlens/indexes', index_count: 0 };
280
+ if (configResult.success) {
281
+ try {
282
+ const configData = extractJSON(configResult.output);
283
+ if (configData.success && configData.result) {
284
+ config.index_dir = configData.result.index_dir || configData.result.index_root || config.index_dir;
285
+ }
286
+ } catch (e) {
287
+ console.error('[CodexLens] Failed to parse config for dashboard init:', e.message);
288
+ }
289
+ }
290
+
291
+ // Parse status
292
+ let statusData: any = {};
293
+ if (statusResult.success) {
294
+ try {
295
+ const status = extractJSON(statusResult.output);
296
+ if (status.success && status.result) {
297
+ config.index_count = status.result.projects_count || 0;
298
+ statusData = status.result;
299
+ }
300
+ } catch (e) {
301
+ console.error('[CodexLens] Failed to parse status for dashboard init:', e.message);
302
+ }
303
+ }
304
+
305
+ res.writeHead(200, { 'Content-Type': 'application/json' });
306
+ res.end(JSON.stringify({
307
+ installed: true,
308
+ status: venvStatus,
309
+ config,
310
+ semantic: semanticStatus,
311
+ statusData
312
+ }));
313
+ } catch (err) {
314
+ res.writeHead(500, { 'Content-Type': 'application/json' });
315
+ res.end(JSON.stringify({ success: false, error: err.message }));
316
+ }
317
+ return true;
318
+ }
319
+
245
320
  // API: CodexLens Bootstrap (Install)
246
321
  if (pathname === '/api/codexlens/bootstrap' && req.method === 'POST') {
247
322
  handlePostRequest(req, res, async () => {
@@ -290,14 +365,24 @@ export async function handleCodexLensRoutes(ctx: RouteContext): Promise<boolean>
290
365
  // API: CodexLens Config - GET (Get current configuration with index count)
291
366
  if (pathname === '/api/codexlens/config' && req.method === 'GET') {
292
367
  try {
368
+ // Check if CodexLens is installed first (without auto-installing)
369
+ const venvStatus = await checkVenvStatus();
370
+
371
+ let responseData = { index_dir: '~/.codexlens/indexes', index_count: 0 };
372
+
373
+ // If not installed, return default config without executing CodexLens
374
+ if (!venvStatus.ready) {
375
+ res.writeHead(200, { 'Content-Type': 'application/json' });
376
+ res.end(JSON.stringify(responseData));
377
+ return true;
378
+ }
379
+
293
380
  // Fetch both config and status to merge index_count
294
381
  const [configResult, statusResult] = await Promise.all([
295
382
  executeCodexLens(['config', '--json']),
296
383
  executeCodexLens(['status', '--json'])
297
384
  ]);
298
385
 
299
- let responseData = { index_dir: '~/.codexlens/indexes', index_count: 0 };
300
-
301
386
  // Parse config (extract JSON from output that may contain log messages)
302
387
  if (configResult.success) {
303
388
  try {
@@ -388,9 +473,17 @@ export async function handleCodexLensRoutes(ctx: RouteContext): Promise<boolean>
388
473
  // API: CodexLens Init (Initialize workspace index)
389
474
  if (pathname === '/api/codexlens/init' && req.method === 'POST') {
390
475
  handlePostRequest(req, res, async (body) => {
391
- const { path: projectPath, indexType = 'vector', embeddingModel = 'code' } = body;
476
+ const { path: projectPath, indexType = 'vector', embeddingModel = 'code', embeddingBackend = 'fastembed', maxWorkers = 1 } = body;
392
477
  const targetPath = projectPath || initialPath;
393
478
 
479
+ // Ensure LiteLLM backend dependencies are installed before running the CLI
480
+ if (indexType !== 'normal' && embeddingBackend === 'litellm') {
481
+ const installResult = await ensureLiteLLMEmbedderReady();
482
+ if (!installResult.success) {
483
+ return { success: false, error: installResult.error || 'Failed to prepare LiteLLM embedder', status: 500 };
484
+ }
485
+ }
486
+
394
487
  // Build CLI arguments based on index type
395
488
  const args = ['init', targetPath, '--json'];
396
489
  if (indexType === 'normal') {
@@ -398,6 +491,14 @@ export async function handleCodexLensRoutes(ctx: RouteContext): Promise<boolean>
398
491
  } else {
399
492
  // Add embedding model selection for vector index
400
493
  args.push('--embedding-model', embeddingModel);
494
+ // Add embedding backend if not using default fastembed
495
+ if (embeddingBackend && embeddingBackend !== 'fastembed') {
496
+ args.push('--embedding-backend', embeddingBackend);
497
+ }
498
+ // Add max workers for concurrent API calls (useful for litellm backend)
499
+ if (maxWorkers && maxWorkers > 1) {
500
+ args.push('--max-workers', String(maxWorkers));
501
+ }
401
502
  }
402
503
 
403
504
  // Broadcast start event
@@ -552,6 +653,8 @@ export async function handleCodexLensRoutes(ctx: RouteContext): Promise<boolean>
552
653
  const query = url.searchParams.get('query') || '';
553
654
  const limit = parseInt(url.searchParams.get('limit') || '20', 10);
554
655
  const mode = url.searchParams.get('mode') || 'exact'; // exact, fuzzy, hybrid, vector
656
+ const maxContentLength = parseInt(url.searchParams.get('max_content_length') || '200', 10);
657
+ const extraFilesCount = parseInt(url.searchParams.get('extra_files_count') || '10', 10);
555
658
  const projectPath = url.searchParams.get('path') || initialPath;
556
659
 
557
660
  if (!query) {
@@ -561,15 +664,46 @@ export async function handleCodexLensRoutes(ctx: RouteContext): Promise<boolean>
561
664
  }
562
665
 
563
666
  try {
564
- const args = ['search', query, '--path', projectPath, '--limit', limit.toString(), '--mode', mode, '--json'];
667
+ // Request more results to support split (full content + extra files)
668
+ const totalToFetch = limit + extraFilesCount;
669
+ const args = ['search', query, '--path', projectPath, '--limit', totalToFetch.toString(), '--mode', mode, '--json'];
565
670
 
566
671
  const result = await executeCodexLens(args, { cwd: projectPath });
567
672
 
568
673
  if (result.success) {
569
674
  try {
570
675
  const parsed = extractJSON(result.output);
676
+ const allResults = parsed.result?.results || [];
677
+
678
+ // Truncate content and split results
679
+ const truncateContent = (content: string | null | undefined): string => {
680
+ if (!content) return '';
681
+ if (content.length <= maxContentLength) return content;
682
+ return content.slice(0, maxContentLength) + '...';
683
+ };
684
+
685
+ // Split results: first N with full content, rest as file paths only
686
+ const resultsWithContent = allResults.slice(0, limit).map((r: any) => ({
687
+ ...r,
688
+ content: truncateContent(r.content || r.excerpt),
689
+ excerpt: truncateContent(r.excerpt || r.content),
690
+ }));
691
+
692
+ const extraResults = allResults.slice(limit, limit + extraFilesCount);
693
+ const extraFiles = [...new Set(extraResults.map((r: any) => r.path || r.file))];
694
+
571
695
  res.writeHead(200, { 'Content-Type': 'application/json' });
572
- res.end(JSON.stringify({ success: true, ...parsed.result }));
696
+ res.end(JSON.stringify({
697
+ success: true,
698
+ results: resultsWithContent,
699
+ extra_files: extraFiles.length > 0 ? extraFiles : undefined,
700
+ metadata: {
701
+ total: allResults.length,
702
+ limit,
703
+ max_content_length: maxContentLength,
704
+ extra_files_count: extraFilesCount,
705
+ },
706
+ }));
573
707
  } catch {
574
708
  res.writeHead(200, { 'Content-Type': 'application/json' });
575
709
  res.end(JSON.stringify({ success: true, results: [], output: result.output }));
@@ -682,6 +816,87 @@ export async function handleCodexLensRoutes(ctx: RouteContext): Promise<boolean>
682
816
  return true;
683
817
  }
684
818
 
819
+ // API: List available GPU devices for selection
820
+ if (pathname === '/api/codexlens/gpu/list' && req.method === 'GET') {
821
+ try {
822
+ // Check if CodexLens is installed first (without auto-installing)
823
+ const venvStatus = await checkVenvStatus();
824
+ if (!venvStatus.ready) {
825
+ res.writeHead(200, { 'Content-Type': 'application/json' });
826
+ res.end(JSON.stringify({ success: true, devices: [], selected_device_id: null }));
827
+ return true;
828
+ }
829
+ const result = await executeCodexLens(['gpu-list', '--json']);
830
+ if (result.success) {
831
+ try {
832
+ const parsed = extractJSON(result.output);
833
+ res.writeHead(200, { 'Content-Type': 'application/json' });
834
+ res.end(JSON.stringify(parsed));
835
+ } catch {
836
+ res.writeHead(200, { 'Content-Type': 'application/json' });
837
+ res.end(JSON.stringify({ success: true, devices: [], output: result.output }));
838
+ }
839
+ } else {
840
+ res.writeHead(500, { 'Content-Type': 'application/json' });
841
+ res.end(JSON.stringify({ success: false, error: result.error }));
842
+ }
843
+ } catch (err) {
844
+ res.writeHead(500, { 'Content-Type': 'application/json' });
845
+ res.end(JSON.stringify({ success: false, error: err.message }));
846
+ }
847
+ return true;
848
+ }
849
+
850
+ // API: Select GPU device for embedding
851
+ if (pathname === '/api/codexlens/gpu/select' && req.method === 'POST') {
852
+ handlePostRequest(req, res, async (body) => {
853
+ const { device_id } = body;
854
+
855
+ if (device_id === undefined || device_id === null) {
856
+ return { success: false, error: 'device_id is required', status: 400 };
857
+ }
858
+
859
+ try {
860
+ const result = await executeCodexLens(['gpu-select', String(device_id), '--json']);
861
+ if (result.success) {
862
+ try {
863
+ const parsed = extractJSON(result.output);
864
+ return parsed;
865
+ } catch {
866
+ return { success: true, message: 'GPU selected', output: result.output };
867
+ }
868
+ } else {
869
+ return { success: false, error: result.error, status: 500 };
870
+ }
871
+ } catch (err) {
872
+ return { success: false, error: err.message, status: 500 };
873
+ }
874
+ });
875
+ return true;
876
+ }
877
+
878
+ // API: Reset GPU selection to auto-detection
879
+ if (pathname === '/api/codexlens/gpu/reset' && req.method === 'POST') {
880
+ handlePostRequest(req, res, async () => {
881
+ try {
882
+ const result = await executeCodexLens(['gpu-reset', '--json']);
883
+ if (result.success) {
884
+ try {
885
+ const parsed = extractJSON(result.output);
886
+ return parsed;
887
+ } catch {
888
+ return { success: true, message: 'GPU selection reset', output: result.output };
889
+ }
890
+ } else {
891
+ return { success: false, error: result.error, status: 500 };
892
+ }
893
+ } catch (err) {
894
+ return { success: false, error: err.message, status: 500 };
895
+ }
896
+ });
897
+ return true;
898
+ }
899
+
685
900
  // API: CodexLens Semantic Search Install (with GPU mode support)
686
901
  if (pathname === '/api/codexlens/semantic/install' && req.method === 'POST') {
687
902
  handlePostRequest(req, res, async (body) => {
@@ -721,6 +936,13 @@ export async function handleCodexLensRoutes(ctx: RouteContext): Promise<boolean>
721
936
  // API: CodexLens Model List (list available embedding models)
722
937
  if (pathname === '/api/codexlens/models' && req.method === 'GET') {
723
938
  try {
939
+ // Check if CodexLens is installed first (without auto-installing)
940
+ const venvStatus = await checkVenvStatus();
941
+ if (!venvStatus.ready) {
942
+ res.writeHead(200, { 'Content-Type': 'application/json' });
943
+ res.end(JSON.stringify({ success: false, error: 'CodexLens not installed' }));
944
+ return true;
945
+ }
724
946
  const result = await executeCodexLens(['model-list', '--json']);
725
947
  if (result.success) {
726
948
  try {