claude-code-workflow 6.3.24 → 6.3.26

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 (75) hide show
  1. package/.claude/commands/issue/discover-by-prompt.md +764 -0
  2. package/.claude/skills/text-formatter/SKILL.md +196 -0
  3. package/.claude/skills/text-formatter/phases/01-input-collection.md +111 -0
  4. package/.claude/skills/text-formatter/phases/02-content-analysis.md +248 -0
  5. package/.claude/skills/text-formatter/phases/03-format-transform.md +245 -0
  6. package/.claude/skills/text-formatter/phases/04-output-preview.md +183 -0
  7. package/.claude/skills/text-formatter/specs/callout-types.md +293 -0
  8. package/.claude/skills/text-formatter/specs/element-mapping.md +226 -0
  9. package/.claude/skills/text-formatter/specs/format-rules.md +273 -0
  10. package/.claude/skills/text-formatter/templates/bbcode-template.md +350 -0
  11. package/ccw/dist/core/routes/help-routes.d.ts.map +1 -1
  12. package/ccw/dist/core/routes/help-routes.js +43 -7
  13. package/ccw/dist/core/routes/help-routes.js.map +1 -1
  14. package/ccw/dist/core/routes/litellm-api-routes.d.ts.map +1 -1
  15. package/ccw/dist/core/routes/litellm-api-routes.js +31 -5
  16. package/ccw/dist/core/routes/litellm-api-routes.js.map +1 -1
  17. package/ccw/dist/core/routes/memory-routes.d.ts.map +1 -1
  18. package/ccw/dist/core/routes/memory-routes.js +73 -0
  19. package/ccw/dist/core/routes/memory-routes.js.map +1 -1
  20. package/ccw/dist/core/routes/status-routes.d.ts.map +1 -1
  21. package/ccw/dist/core/routes/status-routes.js +36 -4
  22. package/ccw/dist/core/routes/status-routes.js.map +1 -1
  23. package/ccw/dist/core/server.d.ts.map +1 -1
  24. package/ccw/dist/core/server.js +58 -0
  25. package/ccw/dist/core/server.js.map +1 -1
  26. package/ccw/dist/core/services/api-key-tester.d.ts.map +1 -1
  27. package/ccw/dist/core/services/api-key-tester.js +8 -3
  28. package/ccw/dist/core/services/api-key-tester.js.map +1 -1
  29. package/ccw/dist/tools/claude-cli-tools.d.ts +7 -0
  30. package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -1
  31. package/ccw/dist/tools/claude-cli-tools.js +11 -1
  32. package/ccw/dist/tools/claude-cli-tools.js.map +1 -1
  33. package/ccw/dist/tools/cli-executor-core.d.ts +11 -0
  34. package/ccw/dist/tools/cli-executor-core.d.ts.map +1 -1
  35. package/ccw/dist/tools/cli-executor-core.js +89 -2
  36. package/ccw/dist/tools/cli-executor-core.js.map +1 -1
  37. package/ccw/dist/tools/codex-lens.d.ts +2 -1
  38. package/ccw/dist/tools/codex-lens.d.ts.map +1 -1
  39. package/ccw/dist/tools/codex-lens.js +51 -8
  40. package/ccw/dist/tools/codex-lens.js.map +1 -1
  41. package/ccw/dist/tools/index.d.ts.map +1 -1
  42. package/ccw/dist/tools/index.js +2 -0
  43. package/ccw/dist/tools/index.js.map +1 -1
  44. package/ccw/dist/tools/litellm-client.d.ts +6 -0
  45. package/ccw/dist/tools/litellm-client.d.ts.map +1 -1
  46. package/ccw/dist/tools/litellm-client.js +22 -1
  47. package/ccw/dist/tools/litellm-client.js.map +1 -1
  48. package/ccw/dist/tools/litellm-executor.js +2 -2
  49. package/ccw/dist/tools/litellm-executor.js.map +1 -1
  50. package/ccw/dist/tools/memory-update-queue.d.ts +172 -0
  51. package/ccw/dist/tools/memory-update-queue.d.ts.map +1 -0
  52. package/ccw/dist/tools/memory-update-queue.js +431 -0
  53. package/ccw/dist/tools/memory-update-queue.js.map +1 -0
  54. package/ccw/src/core/routes/help-routes.ts +46 -7
  55. package/ccw/src/core/routes/litellm-api-routes.ts +35 -4
  56. package/ccw/src/core/routes/memory-routes.ts +84 -0
  57. package/ccw/src/core/routes/status-routes.ts +39 -4
  58. package/ccw/src/core/server.ts +62 -0
  59. package/ccw/src/core/services/api-key-tester.ts +9 -3
  60. package/ccw/src/templates/dashboard-css/21-cli-toolmgmt.css +45 -0
  61. package/ccw/src/templates/dashboard-js/components/cli-status.js +36 -5
  62. package/ccw/src/templates/dashboard-js/components/hook-manager.js +42 -81
  63. package/ccw/src/templates/dashboard-js/components/mcp-manager.js +170 -28
  64. package/ccw/src/templates/dashboard-js/components/notifications.js +14 -4
  65. package/ccw/src/templates/dashboard-js/i18n.js +26 -0
  66. package/ccw/src/templates/dashboard-js/views/cli-manager.js +72 -2
  67. package/ccw/src/templates/dashboard-js/views/codexlens-manager.js +11 -1
  68. package/ccw/src/tools/claude-cli-tools.ts +17 -1
  69. package/ccw/src/tools/cli-executor-core.ts +103 -2
  70. package/ccw/src/tools/codex-lens.ts +63 -8
  71. package/ccw/src/tools/index.ts +2 -0
  72. package/ccw/src/tools/litellm-client.ts +25 -3
  73. package/ccw/src/tools/litellm-executor.ts +2 -2
  74. package/ccw/src/tools/memory-update-queue.js +499 -0
  75. package/package.json +91 -91
