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
@@ -78,6 +78,14 @@ interface CliExecOptions {
78
78
  resume?: string | boolean; // true = last, string = execution ID, comma-separated for merge
79
79
  id?: string; // Custom execution ID (e.g., IMPL-001-step1)
80
80
  noNative?: boolean; // Force prompt concatenation instead of native resume
81
+ cache?: string | boolean; // Cache: true = auto from CONTEXT, string = comma-separated patterns/content
82
+ injectMode?: 'none' | 'full' | 'progressive'; // Inject mode for cached content
83
+ }
84
+
85
+ /** Cache configuration parsed from --cache */
86
+ interface CacheConfig {
87
+ patterns?: string[]; // @patterns to pack (items starting with @)
88
+ content?: string; // Additional text content (items not starting with @)
81
89
  }
82
90
 
83
91
  interface HistoryOptions {
@@ -91,7 +99,7 @@ interface StorageOptions {
91
99
  project?: string;
92
100
  cliHistory?: boolean;
93
101
  memory?: boolean;
94
- cache?: boolean;
102
+ storageCache?: boolean;
95
103
  config?: boolean;
96
104
  force?: boolean;
97
105
  }
@@ -173,15 +181,15 @@ async function showStorageInfo(): Promise<void> {
173
181
  * Clean storage
174
182
  */
175
183
  async function cleanStorage(options: StorageOptions): Promise<void> {
176
- const { all, project, force, cliHistory, memory, cache, config } = options;
184
+ const { all, project, force, cliHistory, memory, storageCache, config } = options;
177
185
 
178
186
  // Determine what to clean
179
187
  const cleanTypes = {
180
- cliHistory: cliHistory || (!cliHistory && !memory && !cache && !config),
181
- memory: memory || (!cliHistory && !memory && !cache && !config),
182
- cache: cache || (!cliHistory && !memory && !cache && !config),
188
+ cliHistory: cliHistory || (!cliHistory && !memory && !storageCache && !config),
189
+ memory: memory || (!cliHistory && !memory && !storageCache && !config),
190
+ cache: storageCache || (!cliHistory && !memory && !storageCache && !config),
183
191
  config: config || false, // Config requires explicit flag
184
- all: !cliHistory && !memory && !cache && !config
192
+ all: !cliHistory && !memory && !storageCache && !config
185
193
  };
186
194
 
187
195
  if (project) {
@@ -383,7 +391,7 @@ async function statusAction(): Promise<void> {
383
391
  * @param {Object} options - CLI options
384
392
  */
385
393
  async function execAction(positionalPrompt: string | undefined, options: CliExecOptions): Promise<void> {
386
- const { prompt: optionPrompt, file, tool = 'gemini', mode = 'analysis', model, cd, includeDirs, timeout, noStream, resume, id, noNative } = options;
394
+ const { prompt: optionPrompt, file, tool = 'gemini', mode = 'analysis', model, cd, includeDirs, timeout, noStream, resume, id, noNative, cache, injectMode } = options;
387
395
 
388
396
  // Priority: 1. --file, 2. --prompt/-p option, 3. positional argument
389
397
  let finalPrompt: string | undefined;
@@ -421,6 +429,128 @@ async function execAction(positionalPrompt: string | undefined, options: CliExec
421
429
 
422
430
  const prompt_to_use = finalPrompt || '';
423
431
 
432
+ // Handle cache option: pack @patterns and/or content
433
+ let cacheSessionId: string | undefined;
434
+ let actualPrompt = prompt_to_use;
435
+
436
+ if (cache) {
437
+ const { handler: contextCacheHandler } = await import('../tools/context-cache.js');
438
+
439
+ // Parse cache config from comma-separated string
440
+ // Items starting with @ are patterns, others are text content
441
+ let cacheConfig: CacheConfig = {};
442
+
443
+ if (cache === true) {
444
+ // --cache without value: auto-extract from CONTEXT field
445
+ const contextMatch = prompt_to_use.match(/CONTEXT:\s*([^\n]+)/i);
446
+ if (contextMatch) {
447
+ const contextLine = contextMatch[1];
448
+ const patternMatches = contextLine.matchAll(/@[^\s|]+/g);
449
+ cacheConfig.patterns = Array.from(patternMatches).map(m => m[0]);
450
+ }
451
+ } else if (typeof cache === 'string') {
452
+ // Parse comma-separated items: @patterns and text content
453
+ const items = cache.split(',').map(s => s.trim()).filter(Boolean);
454
+ const patterns: string[] = [];
455
+ const contentParts: string[] = [];
456
+
457
+ for (const item of items) {
458
+ if (item.startsWith('@')) {
459
+ patterns.push(item);
460
+ } else {
461
+ contentParts.push(item);
462
+ }
463
+ }
464
+
465
+ if (patterns.length > 0) {
466
+ cacheConfig.patterns = patterns;
467
+ }
468
+ if (contentParts.length > 0) {
469
+ cacheConfig.content = contentParts.join('\n');
470
+ }
471
+ }
472
+
473
+ // Also extract patterns from CONTEXT if not provided
474
+ if ((!cacheConfig.patterns || cacheConfig.patterns.length === 0) && prompt_to_use) {
475
+ const contextMatch = prompt_to_use.match(/CONTEXT:\s*([^\n]+)/i);
476
+ if (contextMatch) {
477
+ const contextLine = contextMatch[1];
478
+ const patternMatches = contextLine.matchAll(/@[^\s|]+/g);
479
+ cacheConfig.patterns = Array.from(patternMatches).map(m => m[0]);
480
+ }
481
+ }
482
+
483
+ // Pack if we have patterns or content
484
+ if ((cacheConfig.patterns && cacheConfig.patterns.length > 0) || cacheConfig.content) {
485
+ const patternCount = cacheConfig.patterns?.length || 0;
486
+ const hasContent = !!cacheConfig.content;
487
+ console.log(chalk.gray(` Caching: ${patternCount} pattern(s)${hasContent ? ' + text content' : ''}...`));
488
+
489
+ const cacheResult = await contextCacheHandler({
490
+ operation: 'pack',
491
+ patterns: cacheConfig.patterns,
492
+ content: cacheConfig.content,
493
+ cwd: cd || process.cwd(),
494
+ include_dirs: includeDirs ? includeDirs.split(',') : undefined,
495
+ });
496
+
497
+ if (cacheResult.success && cacheResult.result) {
498
+ const packResult = cacheResult.result as { session_id: string; files_packed: number; total_bytes: number };
499
+ cacheSessionId = packResult.session_id;
500
+ console.log(chalk.gray(` Cached: ${packResult.files_packed} files, ${packResult.total_bytes} bytes`));
501
+ console.log(chalk.gray(` Session: ${cacheSessionId}`));
502
+
503
+ // Determine inject mode:
504
+ // --inject-mode explicitly set > tool default (codex=full, others=none)
505
+ const effectiveInjectMode = injectMode ?? (tool === 'codex' ? 'full' : 'none');
506
+
507
+ if (effectiveInjectMode !== 'none' && cacheSessionId) {
508
+ if (effectiveInjectMode === 'full') {
509
+ // Read full cache content
510
+ const readResult = await contextCacheHandler({
511
+ operation: 'read',
512
+ session_id: cacheSessionId,
513
+ offset: 0,
514
+ limit: 1024 * 1024, // 1MB max
515
+ });
516
+
517
+ if (readResult.success && readResult.result) {
518
+ const { content: cachedContent, total_bytes } = readResult.result as { content: string; total_bytes: number };
519
+ console.log(chalk.gray(` Injecting ${total_bytes} bytes (full mode)...`));
520
+ actualPrompt = `=== CACHED CONTEXT (${packResult.files_packed} files) ===\n${cachedContent}\n\n=== USER PROMPT ===\n${prompt_to_use}`;
521
+ }
522
+ } else if (effectiveInjectMode === 'progressive') {
523
+ // Progressive mode: read first page only (64KB default)
524
+ const pageLimit = 65536;
525
+ const readResult = await contextCacheHandler({
526
+ operation: 'read',
527
+ session_id: cacheSessionId,
528
+ offset: 0,
529
+ limit: pageLimit,
530
+ });
531
+
532
+ if (readResult.success && readResult.result) {
533
+ const { content: cachedContent, total_bytes, has_more, next_offset } = readResult.result as {
534
+ content: string; total_bytes: number; has_more: boolean; next_offset: number | null
535
+ };
536
+ console.log(chalk.gray(` Injecting ${cachedContent.length}/${total_bytes} bytes (progressive mode)...`));
537
+
538
+ const moreInfo = has_more
539
+ ? `\n[... ${total_bytes - cachedContent.length} more bytes available via: context_cache(operation="read", session_id="${cacheSessionId}", offset=${next_offset}) ...]`
540
+ : '';
541
+
542
+ actualPrompt = `=== CACHED CONTEXT (${packResult.files_packed} files, progressive) ===\n${cachedContent}${moreInfo}\n\n=== USER PROMPT ===\n${prompt_to_use}`;
543
+ }
544
+ }
545
+ }
546
+
547
+ console.log();
548
+ } else {
549
+ console.log(chalk.yellow(` Cache warning: ${cacheResult.error}`));
550
+ }
551
+ }
552
+ }
553
+
424
554
  // Parse resume IDs for merge scenario
425
555
  const resumeIds = resume && typeof resume === 'string' ? resume.split(',').map(s => s.trim()).filter(Boolean) : [];
426
556
  const isMerge = resumeIds.length > 1;
@@ -462,7 +592,7 @@ async function execAction(positionalPrompt: string | undefined, options: CliExec
462
592
  try {
463
593
  const result = await cliExecutorTool.execute({
464
594
  tool,
465
- prompt: prompt_to_use,
595
+ prompt: actualPrompt,
466
596
  mode,
467
597
  model,
468
598
  cd,
@@ -725,14 +855,28 @@ export async function cliCommand(
725
855
  console.log(chalk.gray(' --model <model> Model override'));
726
856
  console.log(chalk.gray(' --cd <path> Working directory'));
727
857
  console.log(chalk.gray(' --includeDirs <dirs> Additional directories'));
728
- console.log(chalk.gray(' --timeout <ms> Timeout (default: 300000)'));
858
+ console.log(chalk.gray(' --timeout <ms> Timeout (default: 0=disabled)'));
729
859
  console.log(chalk.gray(' --resume [id] Resume previous session'));
860
+ console.log(chalk.gray(' --cache <items> Cache: comma-separated @patterns and text'));
861
+ console.log(chalk.gray(' --inject-mode <m> Inject mode: none, full, progressive'));
862
+ console.log();
863
+ console.log(' Cache format:');
864
+ console.log(chalk.gray(' --cache "@src/**/*.ts,@CLAUDE.md" # @patterns to pack'));
865
+ console.log(chalk.gray(' --cache "@src/**/*,extra context" # patterns + text content'));
866
+ console.log(chalk.gray(' --cache # auto from CONTEXT field'));
867
+ console.log();
868
+ console.log(' Inject modes:');
869
+ console.log(chalk.gray(' none: cache only, no injection (default for gemini/qwen)'));
870
+ console.log(chalk.gray(' full: inject all cached content (default for codex)'));
871
+ console.log(chalk.gray(' progressive: inject first 64KB with MCP continuation hint'));
730
872
  console.log();
731
873
  console.log(' Examples:');
732
874
  console.log(chalk.gray(' ccw cli -p "Analyze auth module" --tool gemini'));
733
875
  console.log(chalk.gray(' ccw cli -f prompt.txt --tool codex --mode write'));
734
876
  console.log(chalk.gray(' ccw cli -p "$(cat template.md)" --tool gemini'));
735
877
  console.log(chalk.gray(' ccw cli --resume --tool gemini'));
878
+ console.log(chalk.gray(' ccw cli -p "..." --cache "@src/**/*.ts" --tool codex'));
879
+ console.log(chalk.gray(' ccw cli -p "..." --cache "@src/**/*" --inject-mode progressive --tool gemini'));
736
880
  console.log();
737
881
  }
738
882
  }
@@ -6,7 +6,7 @@
6
6
  import chalk from 'chalk';
7
7
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
8
8
  import { join, dirname } from 'path';
9
- import { tmpdir } from 'os';
9
+ import { homedir } from 'os';
10
10
 
11
11
  interface HookOptions {
12
12
  stdin?: boolean;
@@ -53,9 +53,10 @@ async function readStdin(): Promise<string> {
53
53
 
54
54
  /**
55
55
  * Get session state file path
56
+ * Uses ~/.claude/.ccw-sessions/ for reliable persistence across sessions
56
57
  */
57
58
  function getSessionStateFile(sessionId: string): string {
58
- const stateDir = join(tmpdir(), '.ccw-sessions');
59
+ const stateDir = join(homedir(), '.claude', '.ccw-sessions');
59
60
  if (!existsSync(stateDir)) {
60
61
  mkdirSync(stateDir, { recursive: true });
61
62
  }
@@ -0,0 +1,441 @@
1
+ /**
2
+ * LiteLLM API Config Manager
3
+ * Manages provider credentials, endpoint configurations, and model discovery
4
+ */
5
+
6
+ import { join } from 'path';
7
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
8
+ import { homedir } from 'os';
9
+
10
+ // ===========================
11
+ // Type Definitions
12
+ // ===========================
13
+
14
+ export type ProviderType =
15
+ | 'openai'
16
+ | 'anthropic'
17
+ | 'google'
18
+ | 'cohere'
19
+ | 'azure'
20
+ | 'bedrock'
21
+ | 'vertexai'
22
+ | 'huggingface'
23
+ | 'ollama'
24
+ | 'custom';
25
+
26
+ export interface ProviderCredential {
27
+ id: string;
28
+ name: string;
29
+ type: ProviderType;
30
+ apiKey?: string;
31
+ baseUrl?: string;
32
+ apiVersion?: string;
33
+ region?: string;
34
+ projectId?: string;
35
+ organizationId?: string;
36
+ enabled: boolean;
37
+ metadata?: Record<string, any>;
38
+ createdAt: string;
39
+ updatedAt: string;
40
+ }
41
+
42
+ export interface EndpointConfig {
43
+ id: string;
44
+ name: string;
45
+ providerId: string;
46
+ model: string;
47
+ alias?: string;
48
+ temperature?: number;
49
+ maxTokens?: number;
50
+ topP?: number;
51
+ enabled: boolean;
52
+ metadata?: Record<string, any>;
53
+ createdAt: string;
54
+ updatedAt: string;
55
+ }
56
+
57
+ export interface ModelInfo {
58
+ id: string;
59
+ name: string;
60
+ provider: ProviderType;
61
+ contextWindow: number;
62
+ supportsFunctions: boolean;
63
+ supportsStreaming: boolean;
64
+ inputCostPer1k?: number;
65
+ outputCostPer1k?: number;
66
+ }
67
+
68
+ export interface LiteLLMApiConfig {
69
+ version: string;
70
+ providers: ProviderCredential[];
71
+ endpoints: EndpointConfig[];
72
+ }
73
+
74
+ // ===========================
75
+ // Model Definitions
76
+ // ===========================
77
+
78
+ export const PROVIDER_MODELS: Record<ProviderType, ModelInfo[]> = {
79
+ openai: [
80
+ {
81
+ id: 'gpt-4-turbo',
82
+ name: 'GPT-4 Turbo',
83
+ provider: 'openai',
84
+ contextWindow: 128000,
85
+ supportsFunctions: true,
86
+ supportsStreaming: true,
87
+ inputCostPer1k: 0.01,
88
+ outputCostPer1k: 0.03,
89
+ },
90
+ {
91
+ id: 'gpt-4',
92
+ name: 'GPT-4',
93
+ provider: 'openai',
94
+ contextWindow: 8192,
95
+ supportsFunctions: true,
96
+ supportsStreaming: true,
97
+ inputCostPer1k: 0.03,
98
+ outputCostPer1k: 0.06,
99
+ },
100
+ {
101
+ id: 'gpt-3.5-turbo',
102
+ name: 'GPT-3.5 Turbo',
103
+ provider: 'openai',
104
+ contextWindow: 16385,
105
+ supportsFunctions: true,
106
+ supportsStreaming: true,
107
+ inputCostPer1k: 0.0005,
108
+ outputCostPer1k: 0.0015,
109
+ },
110
+ ],
111
+ anthropic: [
112
+ {
113
+ id: 'claude-3-opus-20240229',
114
+ name: 'Claude 3 Opus',
115
+ provider: 'anthropic',
116
+ contextWindow: 200000,
117
+ supportsFunctions: true,
118
+ supportsStreaming: true,
119
+ inputCostPer1k: 0.015,
120
+ outputCostPer1k: 0.075,
121
+ },
122
+ {
123
+ id: 'claude-3-sonnet-20240229',
124
+ name: 'Claude 3 Sonnet',
125
+ provider: 'anthropic',
126
+ contextWindow: 200000,
127
+ supportsFunctions: true,
128
+ supportsStreaming: true,
129
+ inputCostPer1k: 0.003,
130
+ outputCostPer1k: 0.015,
131
+ },
132
+ {
133
+ id: 'claude-3-haiku-20240307',
134
+ name: 'Claude 3 Haiku',
135
+ provider: 'anthropic',
136
+ contextWindow: 200000,
137
+ supportsFunctions: true,
138
+ supportsStreaming: true,
139
+ inputCostPer1k: 0.00025,
140
+ outputCostPer1k: 0.00125,
141
+ },
142
+ ],
143
+ google: [
144
+ {
145
+ id: 'gemini-pro',
146
+ name: 'Gemini Pro',
147
+ provider: 'google',
148
+ contextWindow: 32768,
149
+ supportsFunctions: true,
150
+ supportsStreaming: true,
151
+ },
152
+ {
153
+ id: 'gemini-pro-vision',
154
+ name: 'Gemini Pro Vision',
155
+ provider: 'google',
156
+ contextWindow: 16384,
157
+ supportsFunctions: false,
158
+ supportsStreaming: true,
159
+ },
160
+ ],
161
+ cohere: [
162
+ {
163
+ id: 'command',
164
+ name: 'Command',
165
+ provider: 'cohere',
166
+ contextWindow: 4096,
167
+ supportsFunctions: false,
168
+ supportsStreaming: true,
169
+ },
170
+ {
171
+ id: 'command-light',
172
+ name: 'Command Light',
173
+ provider: 'cohere',
174
+ contextWindow: 4096,
175
+ supportsFunctions: false,
176
+ supportsStreaming: true,
177
+ },
178
+ ],
179
+ azure: [],
180
+ bedrock: [],
181
+ vertexai: [],
182
+ huggingface: [],
183
+ ollama: [],
184
+ custom: [],
185
+ };
186
+
187
+ // ===========================
188
+ // Config File Management
189
+ // ===========================
190
+
191
+ const CONFIG_DIR = join(homedir(), '.claude', 'litellm');
192
+ const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
193
+
194
+ function ensureConfigDir(): void {
195
+ if (!existsSync(CONFIG_DIR)) {
196
+ mkdirSync(CONFIG_DIR, { recursive: true });
197
+ }
198
+ }
199
+
200
+ function loadConfig(): LiteLLMApiConfig {
201
+ ensureConfigDir();
202
+
203
+ if (!existsSync(CONFIG_FILE)) {
204
+ const defaultConfig: LiteLLMApiConfig = {
205
+ version: '1.0.0',
206
+ providers: [],
207
+ endpoints: [],
208
+ };
209
+ saveConfig(defaultConfig);
210
+ return defaultConfig;
211
+ }
212
+
213
+ try {
214
+ const content = readFileSync(CONFIG_FILE, 'utf-8');
215
+ return JSON.parse(content);
216
+ } catch (err) {
217
+ throw new Error(`Failed to load config: ${(err as Error).message}`);
218
+ }
219
+ }
220
+
221
+ function saveConfig(config: LiteLLMApiConfig): void {
222
+ ensureConfigDir();
223
+
224
+ try {
225
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8');
226
+ } catch (err) {
227
+ throw new Error(`Failed to save config: ${(err as Error).message}`);
228
+ }
229
+ }
230
+
231
+ // ===========================
232
+ // Provider Management
233
+ // ===========================
234
+
235
+ export function getAllProviders(): ProviderCredential[] {
236
+ const config = loadConfig();
237
+ return config.providers;
238
+ }
239
+
240
+ export function getProvider(id: string): ProviderCredential | null {
241
+ const config = loadConfig();
242
+ return config.providers.find((p) => p.id === id) || null;
243
+ }
244
+
245
+ export function createProvider(
246
+ data: Omit<ProviderCredential, 'id' | 'createdAt' | 'updatedAt'>
247
+ ): ProviderCredential {
248
+ const config = loadConfig();
249
+
250
+ const now = new Date().toISOString();
251
+ const provider: ProviderCredential = {
252
+ ...data,
253
+ id: `provider-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
254
+ createdAt: now,
255
+ updatedAt: now,
256
+ };
257
+
258
+ config.providers.push(provider);
259
+ saveConfig(config);
260
+
261
+ return provider;
262
+ }
263
+
264
+ export function updateProvider(
265
+ id: string,
266
+ updates: Partial<ProviderCredential>
267
+ ): ProviderCredential | null {
268
+ const config = loadConfig();
269
+
270
+ const index = config.providers.findIndex((p) => p.id === id);
271
+ if (index === -1) {
272
+ return null;
273
+ }
274
+
275
+ const updated: ProviderCredential = {
276
+ ...config.providers[index],
277
+ ...updates,
278
+ id,
279
+ updatedAt: new Date().toISOString(),
280
+ };
281
+
282
+ config.providers[index] = updated;
283
+ saveConfig(config);
284
+
285
+ return updated;
286
+ }
287
+
288
+ export function deleteProvider(id: string): { success: boolean } {
289
+ const config = loadConfig();
290
+
291
+ const index = config.providers.findIndex((p) => p.id === id);
292
+ if (index === -1) {
293
+ return { success: false };
294
+ }
295
+
296
+ config.providers.splice(index, 1);
297
+
298
+ // Also delete endpoints using this provider
299
+ config.endpoints = config.endpoints.filter((e) => e.providerId !== id);
300
+
301
+ saveConfig(config);
302
+
303
+ return { success: true };
304
+ }
305
+
306
+ export async function testProviderConnection(
307
+ providerId: string
308
+ ): Promise<{ success: boolean; error?: string }> {
309
+ const provider = getProvider(providerId);
310
+
311
+ if (!provider) {
312
+ return { success: false, error: 'Provider not found' };
313
+ }
314
+
315
+ if (!provider.enabled) {
316
+ return { success: false, error: 'Provider is disabled' };
317
+ }
318
+
319
+ // Basic validation
320
+ if (!provider.apiKey && provider.type !== 'ollama' && provider.type !== 'custom') {
321
+ return { success: false, error: 'API key is required for this provider type' };
322
+ }
323
+
324
+ // TODO: Implement actual provider connection testing using litellm-client
325
+ // For now, just validate the configuration
326
+ return { success: true };
327
+ }
328
+
329
+ // ===========================
330
+ // Endpoint Management
331
+ // ===========================
332
+
333
+ export function getAllEndpoints(): EndpointConfig[] {
334
+ const config = loadConfig();
335
+ return config.endpoints;
336
+ }
337
+
338
+ export function getEndpoint(id: string): EndpointConfig | null {
339
+ const config = loadConfig();
340
+ return config.endpoints.find((e) => e.id === id) || null;
341
+ }
342
+
343
+ export function createEndpoint(
344
+ data: Omit<EndpointConfig, 'id' | 'createdAt' | 'updatedAt'>
345
+ ): EndpointConfig {
346
+ const config = loadConfig();
347
+
348
+ // Validate provider exists
349
+ const provider = config.providers.find((p) => p.id === data.providerId);
350
+ if (!provider) {
351
+ throw new Error('Provider not found');
352
+ }
353
+
354
+ const now = new Date().toISOString();
355
+ const endpoint: EndpointConfig = {
356
+ ...data,
357
+ id: `endpoint-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
358
+ createdAt: now,
359
+ updatedAt: now,
360
+ };
361
+
362
+ config.endpoints.push(endpoint);
363
+ saveConfig(config);
364
+
365
+ return endpoint;
366
+ }
367
+
368
+ export function updateEndpoint(
369
+ id: string,
370
+ updates: Partial<EndpointConfig>
371
+ ): EndpointConfig | null {
372
+ const config = loadConfig();
373
+
374
+ const index = config.endpoints.findIndex((e) => e.id === id);
375
+ if (index === -1) {
376
+ return null;
377
+ }
378
+
379
+ // Validate provider if being updated
380
+ if (updates.providerId) {
381
+ const provider = config.providers.find((p) => p.id === updates.providerId);
382
+ if (!provider) {
383
+ throw new Error('Provider not found');
384
+ }
385
+ }
386
+
387
+ const updated: EndpointConfig = {
388
+ ...config.endpoints[index],
389
+ ...updates,
390
+ id,
391
+ updatedAt: new Date().toISOString(),
392
+ };
393
+
394
+ config.endpoints[index] = updated;
395
+ saveConfig(config);
396
+
397
+ return updated;
398
+ }
399
+
400
+ export function deleteEndpoint(id: string): { success: boolean } {
401
+ const config = loadConfig();
402
+
403
+ const index = config.endpoints.findIndex((e) => e.id === id);
404
+ if (index === -1) {
405
+ return { success: false };
406
+ }
407
+
408
+ config.endpoints.splice(index, 1);
409
+ saveConfig(config);
410
+
411
+ return { success: true };
412
+ }
413
+
414
+ // ===========================
415
+ // Model Discovery
416
+ // ===========================
417
+
418
+ export function getModelsForProviderType(providerType: ProviderType): ModelInfo[] | null {
419
+ return PROVIDER_MODELS[providerType] || null;
420
+ }
421
+
422
+ export function getAllModels(): Record<ProviderType, ModelInfo[]> {
423
+ return PROVIDER_MODELS;
424
+ }
425
+
426
+ // ===========================
427
+ // Config Access
428
+ // ===========================
429
+
430
+ export function getFullConfig(): LiteLLMApiConfig {
431
+ return loadConfig();
432
+ }
433
+
434
+ export function resetConfig(): void {
435
+ const defaultConfig: LiteLLMApiConfig = {
436
+ version: '1.0.0',
437
+ providers: [],
438
+ endpoints: [],
439
+ };
440
+ saveConfig(defaultConfig);
441
+ }