@@ -0,0 +1,431 @@
1
+ /**
2
+ * Memory Update Queue Tool
3
+ * Queue mechanism for batching CLAUDE.md updates
4
+ *
5
+ * Configuration:
6
+ * - Threshold: 5 paths trigger update
7
+ * - Timeout: 5 minutes auto-trigger
8
+ * - Storage: ~/.claude/.memory-queue.json
9
+ * - Deduplication: Same path only kept once
10
+ */
11
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
12
+ import { join, dirname, resolve } from 'path';
13
+ import { homedir } from 'os';
14
+ // Default configuration
15
+ const DEFAULT_THRESHOLD = 5;
16
+ const DEFAULT_TIMEOUT_SECONDS = 300; // 5 minutes
17
+ const QUEUE_FILE_PATH = join(homedir(), '.claude', '.memory-queue.json');
18
+ /**
19
+ * Get queue configuration (from file or defaults)
20
+ * @returns {{ threshold: number, timeoutMs: number }}
21
+ */
22
+ function getQueueConfig() {
23
+ try {
24
+ if (existsSync(QUEUE_FILE_PATH)) {
25
+ const content = readFileSync(QUEUE_FILE_PATH, 'utf8');
26
+ const data = JSON.parse(content);
27
+ return {
28
+ threshold: data.config?.threshold || DEFAULT_THRESHOLD,
29
+ timeoutMs: (data.config?.timeout || DEFAULT_TIMEOUT_SECONDS) * 1000
30
+ };
31
+ }
32
+ }
33
+ catch (e) {
34
+ // Use defaults
35
+ }
36
+ return {
37
+ threshold: DEFAULT_THRESHOLD,
38
+ timeoutMs: DEFAULT_TIMEOUT_SECONDS * 1000
39
+ };
40
+ }
41
+ // In-memory timeout reference (for cross-call persistence, we track via file timestamp)
42
+ let scheduledTimeoutId = null;
43
+ /**
44
+ * Ensure parent directory exists
45
+ */
46
+ function ensureDir(filePath) {
47
+ const dir = dirname(filePath);
48
+ if (!existsSync(dir)) {
49
+ mkdirSync(dir, { recursive: true });
50
+ }
51
+ }
52
+ /**
53
+ * Load queue from file
54
+ * @returns {{ items: Array<{path: string, tool: string, strategy: string, addedAt: string}>, createdAt: string | null, config?: { threshold: number, timeout: number } }}
55
+ */
56
+ function loadQueue() {
57
+ try {
58
+ if (existsSync(QUEUE_FILE_PATH)) {
59
+ const content = readFileSync(QUEUE_FILE_PATH, 'utf8');
60
+ const data = JSON.parse(content);
61
+ return {
62
+ items: Array.isArray(data.items) ? data.items : [],
63
+ createdAt: data.createdAt || null,
64
+ config: data.config || null
65
+ };
66
+ }
67
+ }
68
+ catch (e) {
69
+ console.error('[MemoryQueue] Failed to load queue:', e.message);
70
+ }
71
+ return { items: [], createdAt: null, config: null };
72
+ }
73
+ /**
74
+ * Save queue to file
75
+ * @param {{ items: Array<{path: string, tool: string, strategy: string, addedAt: string}>, createdAt: string | null }} data
76
+ */
77
+ function saveQueue(data) {
78
+ try {
79
+ ensureDir(QUEUE_FILE_PATH);
80
+ writeFileSync(QUEUE_FILE_PATH, JSON.stringify(data, null, 2), 'utf8');
81
+ }
82
+ catch (e) {
83
+ console.error('[MemoryQueue] Failed to save queue:', e.message);
84
+ throw e;
85
+ }
86
+ }
87
+ /**
88
+ * Normalize path for comparison (handle Windows/Unix differences)
89
+ * @param {string} p
90
+ * @returns {string}
91
+ */
92
+ function normalizePath(p) {
93
+ return resolve(p).replace(/\\/g, '/').toLowerCase();
94
+ }
95
+ /**
96
+ * Add path to queue with deduplication
97
+ * @param {string} path - Module path to update
98
+ * @param {{ tool?: string, strategy?: string }} options
99
+ * @returns {{ queued: boolean, queueSize: number, willFlush: boolean, message: string }}
100
+ */
101
+ function addToQueue(path, options = {}) {
102
+ const { tool = 'gemini', strategy = 'single-layer' } = options;
103
+ const queue = loadQueue();
104
+ const config = getQueueConfig();
105
+ const normalizedPath = normalizePath(path);
106
+ const now = new Date().toISOString();
107
+ // Check for duplicates
108
+ const existingIndex = queue.items.findIndex(item => normalizePath(item.path) === normalizedPath);
109
+ if (existingIndex !== -1) {
110
+ // Update existing entry timestamp but keep it deduplicated
111
+ queue.items[existingIndex].addedAt = now;
112
+ queue.items[existingIndex].tool = tool;
113
+ queue.items[existingIndex].strategy = strategy;
114
+ saveQueue(queue);
115
+ return {
116
+ queued: false,
117
+ queueSize: queue.items.length,
118
+ willFlush: queue.items.length >= config.threshold,
119
+ message: `Path already in queue (updated): ${path}`
120
+ };
121
+ }
122
+ // Add new item
123
+ queue.items.push({
124
+ path,
125
+ tool,
126
+ strategy,
127
+ addedAt: now
128
+ });
129
+ // Set createdAt if this is the first item
130
+ if (!queue.createdAt) {
131
+ queue.createdAt = now;
132
+ }
133
+ saveQueue(queue);
134
+ const willFlush = queue.items.length >= config.threshold;
135
+ // Schedule timeout if not already scheduled
136
+ scheduleTimeout();
137
+ return {
138
+ queued: true,
139
+ queueSize: queue.items.length,
140
+ willFlush,
141
+ message: willFlush
142
+ ? `Queue threshold reached (${queue.items.length}/${config.threshold}), will flush`
143
+ : `Added to queue (${queue.items.length}/${config.threshold})`
144
+ };
145
+ }
146
+ /**
147
+ * Get current queue status
148
+ * @returns {{ queueSize: number, threshold: number, items: Array, timeoutMs: number | null, createdAt: string | null }}
149
+ */
150
+ function getQueueStatus() {
151
+ const queue = loadQueue();
152
+ const config = getQueueConfig();
153
+ let timeUntilTimeout = null;
154
+ if (queue.createdAt && queue.items.length > 0) {
155
+ const createdTime = new Date(queue.createdAt).getTime();
156
+ const elapsed = Date.now() - createdTime;
157
+ timeUntilTimeout = Math.max(0, config.timeoutMs - elapsed);
158
+ }
159
+ return {
160
+ queueSize: queue.items.length,
161
+ threshold: config.threshold,
162
+ items: queue.items,
163
+ timeoutMs: config.timeoutMs,
164
+ timeoutSeconds: config.timeoutMs / 1000,
165
+ timeUntilTimeout,
166
+ createdAt: queue.createdAt
167
+ };
168
+ }
169
+ /**
170
+ * Configure queue settings
171
+ * @param {{ threshold?: number, timeout?: number }} settings
172
+ * @returns {{ success: boolean, config: { threshold: number, timeout: number } }}
173
+ */
174
+ function configureQueue(settings) {
175
+ const queue = loadQueue();
176
+ const currentConfig = getQueueConfig();
177
+ const newConfig = {
178
+ threshold: settings.threshold || currentConfig.threshold,
179
+ timeout: settings.timeout || (currentConfig.timeoutMs / 1000)
180
+ };
181
+ // Validate
182
+ if (newConfig.threshold < 1 || newConfig.threshold > 20) {
183
+ throw new Error('Threshold must be between 1 and 20');
184
+ }
185
+ if (newConfig.timeout < 60 || newConfig.timeout > 1800) {
186
+ throw new Error('Timeout must be between 60 and 1800 seconds');
187
+ }
188
+ queue.config = newConfig;
189
+ saveQueue(queue);
190
+ return {
191
+ success: true,
192
+ config: newConfig,
193
+ message: `Queue configured: threshold=${newConfig.threshold}, timeout=${newConfig.timeout}s`
194
+ };
195
+ }
196
+ /**
197
+ * Flush queue - execute batch update
198
+ * @returns {Promise<{ success: boolean, processed: number, results: Array, errors: Array }>}
199
+ */
200
+ async function flushQueue() {
201
+ const queue = loadQueue();
202
+ if (queue.items.length === 0) {
203
+ return {
204
+ success: true,
205
+ processed: 0,
206
+ results: [],
207
+ errors: [],
208
+ message: 'Queue is empty'
209
+ };
210
+ }
211
+ // Clear timeout
212
+ clearScheduledTimeout();
213
+ // Import update_module_claude dynamically to avoid circular deps
214
+ const { updateModuleClaudeTool } = await import('./update-module-claude.js');
215
+ const results = [];
216
+ const errors = [];
217
+ // Group by tool and strategy for efficiency
218
+ const groups = new Map();
219
+ for (const item of queue.items) {
220
+ const key = `${item.tool}:${item.strategy}`;
221
+ if (!groups.has(key)) {
222
+ groups.set(key, []);
223
+ }
224
+ groups.get(key).push(item);
225
+ }
226
+ // Process each group
227
+ for (const [key, items] of groups) {
228
+ const [tool, strategy] = key.split(':');
229
+ console.log(`[MemoryQueue] Processing ${items.length} items with ${tool}/${strategy}`);
230
+ for (const item of items) {
231
+ try {
232
+ const result = await updateModuleClaudeTool.execute({
233
+ path: item.path,
234
+ tool: item.tool,
235
+ strategy: item.strategy
236
+ });
237
+ results.push({
238
+ path: item.path,
239
+ success: result.success !== false,
240
+ result
241
+ });
242
+ }
243
+ catch (e) {
244
+ console.error(`[MemoryQueue] Failed to update ${item.path}:`, e.message);
245
+ errors.push({
246
+ path: item.path,
247
+ error: e.message
248
+ });
249
+ }
250
+ }
251
+ }
252
+ // Clear queue after processing
253
+ saveQueue({ items: [], createdAt: null });
254
+ return {
255
+ success: errors.length === 0,
256
+ processed: queue.items.length,
257
+ results,
258
+ errors,
259
+ message: `Processed ${results.length} items, ${errors.length} errors`
260
+ };
261
+ }
262
+ /**
263
+ * Schedule timeout for auto-flush
264
+ */
265
+ function scheduleTimeout() {
266
+ // We use file-based timeout tracking for persistence across process restarts
267
+ // The actual timeout check happens on next add/status call
268
+ const queue = loadQueue();
269
+ const config = getQueueConfig();
270
+ if (!queue.createdAt || queue.items.length === 0) {
271
+ return;
272
+ }
273
+ const createdTime = new Date(queue.createdAt).getTime();
274
+ const elapsed = Date.now() - createdTime;
275
+ if (elapsed >= config.timeoutMs) {
276
+ // Timeout already exceeded, should flush
277
+ console.log('[MemoryQueue] Timeout exceeded, auto-flushing');
278
+ // Don't await here to avoid blocking
279
+ flushQueue().catch(e => {
280
+ console.error('[MemoryQueue] Auto-flush failed:', e.message);
281
+ });
282
+ }
283
+ else if (!scheduledTimeoutId) {
284
+ // Schedule in-memory timeout for current process
285
+ const remaining = config.timeoutMs - elapsed;
286
+ scheduledTimeoutId = setTimeout(() => {
287
+ scheduledTimeoutId = null;
288
+ const currentQueue = loadQueue();
289
+ if (currentQueue.items.length > 0) {
290
+ console.log('[MemoryQueue] Timeout reached, auto-flushing');
291
+ flushQueue().catch(e => {
292
+ console.error('[MemoryQueue] Auto-flush failed:', e.message);
293
+ });
294
+ }
295
+ }, remaining);
296
+ // Prevent timeout from keeping process alive
297
+ if (scheduledTimeoutId.unref) {
298
+ scheduledTimeoutId.unref();
299
+ }
300
+ }
301
+ }
302
+ /**
303
+ * Clear scheduled timeout
304
+ */
305
+ function clearScheduledTimeout() {
306
+ if (scheduledTimeoutId) {
307
+ clearTimeout(scheduledTimeoutId);
308
+ scheduledTimeoutId = null;
309
+ }
310
+ }
311
+ /**
312
+ * Check if timeout has expired and auto-flush if needed
313
+ * @returns {Promise<{ expired: boolean, flushed: boolean, result?: object }>}
314
+ */
315
+ async function checkTimeout() {
316
+ const queue = loadQueue();
317
+ const config = getQueueConfig();
318
+ if (!queue.createdAt || queue.items.length === 0) {
319
+ return { expired: false, flushed: false };
320
+ }
321
+ const createdTime = new Date(queue.createdAt).getTime();
322
+ const elapsed = Date.now() - createdTime;
323
+ if (elapsed >= config.timeoutMs) {
324
+ console.log('[MemoryQueue] Timeout expired, triggering flush');
325
+ const result = await flushQueue();
326
+ return { expired: true, flushed: true, result };
327
+ }
328
+ return { expired: false, flushed: false };
329
+ }
330
+ /**
331
+ * Main execute function for tool interface
332
+ * @param {Record<string, unknown>} params
333
+ * @returns {Promise<unknown>}
334
+ */
335
+ async function execute(params) {
336
+ const { action, path, tool = 'gemini', strategy = 'single-layer', threshold, timeout } = params;
337
+ switch (action) {
338
+ case 'add':
339
+ if (!path) {
340
+ throw new Error('Parameter "path" is required for add action');
341
+ }
342
+ // Check timeout first
343
+ const timeoutCheck = await checkTimeout();
344
+ if (timeoutCheck.flushed) {
345
+ // Queue was flushed due to timeout, add to fresh queue
346
+ const result = addToQueue(path, { tool, strategy });
347
+ return {
348
+ ...result,
349
+ timeoutFlushed: true,
350
+ flushResult: timeoutCheck.result
351
+ };
352
+ }
353
+ const addResult = addToQueue(path, { tool, strategy });
354
+ // Auto-flush if threshold reached
355
+ if (addResult.willFlush) {
356
+ const flushResult = await flushQueue();
357
+ return {
358
+ ...addResult,
359
+ flushed: true,
360
+ flushResult
361
+ };
362
+ }
363
+ return addResult;
364
+ case 'status':
365
+ // Check timeout first
366
+ await checkTimeout();
367
+ return getQueueStatus();
368
+ case 'flush':
369
+ return await flushQueue();
370
+ case 'configure':
371
+ return configureQueue({ threshold, timeout });
372
+ default:
373
+ throw new Error(`Unknown action: ${action}. Valid actions: add, status, flush, configure`);
374
+ }
375
+ }
376
+ /**
377
+ * Tool Definition
378
+ */
379
+ export const memoryQueueTool = {
380
+ name: 'memory_queue',
381
+ description: `Memory update queue management. Batches CLAUDE.md updates for efficiency.
382
+
383
+ Actions:
384
+ - add: Add path to queue (auto-flushes at configured threshold/timeout)
385
+ - status: Get queue status and configuration
386
+ - flush: Immediately execute all queued updates
387
+ - configure: Set threshold and timeout settings`,
388
+ parameters: {
389
+ type: 'object',
390
+ properties: {
391
+ action: {
392
+ type: 'string',
393
+ enum: ['add', 'status', 'flush', 'configure'],
394
+ description: 'Queue action to perform'
395
+ },
396
+ path: {
397
+ type: 'string',
398
+ description: 'Module directory path (required for add action)'
399
+ },
400
+ threshold: {
401
+ type: 'number',
402
+ description: 'Number of paths to trigger flush (1-20, for configure action)',
403
+ minimum: 1,
404
+ maximum: 20
405
+ },
406
+ timeout: {
407
+ type: 'number',
408
+ description: 'Timeout in seconds to trigger flush (60-1800, for configure action)',
409
+ minimum: 60,
410
+ maximum: 1800
411
+ },
412
+ tool: {
413
+ type: 'string',
414
+ enum: ['gemini', 'qwen', 'codex'],
415
+ description: 'CLI tool to use (default: gemini)',
416
+ default: 'gemini'
417
+ },
418
+ strategy: {
419
+ type: 'string',
420
+ enum: ['single-layer', 'multi-layer'],
421
+ description: 'Update strategy (default: single-layer)',
422
+ default: 'single-layer'
423
+ }
424
+ },
425
+ required: ['action']
426
+ },
427
+ execute
428
+ };
429
+ // Export individual functions for direct use
430
+ export { loadQueue, saveQueue, addToQueue, getQueueStatus, flushQueue, configureQueue, scheduleTimeout, clearScheduledTimeout, checkTimeout, DEFAULT_THRESHOLD, DEFAULT_TIMEOUT_SECONDS, QUEUE_FILE_PATH };
431
+ //# sourceMappingURL=memory-update-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-update-queue.js","sourceRoot":"","sources":["../../src/tools/memory-update-queue.js"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAE7B,wBAAwB;AACxB,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAC5B,MAAM,uBAAuB,GAAG,GAAG,CAAC,CAAC,YAAY;AACjD,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,oBAAoB,CAAC,CAAC;AAEzE;;;GAGG;AACH,SAAS,cAAc;IACrB,IAAI,CAAC;QACH,IAAI,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;YAChC,MAAM,OAAO,GAAG,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;YACtD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACjC,OAAO;gBACL,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,IAAI,iBAAiB;gBACtD,SAAS,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,IAAI,uBAAuB,CAAC,GAAG,IAAI;aACpE,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,eAAe;IACjB,CAAC;IACD,OAAO;QACL,SAAS,EAAE,iBAAiB;QAC5B,SAAS,EAAE,uBAAuB,GAAG,IAAI;KAC1C,CAAC;AACJ,CAAC;AAED,wFAAwF;AACxF,IAAI,kBAAkB,GAAG,IAAI,CAAC;AAE9B;;GAEG;AACH,SAAS,SAAS,CAAC,QAAQ;IACzB,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC9B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,SAAS;IAChB,IAAI,CAAC;QACH,IAAI,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;YAChC,MAAM,OAAO,GAAG,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;YACtD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACjC,OAAO;gBACL,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;gBAClD,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;gBACjC,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,IAAI;aAC5B,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,SAAS,SAAS,CAAC,IAAI;IACrB,IAAI,CAAC;QACH,SAAS,CAAC,eAAe,CAAC,CAAC;QAC3B,aAAa,CAAC,eAAe,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACxE,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;QAChE,MAAM,CAAC,CAAC;IACV,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,aAAa,CAAC,CAAC;IACtB,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;AACtD,CAAC;AAED;;;;;GAKG;AACH,SAAS,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,EAAE;IACpC,MAAM,EAAE,IAAI,GAAG,QAAQ,EAAE,QAAQ,GAAG,cAAc,EAAE,GAAG,OAAO,CAAC;IAC/D,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;IAChC,MAAM,cAAc,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAErC,uBAAuB;IACvB,MAAM,aAAa,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,CACzC,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,cAAc,CACpD,CAAC;IAEF,IAAI,aAAa,KAAK,CAAC,CAAC,EAAE,CAAC;QACzB,2DAA2D;QAC3D,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,OAAO,GAAG,GAAG,CAAC;QACzC,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC;QACvC,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAC/C,SAAS,CAAC,KAAK,CAAC,CAAC;QAEjB,OAAO;YACL,MAAM,EAAE,KAAK;YACb,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM;YAC7B,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,SAAS;YACjD,OAAO,EAAE,oCAAoC,IAAI,EAAE;SACpD,CAAC;IACJ,CAAC;IAED,eAAe;IACf,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC;QACf,IAAI;QACJ,IAAI;QACJ,QAAQ;QACR,OAAO,EAAE,GAAG;KACb,CAAC,CAAC;IAEH,0CAA0C;IAC1C,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;QACrB,KAAK,CAAC,SAAS,GAAG,GAAG,CAAC;IACxB,CAAC;IAED,SAAS,CAAC,KAAK,CAAC,CAAC;IAEjB,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,SAAS,CAAC;IAEzD,4CAA4C;IAC5C,eAAe,EAAE,CAAC;IAElB,OAAO;QACL,MAAM,EAAE,IAAI;QACZ,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM;QAC7B,SAAS;QACT,OAAO,EAAE,SAAS;YAChB,CAAC,CAAC,4BAA4B,KAAK,CAAC,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,SAAS,eAAe;YACnF,CAAC,CAAC,mBAAmB,KAAK,CAAC,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,SAAS,GAAG;KACjE,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,cAAc;IACrB,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;IAChC,IAAI,gBAAgB,GAAG,IAAI,CAAC;IAE5B,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;QACxD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,CAAC;QACzC,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,SAAS,GAAG,OAAO,CAAC,CAAC;IAC7D,CAAC;IAED,OAAO;QACL,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM;QAC7B,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,cAAc,EAAE,MAAM,CAAC,SAAS,GAAG,IAAI;QACvC,gBAAgB;QAChB,SAAS,EAAE,KAAK,CAAC,SAAS;KAC3B,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,QAAQ;IAC9B,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,MAAM,aAAa,GAAG,cAAc,EAAE,CAAC;IAEvC,MAAM,SAAS,GAAG;QAChB,SAAS,EAAE,QAAQ,CAAC,SAAS,IAAI,aAAa,CAAC,SAAS;QACxD,OAAO,EAAE,QAAQ,CAAC,OAAO,IAAI,CAAC,aAAa,CAAC,SAAS,GAAG,IAAI,CAAC;KAC9D,CAAC;IAEF,WAAW;IACX,IAAI,SAAS,CAAC,SAAS,GAAG,CAAC,IAAI,SAAS,CAAC,SAAS,GAAG,EAAE,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACxD,CAAC;IACD,IAAI,SAAS,CAAC,OAAO,GAAG,EAAE,IAAI,SAAS,CAAC,OAAO,GAAG,IAAI,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC;IACzB,SAAS,CAAC,KAAK,CAAC,CAAC;IAEjB,OAAO;QACL,OAAO,EAAE,IAAI;QACb,MAAM,EAAE,SAAS;QACjB,OAAO,EAAE,+BAA+B,SAAS,CAAC,SAAS,aAAa,SAAS,CAAC,OAAO,GAAG;KAC7F,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,UAAU;IACvB,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAE1B,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO;YACL,OAAO,EAAE,IAAI;YACb,SAAS,EAAE,CAAC;YACZ,OAAO,EAAE,EAAE;YACX,MAAM,EAAE,EAAE;YACV,OAAO,EAAE,gBAAgB;SAC1B,CAAC;IACJ,CAAC;IAED,gBAAgB;IAChB,qBAAqB,EAAE,CAAC;IAExB,iEAAiE;IACjE,MAAM,EAAE,sBAAsB,EAAE,GAAG,MAAM,MAAM,CAAC,2BAA2B,CAAC,CAAC;IAE7E,MAAM,OAAO,GAAG,EAAE,CAAC;IACnB,MAAM,MAAM,GAAG,EAAE,CAAC;IAElB,4CAA4C;IAC5C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;IACzB,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACtB,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,qBAAqB;IACrB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;QAClC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,4BAA4B,KAAK,CAAC,MAAM,eAAe,IAAI,IAAI,QAAQ,EAAE,CAAC,CAAC;QAEvF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,sBAAsB,CAAC,OAAO,CAAC;oBAClD,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;iBACxB,CAAC,CAAC;gBAEH,OAAO,CAAC,IAAI,CAAC;oBACX,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,OAAO,EAAE,MAAM,CAAC,OAAO,KAAK,KAAK;oBACjC,MAAM;iBACP,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,kCAAkC,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;gBACzE,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,KAAK,EAAE,CAAC,CAAC,OAAO;iBACjB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,+BAA+B;IAC/B,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1C,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC;QAC5B,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM;QAC7B,OAAO;QACP,MAAM;QACN,OAAO,EAAE,aAAa,OAAO,CAAC,MAAM,WAAW,MAAM,CAAC,MAAM,SAAS;KACtE,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,eAAe;IACtB,6EAA6E;IAC7E,2DAA2D;IAC3D,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;IAEhC,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjD,OAAO;IACT,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;IACxD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,CAAC;IAEzC,IAAI,OAAO,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QAChC,yCAAyC;QACzC,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC;QAC7D,qCAAqC;QACrC,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;YACrB,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;IACL,CAAC;SAAM,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC/B,iDAAiD;QACjD,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,GAAG,OAAO,CAAC;QAC7C,kBAAkB,GAAG,UAAU,CAAC,GAAG,EAAE;YACnC,kBAAkB,GAAG,IAAI,CAAC;YAC1B,MAAM,YAAY,GAAG,SAAS,EAAE,CAAC;YACjC,IAAI,YAAY,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAClC,OAAO,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;gBAC5D,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;oBACrB,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;gBAC/D,CAAC,CAAC,CAAC;YACL,CAAC;QACH,CAAC,EAAE,SAAS,CAAC,CAAC;QAEd,6CAA6C;QAC7C,IAAI,kBAAkB,CAAC,KAAK,EAAE,CAAC;YAC7B,kBAAkB,CAAC,KAAK,EAAE,CAAC;QAC7B,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,qBAAqB;IAC5B,IAAI,kBAAkB,EAAE,CAAC;QACvB,YAAY,CAAC,kBAAkB,CAAC,CAAC;QACjC,kBAAkB,GAAG,IAAI,CAAC;IAC5B,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,YAAY;IACzB,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;IAEhC,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC5C,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;IACxD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,CAAC;IAEzC,IAAI,OAAO,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QAChC,OAAO,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC;QAC/D,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;QAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAClD,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC5C,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,OAAO,CAAC,MAAM;IAC3B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,QAAQ,EAAE,QAAQ,GAAG,cAAc,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IAEhG,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,KAAK;YACR,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;YACjE,CAAC;YACD,sBAAsB;YACtB,MAAM,YAAY,GAAG,MAAM,YAAY,EAAE,CAAC;YAC1C,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC;gBACzB,uDAAuD;gBACvD,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACpD,OAAO;oBACL,GAAG,MAAM;oBACT,cAAc,EAAE,IAAI;oBACpB,WAAW,EAAE,YAAY,CAAC,MAAM;iBACjC,CAAC;YACJ,CAAC;YAED,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;YAEvD,kCAAkC;YAClC,IAAI,SAAS,CAAC,SAAS,EAAE,CAAC;gBACxB,MAAM,WAAW,GAAG,MAAM,UAAU,EAAE,CAAC;gBACvC,OAAO;oBACL,GAAG,SAAS;oBACZ,OAAO,EAAE,IAAI;oBACb,WAAW;iBACZ,CAAC;YACJ,CAAC;YAED,OAAO,SAAS,CAAC;QAEnB,KAAK,QAAQ;YACX,sBAAsB;YACtB,MAAM,YAAY,EAAE,CAAC;YACrB,OAAO,cAAc,EAAE,CAAC;QAE1B,KAAK,OAAO;YACV,OAAO,MAAM,UAAU,EAAE,CAAC;QAE5B,KAAK,WAAW;YACd,OAAO,cAAc,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;QAEhD;YACE,MAAM,IAAI,KAAK,CAAC,mBAAmB,MAAM,gDAAgD,CAAC,CAAC;IAC/F,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,IAAI,EAAE,cAAc;IACpB,WAAW,EAAE;;;;;;gDAMiC;IAC9C,UAAU,EAAE;QACV,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,CAAC;gBAC7C,WAAW,EAAE,yBAAyB;aACvC;YACD,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,iDAAiD;aAC/D;YACD,SAAS,EAAE;gBACT,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,+DAA+D;gBAC5E,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,EAAE;aACZ;YACD,OAAO,EAAE;gBACP,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,qEAAqE;gBAClF,OAAO,EAAE,EAAE;gBACX,OAAO,EAAE,IAAI;aACd;YACD,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC;gBACjC,WAAW,EAAE,mCAAmC;gBAChD,OAAO,EAAE,QAAQ;aAClB;YACD,QAAQ,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,cAAc,EAAE,aAAa,CAAC;gBACrC,WAAW,EAAE,yCAAyC;gBACtD,OAAO,EAAE,cAAc;aACxB;SACF;QACD,QAAQ,EAAE,CAAC,QAAQ,CAAC;KACrB;IACD,OAAO;CACR,CAAC;AAEF,6CAA6C;AAC7C,OAAO,EACL,SAAS,EACT,SAAS,EACT,UAAU,EACV,cAAc,EACd,UAAU,EACV,cAAc,EACd,eAAe,EACf,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,uBAAuB,EACvB,eAAe,EAChB,CAAC"}
@@ -7,6 +7,29 @@ import { join } from 'path';
7
7
  import { homedir } from 'os';
8
8
  import type { RouteContext } from './types.js';
9
9
 
10
+ /**
11
+ * Get the ccw-help index directory path (pure function)
12
+ * Priority: project path (.claude/skills/ccw-help/index) > user path (~/.claude/skills/ccw-help/index)
13
+ * @param projectPath - The project path to check first
14
+ */
15
+ function getIndexDir(projectPath: string | null): string | null {
16
+ // Try project path first
17
+ if (projectPath) {
18
+ const projectIndexDir = join(projectPath, '.claude', 'skills', 'ccw-help', 'index');
19
+ if (existsSync(projectIndexDir)) {
20
+ return projectIndexDir;
21
+ }
22
+ }
23
+
24
+ // Fall back to user path
25
+ const userIndexDir = join(homedir(), '.claude', 'skills', 'ccw-help', 'index');
26
+ if (existsSync(userIndexDir)) {
27
+ return userIndexDir;
28
+ }
29
+
30
+ return null;
31
+ }
32
+
10
33
  // ========== In-Memory Cache ==========
11
34
  interface CacheEntry {
12
35
  data: any;
@@ -61,14 +84,15 @@ let watchersInitialized = false;
61
84
 
62
85
  /**
63
86
  * Initialize file watchers for JSON indexes
87
+ * @param projectPath - The project path to resolve index directory
64
88
  */
65
- function initializeFileWatchers(): void {
89
+ function initializeFileWatchers(projectPath: string | null): void {
66
90
  if (watchersInitialized) return;
67
91
 
68
- const indexDir = join(homedir(), '.claude', 'skills', 'command-guide', 'index');
92
+ const indexDir = getIndexDir(projectPath);
69
93
 
70
- if (!existsSync(indexDir)) {
71
- console.warn(`Command guide index directory not found: ${indexDir}`);
94
+ if (!indexDir) {
95
+ console.warn(`ccw-help index directory not found in project or user paths`);
72
96
  return;
73
97
  }
74
98
 
@@ -152,15 +176,20 @@ function groupCommandsByCategory(commands: any[]): any {
152
176
  * @returns true if route was handled, false otherwise
153
177
  */
154
178
  export async function handleHelpRoutes(ctx: RouteContext): Promise<boolean> {
155
- const { pathname, url, req, res } = ctx;
179
+ const { pathname, url, req, res, initialPath } = ctx;
156
180
 
157
181
  // Initialize file watchers on first request
158
- initializeFileWatchers();
182
+ initializeFileWatchers(initialPath);
159
183
 
160
- const indexDir = join(homedir(), '.claude', 'skills', 'command-guide', 'index');
184
+ const indexDir = getIndexDir(initialPath);
161
185
 
162
186
  // API: Get all commands with optional search
163
187
  if (pathname === '/api/help/commands') {
188
+ if (!indexDir) {
189
+ res.writeHead(404, { 'Content-Type': 'application/json' });
190
+ res.end(JSON.stringify({ error: 'ccw-help index directory not found' }));
191
+ return true;
192
+ }
164
193
  const searchQuery = url.searchParams.get('q') || '';
165
194
  const filePath = join(indexDir, 'all-commands.json');
166
195
 
@@ -191,6 +220,11 @@ export async function handleHelpRoutes(ctx: RouteContext): Promise<boolean> {
191
220
 
192
221
  // API: Get workflow command relationships
193
222
  if (pathname === '/api/help/workflows') {
223
+ if (!indexDir) {
224
+ res.writeHead(404, { 'Content-Type': 'application/json' });
225
+ res.end(JSON.stringify({ error: 'ccw-help index directory not found' }));
226
+ return true;
227
+ }
194
228
  const filePath = join(indexDir, 'command-relationships.json');
195
229
  const relationships = getCachedData('command-relationships', filePath);
196
230
 
@@ -207,6 +241,11 @@ export async function handleHelpRoutes(ctx: RouteContext): Promise<boolean> {
207
241
 
208
242
  // API: Get commands by category
209
243
  if (pathname === '/api/help/commands/by-category') {
244
+ if (!indexDir) {
245
+ res.writeHead(404, { 'Content-Type': 'application/json' });
246
+ res.end(JSON.stringify({ error: 'ccw-help index directory not found' }));
247
+ return true;
248
+ }
210
249
  const filePath = join(indexDir, 'by-category.json');
211
250
  const byCategory = getCachedData('by-category', filePath);
212
251
 
@@ -334,12 +334,43 @@ export async function handleLiteLLMApiRoutes(ctx: RouteContext): Promise<boolean
334
334
  return true;
335
335
  }
336
336
 
337
- // Test connection using litellm client
338
- const client = getLiteLLMClient();
339
- const available = await client.isAvailable();
337
+ // Get the API key to test (prefer first key from apiKeys array, fall back to default apiKey)
338
+ let apiKeyValue: string | null = null;
339
+ if (provider.apiKeys && provider.apiKeys.length > 0) {
340
+ apiKeyValue = provider.apiKeys[0].key;
341
+ } else if (provider.apiKey) {
342
+ apiKeyValue = provider.apiKey;
343
+ }
344
+
345
+ if (!apiKeyValue) {
346
+ res.writeHead(200, { 'Content-Type': 'application/json' });
347
+ res.end(JSON.stringify({ success: false, error: 'No API key configured for this provider' }));
348
+ return true;
349
+ }
350
+
351
+ // Resolve environment variables in the API key
352
+ const { resolveEnvVar } = await import('../../config/litellm-api-config-manager.js');
353
+ const resolvedKey = resolveEnvVar(apiKeyValue);
354
+
355
+ if (!resolvedKey) {
356
+ res.writeHead(200, { 'Content-Type': 'application/json' });
357
+ res.end(JSON.stringify({ success: false, error: 'API key is empty or environment variable not set' }));
358
+ return true;
359
+ }
360
+
361
+ // Determine API base URL
362
+ const apiBase = provider.apiBase || getDefaultApiBase(provider.type);
363
+
364
+ // Test the API key connection
365
+ const testResult = await testApiKeyConnection(provider.type, apiBase, resolvedKey);
340
366
 
341
367
  res.writeHead(200, { 'Content-Type': 'application/json' });
342
- res.end(JSON.stringify({ success: available, provider: provider.type }));
368
+ res.end(JSON.stringify({
369
+ success: testResult.valid,
370
+ provider: provider.type,
371
+ latencyMs: testResult.latencyMs,
372
+ error: testResult.error,
373
+ }));
343
374
  } catch (err) {
344
375
  res.writeHead(500, { 'Content-Type': 'application/json' });
345
376
  res.end(JSON.stringify({ success: false, error: (err as Error).message }));
@@ -1256,5 +1256,89 @@ RULES: Be concise. Focus on practical understanding. Include function signatures
1256
1256
  return true;
1257
1257
  }
1258
1258
 
1259
+ // API: Memory Queue - Add path to queue
1260
+ if (pathname === '/api/memory/queue/add' && req.method === 'POST') {
1261
+ handlePostRequest(req, res, async (body) => {
1262
+ const { path: modulePath, tool = 'gemini', strategy = 'single-layer' } = body;
1263
+
1264
+ if (!modulePath) {
1265
+ return { error: 'path is required', status: 400 };
1266
+ }
1267
+
1268
+ try {
1269
+ const { memoryQueueTool } = await import('../../tools/memory-update-queue.js');
1270
+ const result = await memoryQueueTool.execute({
1271
+ action: 'add',
1272
+ path: modulePath,
1273
+ tool,
1274
+ strategy
1275
+ }) as { queueSize?: number; willFlush?: boolean; flushed?: boolean };
1276
+
1277
+ // Broadcast queue update event
1278
+ broadcastToClients({
1279
+ type: 'MEMORY_QUEUE_UPDATED',
1280
+ payload: {
1281
+ action: 'add',
1282
+ path: modulePath,
1283
+ queueSize: result.queueSize || 0,
1284
+ willFlush: result.willFlush || false,
1285
+ flushed: result.flushed || false,
1286
+ timestamp: new Date().toISOString()
1287
+ }
1288
+ });
1289
+
1290
+ return { success: true, ...result };
1291
+ } catch (error: unknown) {
1292
+ return { error: (error as Error).message, status: 500 };
1293
+ }
1294
+ });
1295
+ return true;
1296
+ }
1297
+
1298
+ // API: Memory Queue - Get queue status
1299
+ if (pathname === '/api/memory/queue/status' && req.method === 'GET') {
1300
+ try {
1301
+ const { memoryQueueTool } = await import('../../tools/memory-update-queue.js');
1302
+ const result = await memoryQueueTool.execute({ action: 'status' }) as Record<string, unknown>;
1303
+
1304
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1305
+ res.end(JSON.stringify({ success: true, ...result }));
1306
+ } catch (error: unknown) {
1307
+ res.writeHead(500, { 'Content-Type': 'application/json' });
1308
+ res.end(JSON.stringify({ error: (error as Error).message }));
1309
+ }
1310
+ return true;
1311
+ }
1312
+
1313
+ // API: Memory Queue - Flush queue immediately
1314
+ if (pathname === '/api/memory/queue/flush' && req.method === 'POST') {
1315
+ handlePostRequest(req, res, async () => {
1316
+ try {
1317
+ const { memoryQueueTool } = await import('../../tools/memory-update-queue.js');
1318
+ const result = await memoryQueueTool.execute({ action: 'flush' }) as {
1319
+ processed?: number;
1320
+ success?: boolean;
1321
+ errors?: unknown[];
1322
+ };
1323
+
1324
+ // Broadcast queue flushed event
1325
+ broadcastToClients({
1326
+ type: 'MEMORY_QUEUE_FLUSHED',
1327
+ payload: {
1328
+ processed: result.processed || 0,
1329
+ success: result.success || false,
1330
+ errors: result.errors?.length || 0,
1331
+ timestamp: new Date().toISOString()
1332
+ }
1333
+ });
1334
+
1335
+ return { success: true, ...result };
1336
+ } catch (error: unknown) {
1337
+ return { error: (error as Error).message, status: 500 };
1338
+ }
1339
+ });
1340
+ return true;
1341
+ }
1342
+
1259
1343
  return false;
1260
1344
  }