ccjk 9.6.1 → 9.8.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 (56) hide show
  1. package/dist/chunks/boost.mjs +246 -7
  2. package/dist/chunks/ccjk-mcp.mjs +1 -1
  3. package/dist/chunks/ccr.mjs +25 -28
  4. package/dist/chunks/check-updates.mjs +4 -3
  5. package/dist/chunks/claude-code-config-manager.mjs +1 -1
  6. package/dist/chunks/claude-code-incremental-manager.mjs +1 -1
  7. package/dist/chunks/claude-config.mjs +1 -1
  8. package/dist/chunks/codex-config-switch.mjs +3 -4
  9. package/dist/chunks/codex-provider-manager.mjs +1 -2
  10. package/dist/chunks/codex.mjs +204 -3
  11. package/dist/chunks/config-switch.mjs +2 -3
  12. package/dist/chunks/config.mjs +1 -1
  13. package/dist/chunks/doctor.mjs +1 -1
  14. package/dist/chunks/features.mjs +24 -15
  15. package/dist/chunks/hook-installer.mjs +44 -0
  16. package/dist/chunks/index3.mjs +32 -32
  17. package/dist/chunks/init.mjs +129 -87
  18. package/dist/chunks/installer2.mjs +1 -1
  19. package/dist/chunks/interview.mjs +1 -1
  20. package/dist/chunks/mcp.mjs +1058 -17
  21. package/dist/chunks/menu.mjs +140 -56
  22. package/dist/chunks/package.mjs +2 -210
  23. package/dist/chunks/platform.mjs +1 -1
  24. package/dist/chunks/quick-setup.mjs +35 -18
  25. package/dist/chunks/simple-config.mjs +1 -1
  26. package/dist/{shared/ccjk.q1koQxEE.mjs → chunks/smart-defaults.mjs} +77 -79
  27. package/dist/chunks/status.mjs +208 -101
  28. package/dist/chunks/thinking.mjs +1 -1
  29. package/dist/chunks/uninstall.mjs +6 -4
  30. package/dist/chunks/update.mjs +4 -7
  31. package/dist/chunks/version-checker.mjs +1 -1
  32. package/dist/cli.mjs +4 -80
  33. package/dist/index.d.mts +17 -1482
  34. package/dist/index.d.ts +17 -1482
  35. package/dist/index.mjs +12 -4191
  36. package/dist/shared/{ccjk.CSkyCZIM.mjs → ccjk.Bndhan7G.mjs} +4 -242
  37. package/dist/shared/ccjk.CeE8RLG2.mjs +62 -0
  38. package/dist/shared/ccjk.DKojSRzw.mjs +266 -0
  39. package/dist/shared/{ccjk.CItD1fpl.mjs → ccjk.DvIrK0wz.mjs} +1 -1
  40. package/dist/shared/ccjk.LsPZ2PYo.mjs +1048 -0
  41. package/package.json +1 -1
  42. package/dist/chunks/api-adapter.mjs +0 -180
  43. package/dist/chunks/cli.mjs +0 -2227
  44. package/dist/chunks/context-menu.mjs +0 -913
  45. package/dist/chunks/hooks-sync.mjs +0 -1627
  46. package/dist/chunks/mcp-market.mjs +0 -1077
  47. package/dist/chunks/mcp-server.mjs +0 -776
  48. package/dist/chunks/project-detector.mjs +0 -131
  49. package/dist/chunks/provider-registry.mjs +0 -92
  50. package/dist/chunks/setup-wizard.mjs +0 -362
  51. package/dist/chunks/tools.mjs +0 -143
  52. package/dist/chunks/workflows2.mjs +0 -232
  53. package/dist/shared/ccjk.C0pb50xH.mjs +0 -347
  54. package/dist/shared/ccjk.ChMkBmdL.mjs +0 -490
  55. package/dist/shared/ccjk.CtSfXUSh.mjs +0 -209
  56. package/dist/shared/ccjk.xfAjmbJp.mjs +0 -75
package/dist/index.mjs CHANGED
@@ -1,17 +1,10 @@
1
- import * as fs from 'node:fs/promises';
2
- import { createHash } from 'node:crypto';
3
- import * as path from 'node:path';
4
- import { gzipSync, gunzipSync } from 'node:zlib';
5
- import { EventEmitter } from 'node:events';
6
- import chokidar from 'chokidar';
7
- import process__default from 'node:process';
8
- import ansis from 'ansis';
9
- import inquirer from 'inquirer';
10
1
  import { exec, execSync, spawn } from 'node:child_process';
11
2
  import { promises, existsSync, readdirSync, readFileSync, statSync, createReadStream } from 'node:fs';
12
3
  import * as os from 'node:os';
13
4
  import { homedir } from 'node:os';
5
+ import * as path from 'node:path';
14
6
  import { promisify } from 'node:util';
7
+ import ansis from 'ansis';
15
8
  import { version } from './chunks/package.mjs';
16
9
  import { join as join$1 } from 'pathe';
17
10
  export { c as config } from './chunks/config.mjs';
@@ -21,2716 +14,18 @@ import { Transform } from 'node:stream';
21
14
  import { pipeline } from 'node:stream/promises';
22
15
  import 'node:url';
23
16
  import 'dayjs';
17
+ import 'inquirer';
24
18
  import './chunks/constants.mjs';
25
19
  import './chunks/index.mjs';
20
+ import 'node:process';
26
21
  import 'i18next';
27
- import 'i18next-fs-backend';
28
- import './chunks/claude-config.mjs';
29
- import './chunks/json-config.mjs';
30
- import './chunks/fs-operations.mjs';
31
- import 'tinyexec';
32
-
33
- class MemoryCache {
34
- cache;
35
- head = null;
36
- tail = null;
37
- maxSize;
38
- ttl;
39
- hits = 0;
40
- misses = 0;
41
- constructor(maxSize = 1e3) {
42
- this.cache = /* @__PURE__ */ new Map();
43
- this.maxSize = maxSize;
44
- this.ttl = 1e3 * 60 * 30;
45
- }
46
- /**
47
- * Get entry from cache
48
- */
49
- get(key) {
50
- const node = this.cache.get(key);
51
- if (!node) {
52
- this.misses++;
53
- return void 0;
54
- }
55
- if (Date.now() - node.timestamp > this.ttl) {
56
- this.delete(key);
57
- this.misses++;
58
- return void 0;
59
- }
60
- this.moveToFront(node);
61
- this.hits++;
62
- return node.value;
63
- }
64
- /**
65
- * Set entry in cache
66
- */
67
- set(key, entry) {
68
- const existing = this.cache.get(key);
69
- if (existing) {
70
- existing.value = entry;
71
- existing.timestamp = Date.now();
72
- this.moveToFront(existing);
73
- return;
74
- }
75
- const node = {
76
- key,
77
- value: entry,
78
- timestamp: Date.now(),
79
- prev: null,
80
- next: null
81
- };
82
- this.cache.set(key, node);
83
- this.addToFront(node);
84
- if (this.cache.size > this.maxSize) {
85
- this.evictLRU();
86
- }
87
- }
88
- /**
89
- * Check if key exists
90
- */
91
- has(key) {
92
- const node = this.cache.get(key);
93
- if (!node) {
94
- return false;
95
- }
96
- if (Date.now() - node.timestamp > this.ttl) {
97
- this.delete(key);
98
- return false;
99
- }
100
- return true;
101
- }
102
- /**
103
- * Delete entry from cache
104
- */
105
- delete(key) {
106
- const node = this.cache.get(key);
107
- if (!node) {
108
- return false;
109
- }
110
- this.removeNode(node);
111
- return this.cache.delete(key);
112
- }
113
- /**
114
- * Clear all entries
115
- */
116
- clear() {
117
- this.cache.clear();
118
- this.head = null;
119
- this.tail = null;
120
- this.hits = 0;
121
- this.misses = 0;
122
- }
123
- /**
124
- * Get cache statistics
125
- */
126
- getStats() {
127
- const total = this.hits + this.misses;
128
- const hitRate = total > 0 ? this.hits / total : 0;
129
- return {
130
- hits: this.hits,
131
- misses: this.misses,
132
- size: this.cache.size,
133
- hitRate
134
- };
135
- }
136
- /**
137
- * Get cache size
138
- */
139
- getSize() {
140
- return this.cache.size;
141
- }
142
- /**
143
- * Check if cache is empty
144
- */
145
- isEmpty() {
146
- return this.cache.size === 0;
147
- }
148
- /**
149
- * Get all keys
150
- */
151
- keys() {
152
- return Array.from(this.cache.keys());
153
- }
154
- /**
155
- * Get all values
156
- */
157
- values() {
158
- return Array.from(this.cache.values()).map((node) => node.value);
159
- }
160
- /**
161
- * Dump cache contents (for debugging)
162
- */
163
- dump() {
164
- const map = /* @__PURE__ */ new Map();
165
- const entries = Array.from(this.cache.entries());
166
- for (const [key, node] of entries) {
167
- map.set(key, node.value);
168
- }
169
- return map;
170
- }
171
- /**
172
- * Load multiple entries into cache
173
- */
174
- loadMany(entries) {
175
- for (const [key, entry] of entries) {
176
- this.set(key, entry);
177
- }
178
- }
179
- /**
180
- * Delete multiple entries
181
- */
182
- deleteMany(keys) {
183
- let deleted = 0;
184
- for (const key of keys) {
185
- if (this.cache.delete(key)) {
186
- deleted++;
187
- }
188
- }
189
- return deleted;
190
- }
191
- /**
192
- * Reset statistics
193
- */
194
- resetStats() {
195
- this.hits = 0;
196
- this.misses = 0;
197
- }
198
- /**
199
- * Get estimated memory usage
200
- */
201
- getEstimatedSize() {
202
- let size = 0;
203
- const entries = Array.from(this.cache.entries());
204
- for (const [key, node] of entries) {
205
- size += key.length * 2;
206
- size += JSON.stringify(node.value).length * 2;
207
- }
208
- return size;
209
- }
210
- /**
211
- * Prune cache (remove expired entries)
212
- */
213
- prune() {
214
- }
215
- /**
216
- * Set cache size limit
217
- */
218
- setMaxSize(maxSize) {
219
- this.maxSize = maxSize;
220
- while (this.cache.size > this.maxSize) {
221
- this.evictLRU();
222
- }
223
- }
224
- /**
225
- * Get cache configuration
226
- */
227
- getConfig() {
228
- return {
229
- maxSize: this.maxSize,
230
- ttl: this.ttl
231
- };
232
- }
233
- /**
234
- * Move node to front of list
235
- */
236
- moveToFront(node) {
237
- if (node === this.head) {
238
- return;
239
- }
240
- this.removeNode(node);
241
- this.addToFront(node);
242
- }
243
- /**
244
- * Add node to front of list
245
- */
246
- addToFront(node) {
247
- node.next = this.head;
248
- node.prev = null;
249
- if (this.head) {
250
- this.head.prev = node;
251
- }
252
- this.head = node;
253
- if (!this.tail) {
254
- this.tail = node;
255
- }
256
- }
257
- /**
258
- * Remove node from list
259
- */
260
- removeNode(node) {
261
- if (node.prev) {
262
- node.prev.next = node.next;
263
- } else {
264
- this.head = node.next;
265
- }
266
- if (node.next) {
267
- node.next.prev = node.prev;
268
- } else {
269
- this.tail = node.prev;
270
- }
271
- }
272
- /**
273
- * Evict least recently used entry
274
- */
275
- evictLRU() {
276
- if (!this.tail) {
277
- return;
278
- }
279
- this.cache.delete(this.tail.key);
280
- this.removeNode(this.tail);
281
- }
282
- }
283
- let globalCache = null;
284
- function getGlobalCache(maxSize = 1e3) {
285
- if (!globalCache) {
286
- globalCache = new MemoryCache(maxSize);
287
- }
288
- return globalCache;
289
- }
290
-
291
- class LevelDBStorage {
292
- dbPath;
293
- compressionEnabled;
294
- cache;
295
- initialized = false;
296
- constructor(dbPath, compressionEnabled = true) {
297
- this.dbPath = dbPath;
298
- this.compressionEnabled = compressionEnabled;
299
- this.cache = /* @__PURE__ */ new Map();
300
- }
301
- /**
302
- * Initialize storage
303
- */
304
- async initialize() {
305
- if (this.initialized) {
306
- return;
307
- }
308
- try {
309
- await fs.mkdir(this.dbPath, { recursive: true });
310
- await this.loadFromDisk();
311
- this.initialized = true;
312
- } catch (error) {
313
- console.error("Failed to initialize storage:", error);
314
- }
315
- }
316
- /**
317
- * Load data from disk
318
- */
319
- async loadFromDisk() {
320
- try {
321
- const files = await fs.readdir(this.dbPath);
322
- for (const file of files) {
323
- if (file.endsWith(".json")) {
324
- const filePath = path.join(this.dbPath, file);
325
- const content = await fs.readFile(filePath, "utf-8");
326
- const entry = JSON.parse(content);
327
- this.cache.set(entry.key, entry);
328
- }
329
- }
330
- } catch (_error) {
331
- }
332
- }
333
- /**
334
- * Get file path for a key
335
- */
336
- getFilePath(key) {
337
- const hash = createHash("md5").update(key).digest("hex");
338
- return path.join(this.dbPath, `${hash}.json`);
339
- }
340
- /**
341
- * Store a cache entry
342
- */
343
- async put(entry) {
344
- await this.initialize();
345
- const dataToStore = { ...entry };
346
- if (this.compressionEnabled && shouldCompress(entry.type)) {
347
- dataToStore.data = compressData(entry.data);
348
- dataToStore.compressed = true;
349
- }
350
- this.cache.set(entry.key, dataToStore);
351
- const filePath = this.getFilePath(entry.key);
352
- await fs.writeFile(filePath, JSON.stringify(dataToStore, null, 2), "utf-8");
353
- }
354
- /**
355
- * Retrieve a cache entry
356
- */
357
- async get(key) {
358
- await this.initialize();
359
- const entry = this.cache.get(key);
360
- if (!entry) {
361
- return null;
362
- }
363
- if (entry.compressed) {
364
- const decompressed = { ...entry };
365
- decompressed.data = decompressData(entry.data);
366
- decompressed.compressed = false;
367
- return decompressed;
368
- }
369
- return entry;
370
- }
371
- /**
372
- * Delete a cache entry
373
- */
374
- async del(key) {
375
- await this.initialize();
376
- this.cache.delete(key);
377
- const filePath = this.getFilePath(key);
378
- try {
379
- await fs.unlink(filePath);
380
- } catch (_error) {
381
- }
382
- }
383
- /**
384
- * Check if key exists
385
- */
386
- async has(key) {
387
- await this.initialize();
388
- return this.cache.has(key);
389
- }
390
- /**
391
- * Get all entries matching a prefix
392
- */
393
- async getByPrefix(prefix) {
394
- await this.initialize();
395
- const entries = [];
396
- const cacheEntries = Array.from(this.cache.entries());
397
- for (const [key, entry] of cacheEntries) {
398
- if (key.startsWith(prefix)) {
399
- if (entry.compressed) {
400
- const decompressed = { ...entry };
401
- decompressed.data = decompressData(entry.data);
402
- decompressed.compressed = false;
403
- entries.push(decompressed);
404
- } else {
405
- entries.push(entry);
406
- }
407
- }
408
- }
409
- return entries;
410
- }
411
- /**
412
- * Batch write multiple entries
413
- */
414
- async batch(entries) {
415
- await this.initialize();
416
- await Promise.all(entries.map((entry) => this.put(entry)));
417
- }
418
- /**
419
- * Clear all entries
420
- */
421
- async clear() {
422
- await this.initialize();
423
- this.cache.clear();
424
- try {
425
- const files = await fs.readdir(this.dbPath);
426
- await Promise.all(
427
- files.filter((file) => file.endsWith(".json")).map((file) => fs.unlink(path.join(this.dbPath, file)))
428
- );
429
- } catch (_error) {
430
- }
431
- }
432
- /**
433
- * Close database connection
434
- */
435
- async close() {
436
- this.initialized = false;
437
- }
438
- /**
439
- * Get approximate database size
440
- */
441
- async getSize() {
442
- await this.initialize();
443
- return this.cache.size;
444
- }
445
- /**
446
- * Compact database
447
- */
448
- async compact() {
449
- }
450
- /**
451
- * Generate checksum for data
452
- */
453
- generateChecksum(data) {
454
- const json = JSON.stringify(data);
455
- return createHash("sha256").update(json).digest("hex");
456
- }
457
- /**
458
- * Verify checksum
459
- */
460
- verifyChecksum(entry) {
461
- const calculated = this.generateChecksum(entry.data);
462
- return calculated === entry.checksum;
463
- }
464
- }
465
- function shouldCompress(type) {
466
- return type === "ast" || type === "call-graph";
467
- }
468
- function compressData(data) {
469
- const json = JSON.stringify(data);
470
- const compressed = gzipSync(Buffer.from(json, "utf-8"));
471
- return compressed.toString("base64");
472
- }
473
- function decompressData(compressedData) {
474
- const buffer = Buffer.from(compressedData, "base64");
475
- const decompressed = gunzipSync(buffer);
476
- return JSON.parse(decompressed.toString("utf-8"));
477
- }
478
-
479
- class MultiLevelIndex {
480
- l1;
481
- l2;
482
- config;
483
- constructor(dbPath, config) {
484
- this.l1 = getGlobalCache(config?.l1?.maxSize || 1e3);
485
- this.l2 = new LevelDBStorage(dbPath);
486
- this.config = {
487
- l1: {
488
- name: "L1-Memory",
489
- maxSize: config?.l1?.maxSize || 1e3,
490
- ttl: config?.l1?.ttl || 1e3 * 60 * 30
491
- // 30 minutes
492
- },
493
- l2: {
494
- name: "L2-Disk",
495
- maxSize: config?.l2?.maxSize || 1e5,
496
- ttl: config?.l2?.ttl || 1e3 * 60 * 60 * 24 * 7
497
- // 7 days
498
- }
499
- };
500
- }
501
- /**
502
- * Get entry from cache (L1 first, then L2)
503
- */
504
- async get(key) {
505
- const l1Entry = this.l1.get(key);
506
- if (l1Entry) {
507
- l1Entry.accessCount++;
508
- l1Entry.lastAccess = Date.now();
509
- l1Entry.level = 1;
510
- return l1Entry;
511
- }
512
- const l2Entry = await this.l2.get(key);
513
- if (l2Entry) {
514
- if (l2Entry.accessCount > 5) {
515
- await this.promoteToL1(key, l2Entry);
516
- }
517
- l2Entry.accessCount++;
518
- l2Entry.lastAccess = Date.now();
519
- l2Entry.level = 2;
520
- return l2Entry;
521
- }
522
- return null;
523
- }
524
- /**
525
- * Set entry in cache (writes to both levels)
526
- */
527
- async set(key, entry) {
528
- const indexedEntry = {
529
- ...entry,
530
- accessCount: 0,
531
- lastAccess: Date.now(),
532
- level: 1
533
- };
534
- this.l1.set(key, indexedEntry);
535
- await this.l2.put(indexedEntry);
536
- }
537
- /**
538
- * Delete entry from both levels
539
- */
540
- async delete(key) {
541
- this.l1.delete(key);
542
- await this.l2.del(key);
543
- }
544
- /**
545
- * Check if key exists
546
- */
547
- async has(key) {
548
- if (this.l1.has(key)) {
549
- return true;
550
- }
551
- return await this.l2.has(key);
552
- }
553
- /**
554
- * Get all entries for a file path
555
- */
556
- async getByFilePath(filePath) {
557
- const prefix = `${filePath}|`;
558
- const l1Entries = [];
559
- for (const key of this.l1.keys()) {
560
- if (key.startsWith(prefix)) {
561
- const entry = this.l1.get(key);
562
- if (entry) {
563
- l1Entries.push(entry);
564
- }
565
- }
566
- }
567
- const l2Entries = await this.l2.getByPrefix(prefix);
568
- return [...l1Entries, ...l2Entries];
569
- }
570
- /**
571
- * Promote entry to L1 cache
572
- */
573
- async promoteToL1(key, entry) {
574
- entry.level = 1;
575
- this.l1.set(key, entry);
576
- }
577
- /**
578
- * Demote entry from L1 to L2
579
- */
580
- async demoteToL2(key) {
581
- const entry = this.l1.get(key);
582
- if (entry) {
583
- entry.level = 2;
584
- await this.l2.put(entry);
585
- this.l1.delete(key);
586
- }
587
- }
588
- /**
589
- * Get combined statistics
590
- */
591
- async getStats() {
592
- const l1Stats = this.l1.getStats();
593
- const l2Size = await this.l2.getSize();
594
- const totalHits = l1Stats.hits;
595
- const totalMisses = l1Stats.misses;
596
- const combinedHitRate = totalHits + totalMisses > 0 ? totalHits / (totalHits + totalMisses) : 0;
597
- return {
598
- l1: l1Stats,
599
- l2: { size: l2Size },
600
- combined: {
601
- hitRate: combinedHitRate
602
- }
603
- };
604
- }
605
- /**
606
- * Clear all caches
607
- */
608
- async clear() {
609
- this.l1.clear();
610
- await this.l2.clear();
611
- }
612
- /**
613
- * Warm up L1 cache with frequently accessed entries
614
- */
615
- async warmup(filePath) {
616
- const entries = await this.getByFilePath(filePath);
617
- entries.sort((a, b) => b.accessCount - a.accessCount).slice(0, 100).forEach(async (entry) => {
618
- await this.promoteToL1(`${filePath}|${entry.type}`, entry);
619
- });
620
- }
621
- /**
622
- * Evict stale entries from L1
623
- */
624
- async evictStale() {
625
- const keys = this.l1.keys();
626
- const now = Date.now();
627
- for (const key of keys) {
628
- const entry = this.l1.get(key);
629
- if (entry && now - entry.lastAccess > this.config.l1.ttl) {
630
- await this.demoteToL2(key);
631
- }
632
- }
633
- }
634
- /**
635
- * Close cache system
636
- */
637
- async close() {
638
- await this.l2.close();
639
- }
640
- /**
641
- * Compact L2 storage
642
- */
643
- async compact() {
644
- await this.l2.compact();
645
- }
646
- /**
647
- * Get cache configuration
648
- */
649
- getConfig() {
650
- return { ...this.config };
651
- }
652
- /**
653
- * Update cache configuration
654
- */
655
- updateConfig(config) {
656
- if (config.l1) {
657
- this.config.l1 = { ...this.config.l1, ...config.l1 };
658
- if (config.l1.maxSize) {
659
- this.l1.setMaxSize(config.l1.maxSize);
660
- }
661
- }
662
- if (config.l2) {
663
- this.config.l2 = { ...this.config.l2, ...config.l2 };
664
- }
665
- }
666
- }
667
- let globalIndex = null;
668
- function getGlobalIndex(dbPath = "./actionbook-cache") {
669
- if (!globalIndex) {
670
- globalIndex = new MultiLevelIndex(dbPath);
671
- }
672
- return globalIndex;
673
- }
674
- async function closeGlobalIndex() {
675
- if (globalIndex) {
676
- await globalIndex.close();
677
- globalIndex = null;
678
- }
679
- }
680
-
681
- async function queryAST(filePath, offset) {
682
- const index = getGlobalIndex();
683
- const key = offset ? `${filePath}|ast|${offset}` : `${filePath}|ast`;
684
- const entry = await index.get(key);
685
- if (!entry) {
686
- return null;
687
- }
688
- return entry.data;
689
- }
690
- async function queryASTAtPosition(filePath, line, column) {
691
- const ast = await queryAST(filePath);
692
- if (!ast) {
693
- return null;
694
- }
695
- return findNodeAtPosition(ast, line, column);
696
- }
697
- function findNodeAtPosition(node, line, column) {
698
- const isBefore = line < node.range.start.line || line === node.range.start.line && column < node.range.start.column;
699
- const isAfter = line > node.range.end.line || line === node.range.end.line && column > node.range.end.column;
700
- if (isBefore || isAfter) {
701
- return null;
702
- }
703
- for (const child of node.children) {
704
- const found = findNodeAtPosition(child, line, column);
705
- if (found) {
706
- return found;
707
- }
708
- }
709
- return node;
710
- }
711
- async function queryASTByType(filePath, nodeType) {
712
- const ast = await queryAST(filePath);
713
- if (!ast) {
714
- return [];
715
- }
716
- return findNodesByType(ast, nodeType);
717
- }
718
- function findNodesByType(node, nodeType) {
719
- const results = [];
720
- if (node.type === nodeType) {
721
- results.push(node);
722
- }
723
- for (const child of node.children) {
724
- results.push(...findNodesByType(child, nodeType));
725
- }
726
- return results;
727
- }
728
- async function queryASTByName(filePath, name) {
729
- const ast = await queryAST(filePath);
730
- if (!ast) {
731
- return [];
732
- }
733
- return findNodesByName(ast, name);
734
- }
735
- function findNodesByName(node, name) {
736
- const results = [];
737
- if (node.name === name) {
738
- results.push(node);
739
- }
740
- for (const child of node.children) {
741
- results.push(...findNodesByName(child, name));
742
- }
743
- return results;
744
- }
745
-
746
- const astQueries = {
747
- __proto__: null,
748
- queryAST: queryAST,
749
- queryASTAtPosition: queryASTAtPosition,
750
- queryASTByName: queryASTByName,
751
- queryASTByType: queryASTByType
752
- };
753
-
754
- async function queryCallGraph(filePath) {
755
- const index = getGlobalIndex();
756
- const key = `${filePath}|call-graph`;
757
- const entry = await index.get(key);
758
- if (!entry) {
759
- return null;
760
- }
761
- return entry.data;
762
- }
763
- async function queryEntryPoints(filePath) {
764
- const callGraph = await queryCallGraph(filePath);
765
- if (!callGraph) {
766
- return [];
767
- }
768
- const entryNodes = callGraph.nodes.filter(
769
- (node) => callGraph.entryPoints.includes(node.id)
770
- );
771
- return entryNodes;
772
- }
773
- async function queryFunction(filePath, name) {
774
- const callGraph = await queryCallGraph(filePath);
775
- if (!callGraph) {
776
- return null;
777
- }
778
- return callGraph.nodes.find((node) => node.name === name) || null;
779
- }
780
- async function queryCallers(filePath, functionName) {
781
- const callGraph = await queryCallGraph(filePath);
782
- if (!callGraph) {
783
- return [];
784
- }
785
- const targetNode = callGraph.nodes.find((n) => n.name === functionName);
786
- if (!targetNode) {
787
- return [];
788
- }
789
- return callGraph.edges.filter((edge) => edge.to === targetNode.id);
790
- }
791
- async function queryCallees(filePath, functionName) {
792
- const callGraph = await queryCallGraph(filePath);
793
- if (!callGraph) {
794
- return [];
795
- }
796
- const sourceNode = callGraph.nodes.find((n) => n.name === functionName);
797
- if (!sourceNode) {
798
- return [];
799
- }
800
- return callGraph.edges.filter((edge) => edge.from === sourceNode.id);
801
- }
802
- async function queryCallChain(filePath, from, to) {
803
- const callGraph = await queryCallGraph(filePath);
804
- if (!callGraph) {
805
- return [];
806
- }
807
- const fromNode = callGraph.nodes.find((n) => n.name === from);
808
- const toNode = callGraph.nodes.find((n) => n.name === to);
809
- if (!fromNode || !toNode) {
810
- return [];
811
- }
812
- const queue = [
813
- { node: fromNode.id, path: [fromNode.id] }
814
- ];
815
- const visited = /* @__PURE__ */ new Set([fromNode.id]);
816
- while (queue.length > 0) {
817
- const { node, path } = queue.shift();
818
- if (node === toNode.id) {
819
- return path.map((id) => callGraph.nodes.find((n) => n.id === id).name);
820
- }
821
- const edges = callGraph.edges.filter((e) => e.from === node);
822
- for (const edge of edges) {
823
- if (!visited.has(edge.to)) {
824
- visited.add(edge.to);
825
- queue.push({ node: edge.to, path: [...path, edge.to] });
826
- }
827
- }
828
- }
829
- return [];
830
- }
831
- async function queryRecursiveFunctions(filePath) {
832
- const callGraph = await queryCallGraph(filePath);
833
- if (!callGraph) {
834
- return [];
835
- }
836
- const recursiveNodes = [];
837
- for (const node of callGraph.nodes) {
838
- const edges = callGraph.edges.filter((e) => e.from === node.id);
839
- if (edges.some((e) => e.to === node.id)) {
840
- recursiveNodes.push(node);
841
- }
842
- }
843
- return recursiveNodes;
844
- }
845
- async function queryCallFrequency(filePath) {
846
- const callGraph = await queryCallGraph(filePath);
847
- if (!callGraph) {
848
- return /* @__PURE__ */ new Map();
849
- }
850
- const frequency = /* @__PURE__ */ new Map();
851
- for (const edge of callGraph.edges) {
852
- const toNode = callGraph.nodes.find((n) => n.id === edge.to);
853
- if (toNode) {
854
- const currentCount = frequency.get(toNode.name) || 0;
855
- frequency.set(toNode.name, currentCount + edge.callCount);
856
- }
857
- }
858
- return frequency;
859
- }
860
- async function queryMostCalledFunctions(filePath, limit = 10) {
861
- const frequency = await queryCallFrequency(filePath);
862
- return Array.from(frequency.entries()).sort((a, b) => b[1] - a[1]).slice(0, limit).map(([name, count]) => ({ name, count }));
863
- }
864
- async function queryFunctionDepth(filePath, functionName) {
865
- const callGraph = await queryCallGraph(filePath);
866
- if (!callGraph) {
867
- return 0;
868
- }
869
- const targetNode = callGraph.nodes.find((n) => n.name === functionName);
870
- if (!targetNode) {
871
- return 0;
872
- }
873
- const queue = callGraph.entryPoints.map((id) => ({
874
- node: id,
875
- depth: 0
876
- }));
877
- const visited = new Set(callGraph.entryPoints);
878
- while (queue.length > 0) {
879
- const { node, depth } = queue.shift();
880
- if (node === targetNode.id) {
881
- return depth;
882
- }
883
- const edges = callGraph.edges.filter((e) => e.from === node);
884
- for (const edge of edges) {
885
- if (!visited.has(edge.to)) {
886
- visited.add(edge.to);
887
- queue.push({ node: edge.to, depth: depth + 1 });
888
- }
889
- }
890
- }
891
- return 0;
892
- }
893
- async function queryDynamicCalls(filePath) {
894
- const callGraph = await queryCallGraph(filePath);
895
- if (!callGraph) {
896
- return [];
897
- }
898
- return callGraph.edges.filter((edge) => edge.isDynamic);
899
- }
900
- async function queryAsyncFunctions(filePath) {
901
- const callGraph = await queryCallGraph(filePath);
902
- if (!callGraph) {
903
- return [];
904
- }
905
- return callGraph.nodes.filter((node) => node.isAsync);
906
- }
907
-
908
- const callGraphQueries = {
909
- __proto__: null,
910
- queryAsyncFunctions: queryAsyncFunctions,
911
- queryCallChain: queryCallChain,
912
- queryCallFrequency: queryCallFrequency,
913
- queryCallGraph: queryCallGraph,
914
- queryCallees: queryCallees,
915
- queryCallers: queryCallers,
916
- queryDynamicCalls: queryDynamicCalls,
917
- queryEntryPoints: queryEntryPoints,
918
- queryFunction: queryFunction,
919
- queryFunctionDepth: queryFunctionDepth,
920
- queryMostCalledFunctions: queryMostCalledFunctions,
921
- queryRecursiveFunctions: queryRecursiveFunctions
922
- };
923
-
924
- async function querySymbols(filePath) {
925
- const index = getGlobalIndex();
926
- const key = `${filePath}|symbol`;
927
- const entry = await index.get(key);
928
- if (!entry) {
929
- return null;
930
- }
931
- return entry.data;
932
- }
933
- async function queryExports(filePath) {
934
- const symbolTable = await querySymbols(filePath);
935
- if (!symbolTable) {
936
- return [];
937
- }
938
- return symbolTable.exports;
939
- }
940
- async function queryImports(filePath) {
941
- const symbolTable = await querySymbols(filePath);
942
- if (!symbolTable) {
943
- return [];
944
- }
945
- return symbolTable.imports;
946
- }
947
- async function querySymbolByName(filePath, name) {
948
- const symbolTable = await querySymbols(filePath);
949
- if (!symbolTable) {
950
- return null;
951
- }
952
- return symbolTable.symbols.find((s) => s.name === name) || null;
953
- }
954
- async function querySymbolsByKind(filePath, kind) {
955
- const symbolTable = await querySymbols(filePath);
956
- if (!symbolTable) {
957
- return [];
958
- }
959
- return symbolTable.symbols.filter((s) => s.kind === kind);
960
- }
961
- async function querySymbolAtPosition(filePath, line, column) {
962
- const symbolTable = await querySymbols(filePath);
963
- if (!symbolTable) {
964
- return null;
965
- }
966
- return symbolTable.symbols.find((symbol) => {
967
- const isBefore = line < symbol.range.start.line || line === symbol.range.start.line && column < symbol.range.start.column;
968
- const isAfter = line > symbol.range.end.line || line === symbol.range.end.line && column > symbol.range.end.column;
969
- return !isBefore && !isAfter;
970
- }) || null;
971
- }
972
- async function queryFunctions(filePath) {
973
- return querySymbolsByKind(filePath, "function");
974
- }
975
- async function queryClasses(filePath) {
976
- return querySymbolsByKind(filePath, "class");
977
- }
978
- async function queryInterfaces(filePath) {
979
- return querySymbolsByKind(filePath, "interface");
980
- }
981
- async function queryTypes(filePath) {
982
- return querySymbolsByKind(filePath, "type");
983
- }
984
- async function queryVariables(filePath) {
985
- return querySymbolsByKind(filePath, "variable");
986
- }
987
- async function queryScopeAtPosition(filePath, line, column) {
988
- const symbolTable = await querySymbols(filePath);
989
- if (!symbolTable) {
990
- return null;
991
- }
992
- for (const scope of symbolTable.scopes) {
993
- const isBefore = line < scope.range.start.line || line === scope.range.start.line && column < scope.range.start.column;
994
- const isAfter = line > scope.range.end.line || line === scope.range.end.line && column > scope.range.end.column;
995
- if (!isBefore && !isAfter) {
996
- return scope.id;
997
- }
998
- }
999
- return null;
1000
- }
1001
- async function querySymbolsInScope(filePath, scopeId) {
1002
- const symbolTable = await querySymbols(filePath);
1003
- if (!symbolTable) {
1004
- return [];
1005
- }
1006
- const scope = symbolTable.scopes.find((s) => s.id === scopeId);
1007
- if (!scope) {
1008
- return [];
1009
- }
1010
- return symbolTable.symbols.filter((s) => scope.symbols.includes(s.id));
1011
- }
1012
-
1013
- const symbolQueries = {
1014
- __proto__: null,
1015
- queryClasses: queryClasses,
1016
- queryExports: queryExports,
1017
- queryFunctions: queryFunctions,
1018
- queryImports: queryImports,
1019
- queryInterfaces: queryInterfaces,
1020
- queryScopeAtPosition: queryScopeAtPosition,
1021
- querySymbolAtPosition: querySymbolAtPosition,
1022
- querySymbolByName: querySymbolByName,
1023
- querySymbols: querySymbols,
1024
- querySymbolsByKind: querySymbolsByKind,
1025
- querySymbolsInScope: querySymbolsInScope,
1026
- queryTypes: queryTypes,
1027
- queryVariables: queryVariables
1028
- };
1029
-
1030
- class QueryAPIRouter {
1031
- /**
1032
- * Query AST for a file
1033
- */
1034
- async queryAST(filePath, offset) {
1035
- return queryAST(filePath, offset);
1036
- }
1037
- /**
1038
- * Query symbol table for a file
1039
- */
1040
- async querySymbols(filePath) {
1041
- return querySymbols(filePath);
1042
- }
1043
- /**
1044
- * Query call graph for a file
1045
- */
1046
- async queryCallGraph(filePath) {
1047
- return queryCallGraph(filePath);
1048
- }
1049
- /**
1050
- * Query complexity metrics for a file
1051
- */
1052
- async queryComplexity(filePath) {
1053
- const index = getGlobalIndex();
1054
- const key = `${filePath}|complexity`;
1055
- const entry = await index.get(key);
1056
- if (!entry) {
1057
- return null;
1058
- }
1059
- return entry.data;
1060
- }
1061
- /**
1062
- * Query patterns for a file
1063
- */
1064
- async queryPatterns(filePath) {
1065
- const index = getGlobalIndex();
1066
- const key = `${filePath}|patterns`;
1067
- const entry = await index.get(key);
1068
- if (!entry) {
1069
- return null;
1070
- }
1071
- return entry.data;
1072
- }
1073
- /**
1074
- * Query all precomputed data for a file
1075
- */
1076
- async queryAll(filePath) {
1077
- const index = getGlobalIndex();
1078
- const entries = await index.getByFilePath(filePath);
1079
- const result = {
1080
- ast: null,
1081
- symbols: null,
1082
- callGraph: null,
1083
- complexity: null,
1084
- patterns: null
1085
- };
1086
- for (const entry of entries) {
1087
- switch (entry.type) {
1088
- case "ast":
1089
- result.ast = entry.data;
1090
- break;
1091
- case "symbol":
1092
- result.symbols = entry.data;
1093
- break;
1094
- case "call-graph":
1095
- result.callGraph = entry.data;
1096
- break;
1097
- case "complexity":
1098
- result.complexity = entry.data;
1099
- break;
1100
- case "patterns":
1101
- result.patterns = entry.data;
1102
- break;
1103
- }
1104
- }
1105
- return result;
1106
- }
1107
- /**
1108
- * Get cache statistics
1109
- */
1110
- async getStats() {
1111
- const index = getGlobalIndex();
1112
- return index.getStats();
1113
- }
1114
- /**
1115
- * Clear all caches
1116
- */
1117
- async clear() {
1118
- const index = getGlobalIndex();
1119
- await index.clear();
1120
- }
1121
- /**
1122
- * Warm up cache for a file
1123
- */
1124
- async warmup(filePath) {
1125
- const index = getGlobalIndex();
1126
- await index.warmup(filePath);
1127
- }
1128
- }
1129
- let globalQueryAPI = null;
1130
- function getQueryAPI() {
1131
- if (!globalQueryAPI) {
1132
- globalQueryAPI = new QueryAPIRouter();
1133
- }
1134
- return globalQueryAPI;
1135
- }
1136
- const ast = astQueries;
1137
- const symbols = symbolQueries;
1138
- const callGraph = callGraphQueries;
1139
-
1140
- class DependencyTracker {
1141
- graph;
1142
- watchMode = false;
1143
- constructor() {
1144
- this.graph = {
1145
- nodes: /* @__PURE__ */ new Map(),
1146
- edges: /* @__PURE__ */ new Map()
1147
- };
1148
- }
1149
- /**
1150
- * Track dependencies from symbol table
1151
- */
1152
- trackDependencies(filePath, symbols) {
1153
- const node = this.getOrCreateNode(filePath);
1154
- node.dependencies.clear();
1155
- for (const importDecl of symbols.imports) {
1156
- const resolvedPath = this.resolveImportPath(filePath, importDecl.module);
1157
- if (resolvedPath) {
1158
- node.dependencies.add(resolvedPath);
1159
- if (!this.graph.edges.has(filePath)) {
1160
- this.graph.edges.set(filePath, /* @__PURE__ */ new Set());
1161
- }
1162
- this.graph.edges.get(filePath).add(resolvedPath);
1163
- const depNode = this.getOrCreateNode(resolvedPath);
1164
- depNode.dependents.add(filePath);
1165
- }
1166
- }
1167
- node.lastIndexed = Date.now();
1168
- }
1169
- /**
1170
- * Get dependents of a file (files that import this file)
1171
- */
1172
- getDependents(filePath) {
1173
- const node = this.graph.nodes.get(filePath);
1174
- return node ? new Set(node.dependents) : /* @__PURE__ */ new Set();
1175
- }
1176
- /**
1177
- * Get dependencies of a file (files this file imports)
1178
- */
1179
- getDependencies(filePath) {
1180
- const node = this.graph.nodes.get(filePath);
1181
- return node ? new Set(node.dependencies) : /* @__PURE__ */ new Set();
1182
- }
1183
- /**
1184
- * Get transitive dependents (all files that indirectly depend on this file)
1185
- */
1186
- getTransitiveDependents(filePath) {
1187
- const transitive = /* @__PURE__ */ new Set();
1188
- const queue = [filePath];
1189
- while (queue.length > 0) {
1190
- const current = queue.shift();
1191
- const directDependents = this.getDependents(current);
1192
- const dependentsArray = Array.from(directDependents);
1193
- for (const dependent of dependentsArray) {
1194
- if (!transitive.has(dependent)) {
1195
- transitive.add(dependent);
1196
- queue.push(dependent);
1197
- }
1198
- }
1199
- }
1200
- return transitive;
1201
- }
1202
- /**
1203
- * Get transitive dependencies (all files this file indirectly depends on)
1204
- */
1205
- getTransitiveDependencies(filePath) {
1206
- const transitive = /* @__PURE__ */ new Set();
1207
- const queue = [filePath];
1208
- const visited = /* @__PURE__ */ new Set([filePath]);
1209
- while (queue.length > 0) {
1210
- const current = queue.shift();
1211
- const directDeps = this.getDependencies(current);
1212
- const depsArray = Array.from(directDeps);
1213
- for (const dep of depsArray) {
1214
- if (!visited.has(dep)) {
1215
- visited.add(dep);
1216
- transitive.add(dep);
1217
- queue.push(dep);
1218
- }
1219
- }
1220
- }
1221
- return transitive;
1222
- }
1223
- /**
1224
- * Detect circular dependencies
1225
- */
1226
- detectCircularDependencies() {
1227
- const cycles = [];
1228
- const visited = /* @__PURE__ */ new Set();
1229
- const recStack = /* @__PURE__ */ new Set();
1230
- const path2 = [];
1231
- const dfs = (node) => {
1232
- visited.add(node);
1233
- recStack.add(node);
1234
- path2.push(node);
1235
- const neighbors = this.graph.edges.get(node) || /* @__PURE__ */ new Set();
1236
- const neighborsArray = Array.from(neighbors);
1237
- for (const neighbor of neighborsArray) {
1238
- if (!visited.has(neighbor)) {
1239
- if (dfs(neighbor)) {
1240
- return true;
1241
- }
1242
- } else if (recStack.has(neighbor)) {
1243
- const cycleStart = path2.indexOf(neighbor);
1244
- cycles.push([...path2.slice(cycleStart), neighbor]);
1245
- }
1246
- }
1247
- path2.pop();
1248
- recStack.delete(node);
1249
- return false;
1250
- };
1251
- const nodesArray = Array.from(this.graph.nodes.keys());
1252
- for (const node of nodesArray) {
1253
- if (!visited.has(node)) {
1254
- dfs(node);
1255
- }
1256
- }
1257
- return cycles;
1258
- }
1259
- /**
1260
- * Get files affected by a change (need reindexing)
1261
- */
1262
- getAffectedFiles(filePath) {
1263
- const affected = /* @__PURE__ */ new Set();
1264
- const dependents = this.getTransitiveDependents(filePath);
1265
- const dependentsArray = Array.from(dependents);
1266
- for (const dependent of dependentsArray) {
1267
- affected.add(dependent);
1268
- }
1269
- affected.add(filePath);
1270
- return affected;
1271
- }
1272
- /**
1273
- * Remove file from graph
1274
- */
1275
- removeFile(filePath) {
1276
- const node = this.graph.nodes.get(filePath);
1277
- if (node) {
1278
- const depsArray = Array.from(node.dependencies);
1279
- for (const dep of depsArray) {
1280
- const depNode = this.graph.nodes.get(dep);
1281
- if (depNode) {
1282
- depNode.dependents.delete(filePath);
1283
- }
1284
- }
1285
- this.graph.nodes.delete(filePath);
1286
- }
1287
- this.graph.edges.delete(filePath);
1288
- const edgesArray = Array.from(this.graph.edges);
1289
- for (const [_source, targets] of edgesArray) {
1290
- targets.delete(filePath);
1291
- }
1292
- }
1293
- /**
1294
- * Clear entire graph
1295
- */
1296
- clear() {
1297
- this.graph.nodes.clear();
1298
- this.graph.edges.clear();
1299
- }
1300
- /**
1301
- * Get graph statistics
1302
- */
1303
- getStats() {
1304
- let totalDependencies = 0;
1305
- let totalDependents = 0;
1306
- const nodesArray = Array.from(this.graph.nodes.values());
1307
- for (const node of nodesArray) {
1308
- totalDependencies += node.dependencies.size;
1309
- totalDependents += node.dependents.size;
1310
- }
1311
- const nodeCount = this.graph.nodes.size;
1312
- const avgDependencies = nodeCount > 0 ? totalDependencies / nodeCount : 0;
1313
- const avgDependents = nodeCount > 0 ? totalDependents / nodeCount : 0;
1314
- let edgeCount = 0;
1315
- const edgesValuesArray = Array.from(this.graph.edges.values());
1316
- for (const targets of edgesValuesArray) {
1317
- edgeCount += targets.size;
1318
- }
1319
- return {
1320
- nodeCount,
1321
- edgeCount,
1322
- avgDependencies,
1323
- avgDependents
1324
- };
1325
- }
1326
- /**
1327
- * Serialize graph to JSON
1328
- */
1329
- toJSON() {
1330
- const serialized = {
1331
- nodes: Array.from(this.graph.nodes.entries()).map(([path2, node]) => ({
1332
- path: path2,
1333
- dependencies: Array.from(node.dependencies),
1334
- dependents: Array.from(node.dependents),
1335
- lastIndexed: node.lastIndexed
1336
- })),
1337
- edges: Array.from(this.graph.edges.entries()).map(([source, targets]) => ({
1338
- source,
1339
- targets: Array.from(targets)
1340
- }))
1341
- };
1342
- return serialized;
1343
- }
1344
- /**
1345
- * Load graph from JSON
1346
- */
1347
- fromJSON(json) {
1348
- this.clear();
1349
- for (const nodeData of json.nodes || []) {
1350
- const node = {
1351
- filePath: nodeData.path,
1352
- dependencies: new Set(nodeData.dependencies),
1353
- dependents: new Set(nodeData.dependents),
1354
- lastIndexed: nodeData.lastIndexed
1355
- };
1356
- this.graph.nodes.set(nodeData.path, node);
1357
- }
1358
- for (const edgeData of json.edges || []) {
1359
- this.graph.edges.set(edgeData.source, new Set(edgeData.targets));
1360
- }
1361
- }
1362
- /**
1363
- * Resolve import path to absolute file path
1364
- */
1365
- resolveImportPath(fromFile, importPath) {
1366
- if (importPath.startsWith(".")) {
1367
- const fromDir = path.dirname(fromFile);
1368
- const resolved = path.resolve(fromDir, importPath);
1369
- if (!path.extname(resolved)) {
1370
- return `${resolved}.ts`;
1371
- }
1372
- return resolved;
1373
- }
1374
- return null;
1375
- }
1376
- /**
1377
- * Get or create node in graph
1378
- */
1379
- getOrCreateNode(filePath) {
1380
- let node = this.graph.nodes.get(filePath);
1381
- if (!node) {
1382
- node = {
1383
- filePath,
1384
- dependencies: /* @__PURE__ */ new Set(),
1385
- dependents: /* @__PURE__ */ new Set(),
1386
- lastIndexed: 0
1387
- };
1388
- this.graph.nodes.set(filePath, node);
1389
- }
1390
- return node;
1391
- }
1392
- /**
1393
- * Enable watch mode
1394
- */
1395
- enableWatchMode() {
1396
- this.watchMode = true;
1397
- }
1398
- /**
1399
- * Disable watch mode
1400
- */
1401
- disableWatchMode() {
1402
- this.watchMode = false;
1403
- }
1404
- /**
1405
- * Check if watch mode is enabled
1406
- */
1407
- isWatchModeEnabled() {
1408
- return this.watchMode;
1409
- }
1410
- }
1411
- let globalTracker = null;
1412
- function getGlobalTracker() {
1413
- if (!globalTracker) {
1414
- globalTracker = new DependencyTracker();
1415
- }
1416
- return globalTracker;
1417
- }
1418
- function resetGlobalTracker() {
1419
- globalTracker = null;
1420
- }
1421
-
1422
- async function parseAST(filePath) {
1423
- try {
1424
- const source = await fs.readFile(filePath, "utf-8");
1425
- return parseSourceToAST(source, filePath);
1426
- } catch (error) {
1427
- console.error(`Failed to parse AST for ${filePath}:`, error);
1428
- return null;
1429
- }
1430
- }
1431
- function parseSourceToAST(source, filePath) {
1432
- try {
1433
- const parser = require("@typescript-eslint/parser");
1434
- const estree = parser.parse(source, {
1435
- sourceType: "module",
1436
- ecmaVersion: "latest",
1437
- project: "./tsconfig.json",
1438
- filePath
1439
- });
1440
- return convertESTreeToASTNode(estree);
1441
- } catch (error) {
1442
- console.error(`Failed to parse source for ${filePath}:`, error);
1443
- return null;
1444
- }
1445
- }
1446
- function convertESTreeToASTNode(node, _parent) {
1447
- const astNode = {
1448
- type: node.type,
1449
- name: getNodeName(node),
1450
- range: {
1451
- start: {
1452
- line: node.loc?.start.line || 0,
1453
- column: node.loc?.start.column || 0
1454
- },
1455
- end: {
1456
- line: node.loc?.end.line || 0,
1457
- column: node.loc?.end.column || 0
1458
- }
1459
- },
1460
- children: [],
1461
- metadata: extractNodeMetadata(node)
1462
- };
1463
- for (const key of Object.keys(node)) {
1464
- if (key === "parent" || key === "loc" || key === "range")
1465
- continue;
1466
- const child = node[key];
1467
- if (Array.isArray(child)) {
1468
- for (const item of child) {
1469
- if (item && typeof item === "object" && item.type) {
1470
- astNode.children.push(convertESTreeToASTNode(item));
1471
- }
1472
- }
1473
- } else if (child && typeof child === "object" && child.type) {
1474
- astNode.children.push(convertESTreeToASTNode(child));
1475
- }
1476
- }
1477
- return astNode;
1478
- }
1479
- function getNodeName(node) {
1480
- if (node.id?.name)
1481
- return node.id.name;
1482
- if (node.key?.name)
1483
- return node.key.name;
1484
- if (node.name)
1485
- return node.name;
1486
- if (node.type === "Identifier")
1487
- return node.name;
1488
- return void 0;
1489
- }
1490
- function extractNodeMetadata(node) {
1491
- const metadata = {};
1492
- if (node.declarations)
1493
- metadata.declarationCount = node.declarations.length;
1494
- if (node.params)
1495
- metadata.paramCount = node.params.length;
1496
- if (node.async)
1497
- metadata.isAsync = true;
1498
- if (node.generator)
1499
- metadata.isGenerator = true;
1500
- if (node.kind)
1501
- metadata.kind = node.kind;
1502
- if (node.method)
1503
- metadata.isMethod = true;
1504
- return metadata;
1505
- }
1506
-
1507
- function generateCallGraph(filePath, ast) {
1508
- const callGraph = {
1509
- filePath,
1510
- nodes: [],
1511
- edges: [],
1512
- entryPoints: []
1513
- };
1514
- const functionStack = [];
1515
- const nodeIdCounter = 0;
1516
- extractFunctions(ast, callGraph, functionStack, nodeIdCounter);
1517
- functionStack.length = 0;
1518
- extractCallEdges(ast, callGraph, functionStack);
1519
- const calledFunctions = new Set(callGraph.edges.map((e) => e.to));
1520
- callGraph.entryPoints = callGraph.nodes.filter((node) => !calledFunctions.has(node.id)).map((node) => node.id);
1521
- return callGraph;
1522
- }
1523
- function extractFunctions(node, callGraph, functionStack, nodeIdCounter) {
1524
- if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") {
1525
- const callNode = {
1526
- id: `func-${nodeIdCounter++}`,
1527
- name: node.name || "<anonymous>",
1528
- kind: node.type.includes("Arrow") ? "arrow" : node.type === "FunctionExpression" ? "function" : "function",
1529
- range: node.range,
1530
- isAsync: node.metadata.isAsync || false,
1531
- isGenerator: node.metadata.isGenerator || false
1532
- };
1533
- callGraph.nodes.push(callNode);
1534
- functionStack.push(callNode.id);
1535
- }
1536
- for (const child of node.children) {
1537
- nodeIdCounter = extractFunctions(child, callGraph, functionStack, nodeIdCounter);
1538
- }
1539
- if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") {
1540
- functionStack.pop();
1541
- }
1542
- return nodeIdCounter;
1543
- }
1544
- function extractCallEdges(node, callGraph, functionStack) {
1545
- if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") {
1546
- const funcNode = callGraph.nodes.find((n) => n.range === node.range);
1547
- if (funcNode) {
1548
- functionStack.push(funcNode.id);
1549
- }
1550
- }
1551
- if (node.type === "CallExpression" || node.type === "NewExpression") {
1552
- const callee = node.children.find((c) => c.type === "Identifier" || c.type === "MemberExpression");
1553
- if (callee && functionStack.length > 0) {
1554
- const callerId = functionStack[functionStack.length - 1];
1555
- const calleeName = callee.name || getMemberExpressionName(callee);
1556
- const calleeNode = callGraph.nodes.find((n) => n.name === calleeName);
1557
- if (calleeNode) {
1558
- const existingEdge = callGraph.edges.find(
1559
- (e) => e.from === callerId && e.to === calleeNode.id && e.range.start.line === node.range.start.line
1560
- );
1561
- if (existingEdge) {
1562
- existingEdge.callCount++;
1563
- } else {
1564
- const edge = {
1565
- from: callerId,
1566
- to: calleeNode.id,
1567
- range: node.range,
1568
- isDynamic: node.type === "NewExpression" || callee.type === "MemberExpression",
1569
- callCount: 1
1570
- };
1571
- callGraph.edges.push(edge);
1572
- }
1573
- }
1574
- }
1575
- }
1576
- for (const child of node.children) {
1577
- extractCallEdges(child, callGraph, functionStack);
1578
- }
1579
- if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") {
1580
- functionStack.pop();
1581
- }
1582
- }
1583
- function getMemberExpressionName(node) {
1584
- const property = node.children.find((c) => c.type === "Identifier");
1585
- return property?.name;
1586
- }
1587
-
1588
- function calculateComplexity(ast, source) {
1589
- const cyclomatic = calculateCyclomaticComplexity(ast);
1590
- const cognitive = calculateCognitiveComplexity(ast);
1591
- const halstead = calculateHalsteadMetrics(ast);
1592
- const maintainability = calculateMaintainabilityIndex(ast, source, cyclomatic, halstead);
1593
- const linesOfCode = countLinesOfCode(source);
1594
- const commentRatio = calculateCommentRatio(source);
1595
- return {
1596
- cyclomatic,
1597
- cognitive,
1598
- halstead,
1599
- maintainabilityIndex: maintainability,
1600
- linesOfCode,
1601
- commentRatio
1602
- };
1603
- }
1604
- function calculateCyclomaticComplexity(ast) {
1605
- let complexity = 1;
1606
- for (const child of ast.children) {
1607
- complexity += calculateCyclomaticComplexity(child);
1608
- if (child.type === "IfStatement" || child.type === "ConditionalExpression" || child.type === "ForStatement" || child.type === "ForInStatement" || child.type === "ForOfStatement" || child.type === "WhileStatement" || child.type === "DoWhileStatement" || child.type === "SwitchCase") {
1609
- complexity++;
1610
- }
1611
- if (child.type === "CatchClause") {
1612
- complexity++;
1613
- }
1614
- }
1615
- return complexity;
1616
- }
1617
- function calculateCognitiveComplexity(ast, nestingLevel = 0) {
1618
- let complexity = 0;
1619
- for (const child of ast.children) {
1620
- if (child.type === "IfStatement" || child.type === "ConditionalExpression" || child.type === "ForStatement" || child.type === "ForInStatement" || child.type === "ForOfStatement" || child.type === "WhileStatement" || child.type === "DoWhileStatement" || child.type === "CatchClause") {
1621
- complexity += 1 + nestingLevel;
1622
- }
1623
- if (child.type === "SwitchCase") {
1624
- complexity += 1 + nestingLevel;
1625
- }
1626
- complexity += calculateCognitiveComplexity(child, nestingLevel + 1);
1627
- }
1628
- return complexity;
1629
- }
1630
- function calculateHalsteadMetrics(ast) {
1631
- const operators = /* @__PURE__ */ new Set();
1632
- const operands = /* @__PURE__ */ new Set();
1633
- const totalOperators = 0;
1634
- const totalOperands = 0;
1635
- extractHalsteadTokens(ast, operators, operands);
1636
- const n1 = operators.size;
1637
- const n2 = operands.size;
1638
- const N1 = totalOperators;
1639
- const N2 = totalOperands;
1640
- const vocabulary = n1 + n2;
1641
- const length = N1 + N2;
1642
- const volume = length * Math.log2(vocabulary);
1643
- const difficulty = n1 / 2 * (N2 / n2);
1644
- const effort = difficulty * volume;
1645
- const time = effort / 18;
1646
- const bugs = effort / 3e3;
1647
- return {
1648
- n1,
1649
- n2,
1650
- N1,
1651
- N2,
1652
- vocabulary,
1653
- difficulty,
1654
- effort,
1655
- bugs,
1656
- time
1657
- };
1658
- }
1659
- function extractHalsteadTokens(node, operators, operands, totalOperators, totalOperands) {
1660
- const operatorTypes = [
1661
- "BinaryExpression",
1662
- "LogicalExpression",
1663
- "UnaryExpression",
1664
- "AssignmentExpression",
1665
- "UpdateExpression",
1666
- "IfStatement",
1667
- "ForStatement",
1668
- "WhileStatement",
1669
- "DoWhileStatement",
1670
- "SwitchStatement",
1671
- "ConditionalExpression"
1672
- ];
1673
- if (operatorTypes.includes(node.type)) {
1674
- operators.add(node.type);
1675
- }
1676
- if (node.type === "Identifier" && node.name) {
1677
- operands.add(node.name);
1678
- } else if (node.type === "Literal") {
1679
- operands.add("literal");
1680
- }
1681
- for (const child of node.children) {
1682
- extractHalsteadTokens(child, operators, operands);
1683
- }
1684
- }
1685
- function calculateMaintainabilityIndex(ast, source, cyclomatic, halstead) {
1686
- const loc = countLinesOfCode(source);
1687
- const volume = (halstead.N1 + halstead.N2) * Math.log2(halstead.vocabulary);
1688
- const mi = 171 - 5.2 * Math.log(volume) - 0.23 * cyclomatic - 16.2 * Math.log(loc);
1689
- return Math.max(0, Math.min(100, mi));
1690
- }
1691
- function countLinesOfCode(source) {
1692
- const lines = source.split("\n");
1693
- return lines.filter((line) => line.trim().length > 0).length;
1694
- }
1695
- function calculateCommentRatio(source) {
1696
- const lines = source.split("\n");
1697
- const totalLines = lines.filter((line) => line.trim().length > 0).length;
1698
- const commentLines = lines.filter((line) => /^\s*\/\//.test(line) || /^\s*\/\*/.test(line)).length;
1699
- return totalLines > 0 ? commentLines / totalLines : 0;
1700
- }
1701
-
1702
- function detectPatterns(ast) {
1703
- const patterns = [];
1704
- patterns.push(...detectAntiPatterns(ast));
1705
- patterns.push(...detectCodeSmells(ast));
1706
- patterns.push(...detectSecurityRisks(ast));
1707
- patterns.push(...detectPerformanceIssues(ast));
1708
- return patterns;
1709
- }
1710
- function detectAntiPatterns(node, patterns = []) {
1711
- if (node.children.length > 50) {
1712
- patterns.push({
1713
- id: `pattern-god-object-${node.range.start.line}`,
1714
- type: "anti-pattern",
1715
- name: "God Object",
1716
- range: node.range,
1717
- description: "This element has too many children and may be doing too much",
1718
- suggestions: [
1719
- "Consider splitting into smaller, more focused components",
1720
- "Apply Single Responsibility Principle"
1721
- ],
1722
- severity: "warning"
1723
- });
1724
- }
1725
- if (node.type === "Literal" && typeof node.metadata.value === "number") {
1726
- const value = node.metadata.value;
1727
- if (value !== 0 && value !== 1 && Math.abs(value) > 10) {
1728
- patterns.push({
1729
- id: `pattern-magic-number-${node.range.start.line}`,
1730
- type: "anti-pattern",
1731
- name: "Magic Number",
1732
- range: node.range,
1733
- description: "Unnamed numeric literal",
1734
- suggestions: [
1735
- "Replace with named constant",
1736
- "Use configuration object for related values"
1737
- ],
1738
- severity: "info"
1739
- });
1740
- }
1741
- }
1742
- for (const child of node.children) {
1743
- detectAntiPatterns(child, patterns);
1744
- }
1745
- return patterns;
1746
- }
1747
- function detectCodeSmells(node, patterns = []) {
1748
- if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression") {
1749
- const loc = node.range.end.line - node.range.start.line;
1750
- if (loc > 50) {
1751
- patterns.push({
1752
- id: `pattern-long-function-${node.range.start.line}`,
1753
- type: "code-smell",
1754
- name: "Long Function",
1755
- range: node.range,
1756
- description: `Function has ${loc} lines (consider < 20)`,
1757
- suggestions: [
1758
- "Break down into smaller functions",
1759
- "Extract logical blocks into helper functions"
1760
- ],
1761
- severity: "warning"
1762
- });
1763
- }
1764
- const paramCount = node.metadata.paramCount || 0;
1765
- if (paramCount > 5) {
1766
- patterns.push({
1767
- id: `pattern-too-many-params-${node.range.start.line}`,
1768
- type: "code-smell",
1769
- name: "Too Many Parameters",
1770
- range: node.range,
1771
- description: `Function has ${paramCount} parameters (consider < 5)`,
1772
- suggestions: [
1773
- "Use parameter object",
1774
- "Split function into smaller functions"
1775
- ],
1776
- severity: "info"
1777
- });
1778
- }
1779
- }
1780
- if (node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression") {
1781
- const callbackDepth = countCallbackDepth(node);
1782
- if (callbackDepth > 3) {
1783
- patterns.push({
1784
- id: `pattern-callback-hell-${node.range.start.line}`,
1785
- type: "code-smell",
1786
- name: "Callback Hell",
1787
- range: node.range,
1788
- description: `Deep callback nesting (${callbackDepth} levels)`,
1789
- suggestions: [
1790
- "Use async/await instead of callbacks",
1791
- "Extract callbacks to named functions"
1792
- ],
1793
- severity: "warning"
1794
- });
1795
- }
1796
- }
1797
- for (const child of node.children) {
1798
- detectCodeSmells(child, patterns);
1799
- }
1800
- return patterns;
1801
- }
1802
- function detectSecurityRisks(node, patterns = []) {
1803
- if (node.type === "CallExpression" && node.name === "eval") {
1804
- patterns.push({
1805
- id: `pattern-eval-${node.range.start.line}`,
1806
- type: "security-risk",
1807
- name: "Use of eval()",
1808
- range: node.range,
1809
- description: "eval() can execute arbitrary code and is a security risk",
1810
- suggestions: [
1811
- "Avoid eval() completely",
1812
- "Use JSON.parse() for JSON data",
1813
- "Use object property access for dynamic properties"
1814
- ],
1815
- severity: "error"
1816
- });
1817
- }
1818
- if ((node.type === "AssignmentExpression" || node.type === "CallExpression") && node.name?.includes("innerHTML")) {
1819
- patterns.push({
1820
- id: `pattern-innerhtml-${node.range.start.line}`,
1821
- type: "security-risk",
1822
- name: "innerHTML Usage",
1823
- range: node.range,
1824
- description: "Setting innerHTML can lead to XSS vulnerabilities",
1825
- suggestions: [
1826
- "Use textContent instead",
1827
- "Sanitize HTML before insertion",
1828
- "Use DOM methods (createElement, appendChild)"
1829
- ],
1830
- severity: "warning"
1831
- });
1832
- }
1833
- if (node.type === "VariableDeclarator") {
1834
- const name = node.name?.toLowerCase() || "";
1835
- if (name.includes("password") || name.includes("secret") || name.includes("token") || name.includes("api_key")) {
1836
- patterns.push({
1837
- id: `pattern-hardcoded-secret-${node.range.start.line}`,
1838
- type: "security-risk",
1839
- name: "Hardcoded Secret",
1840
- range: node.range,
1841
- description: "Potential hardcoded credential or secret",
1842
- suggestions: [
1843
- "Use environment variables",
1844
- "Use secret management service",
1845
- "Never commit secrets to code"
1846
- ],
1847
- severity: "error"
1848
- });
1849
- }
1850
- }
1851
- for (const child of node.children) {
1852
- detectSecurityRisks(child, patterns);
1853
- }
1854
- return patterns;
1855
- }
1856
- function detectPerformanceIssues(node, patterns = []) {
1857
- if (node.type === "ForStatement" || node.type === "WhileStatement") {
1858
- const hasHeavyOps = node.children.some(
1859
- (child) => child.type === "CallExpression" && (child.name?.includes("querySelector") || child.name?.includes("innerHTML"))
1860
- );
1861
- if (hasHeavyOps) {
1862
- patterns.push({
1863
- id: `pattern-loop-heavy-ops-${node.range.start.line}`,
1864
- type: "performance-issue",
1865
- name: "Heavy Operation in Loop",
1866
- range: node.range,
1867
- description: "Loop contains DOM or heavy I/O operations",
1868
- suggestions: [
1869
- "Move heavy operations outside loop",
1870
- "Cache DOM queries before loop",
1871
- "Use batch processing"
1872
- ],
1873
- severity: "warning"
1874
- });
1875
- }
1876
- }
1877
- if (node.type === "ForStatement" || node.type === "WhileStatement") {
1878
- const loopNestingDepth = countLoopNesting(node);
1879
- if (loopNestingDepth > 2) {
1880
- patterns.push({
1881
- id: `pattern-nested-loops-${node.range.start.line}`,
1882
- type: "performance-issue",
1883
- name: "Nested Loops",
1884
- range: node.range,
1885
- description: `${loopNestingDepth} levels of loop nesting (O(n^${loopNestingDepth}) complexity)`,
1886
- suggestions: [
1887
- "Consider using data structures with better lookup (Map, Set)",
1888
- "Break into multiple loops",
1889
- "Use functional methods (map, filter, reduce)"
1890
- ],
1891
- severity: "info"
1892
- });
1893
- }
1894
- }
1895
- for (const child of node.children) {
1896
- detectPerformanceIssues(child, patterns);
1897
- }
1898
- return patterns;
1899
- }
1900
- function countCallbackDepth(node, depth = 0) {
1901
- let maxDepth = depth;
1902
- for (const child of node.children) {
1903
- if (child.type === "ArrowFunctionExpression" || child.type === "FunctionExpression") {
1904
- const childDepth = countCallbackDepth(child, depth + 1);
1905
- maxDepth = Math.max(maxDepth, childDepth);
1906
- } else {
1907
- const childDepth = countCallbackDepth(child, depth);
1908
- maxDepth = Math.max(maxDepth, childDepth);
1909
- }
1910
- }
1911
- return maxDepth;
1912
- }
1913
- function countLoopNesting(node, depth = 1) {
1914
- let maxDepth = depth;
1915
- for (const child of node.children) {
1916
- if (child.type === "ForStatement" || child.type === "WhileStatement" || child.type === "ForInStatement" || child.type === "ForOfStatement") {
1917
- const childDepth = countLoopNesting(child, depth + 1);
1918
- maxDepth = Math.max(maxDepth, childDepth);
1919
- } else {
1920
- const childDepth = countLoopNesting(child, depth);
1921
- maxDepth = Math.max(maxDepth, childDepth);
1922
- }
1923
- }
1924
- return maxDepth;
1925
- }
1926
-
1927
- function extractSymbols(filePath, ast) {
1928
- const symbolTable = {
1929
- filePath,
1930
- symbols: [],
1931
- exports: [],
1932
- imports: [],
1933
- scopes: []
1934
- };
1935
- const scopeStack = [];
1936
- let scopeIdCounter = 0;
1937
- const moduleScope = {
1938
- id: `scope-${scopeIdCounter++}`,
1939
- kind: "module",
1940
- range: ast.range,
1941
- children: [],
1942
- symbols: []
1943
- };
1944
- symbolTable.scopes.push(moduleScope);
1945
- scopeStack.push(moduleScope);
1946
- extractSymbolsFromNode(ast, symbolTable, scopeStack, scopeIdCounter);
1947
- return symbolTable;
1948
- }
1949
- function extractSymbolsFromNode(node, symbolTable, scopeStack, scopeIdCounter) {
1950
- const currentScope = scopeStack[scopeStack.length - 1];
1951
- switch (node.type) {
1952
- case "FunctionDeclaration":
1953
- case "FunctionExpression":
1954
- case "ArrowFunctionExpression": {
1955
- const symbol = createFunctionSymbol(node, currentScope.id);
1956
- symbolTable.symbols.push(symbol);
1957
- currentScope.symbols.push(symbol.id);
1958
- const functionScope = {
1959
- id: `scope-${scopeIdCounter++}`,
1960
- kind: "function",
1961
- range: node.range,
1962
- parent: currentScope.id,
1963
- children: [],
1964
- symbols: []
1965
- };
1966
- symbolTable.scopes.push(functionScope);
1967
- currentScope.children.push(functionScope.id);
1968
- scopeStack.push(functionScope);
1969
- break;
1970
- }
1971
- case "ClassDeclaration":
1972
- case "ClassExpression": {
1973
- const symbol = createClassSymbol(node, currentScope.id);
1974
- symbolTable.symbols.push(symbol);
1975
- currentScope.symbols.push(symbol.id);
1976
- const classScope = {
1977
- id: `scope-${scopeIdCounter++}`,
1978
- kind: "class",
1979
- range: node.range,
1980
- parent: currentScope.id,
1981
- children: [],
1982
- symbols: []
1983
- };
1984
- symbolTable.scopes.push(classScope);
1985
- currentScope.children.push(classScope.id);
1986
- scopeStack.push(classScope);
1987
- break;
1988
- }
1989
- case "VariableDeclaration":
1990
- case "VariableDeclarator": {
1991
- const symbol = createVariableSymbol(node, currentScope.id);
1992
- symbolTable.symbols.push(symbol);
1993
- currentScope.symbols.push(symbol.id);
1994
- break;
1995
- }
1996
- case "TSInterfaceDeclaration":
1997
- case "TSTypeAliasDeclaration": {
1998
- const symbol = createTypeSymbol(node, currentScope.id);
1999
- symbolTable.symbols.push(symbol);
2000
- symbolTable.exports.push(symbol);
2001
- currentScope.symbols.push(symbol.id);
2002
- break;
2003
- }
2004
- case "ImportDeclaration": {
2005
- const importDecl = createImportDeclaration(node);
2006
- symbolTable.imports.push(importDecl);
2007
- break;
2008
- }
2009
- case "ExportNamedDeclaration":
2010
- case "ExportDefaultDeclaration": {
2011
- if (node.name) {
2012
- const symbol = symbolTable.symbols.find((s) => s.name === node.name);
2013
- if (symbol && !symbolTable.exports.includes(symbol)) {
2014
- symbolTable.exports.push(symbol);
2015
- }
2016
- }
2017
- break;
2018
- }
2019
- case "BlockStatement": {
2020
- const blockScope = {
2021
- id: `scope-${scopeIdCounter++}`,
2022
- kind: "block",
2023
- range: node.range,
2024
- parent: currentScope.id,
2025
- children: [],
2026
- symbols: []
2027
- };
2028
- symbolTable.scopes.push(blockScope);
2029
- currentScope.children.push(blockScope.id);
2030
- scopeStack.push(blockScope);
2031
- break;
2032
- }
2033
- }
2034
- for (const child of node.children) {
2035
- scopeIdCounter = extractSymbolsFromNode(child, symbolTable, scopeStack, scopeIdCounter);
2036
- if (child.type === "FunctionDeclaration" || child.type === "FunctionExpression" || child.type === "ClassDeclaration" || child.type === "ClassExpression" || child.type === "BlockStatement") {
2037
- const childScopeEnd = symbolTable.scopes.findIndex((s) => s.range === child.range);
2038
- if (childScopeEnd !== -1 && childScopeEnd < symbolTable.scopes.length - 1) {
2039
- scopeStack.pop();
2040
- }
2041
- }
2042
- }
2043
- return scopeIdCounter;
2044
- }
2045
- function createFunctionSymbol(node, parentId) {
2046
- return {
2047
- id: `symbol-${node.type}-${node.range.start.line}-${node.range.start.column}`,
2048
- name: node.name || "<anonymous>",
2049
- kind: node.type.includes("Method") ? "method" : "function",
2050
- range: node.range,
2051
- definingScope: parentId,
2052
- references: [],
2053
- metadata: {
2054
- isAsync: node.metadata.isAsync || false,
2055
- isGenerator: node.metadata.isGenerator || false,
2056
- paramCount: node.metadata.paramCount || 0
2057
- }
2058
- };
2059
- }
2060
- function createClassSymbol(node, parentId) {
2061
- return {
2062
- id: `symbol-class-${node.range.start.line}-${node.range.start.column}`,
2063
- name: node.name || "<anonymous>",
2064
- kind: "class",
2065
- range: node.range,
2066
- definingScope: parentId,
2067
- references: [],
2068
- metadata: {}
2069
- };
2070
- }
2071
- function createVariableSymbol(node, parentId) {
2072
- const isConst = node.metadata.kind === "const";
2073
- return {
2074
- id: `symbol-variable-${node.range.start.line}-${node.range.start.column}`,
2075
- name: node.name || "<unknown>",
2076
- kind: isConst ? "constant" : "variable",
2077
- range: node.range,
2078
- definingScope: parentId,
2079
- references: [],
2080
- metadata: {}
2081
- };
2082
- }
2083
- function createTypeSymbol(node, parentId) {
2084
- return {
2085
- id: `symbol-type-${node.range.start.line}-${node.range.start.column}`,
2086
- name: node.name || "<anonymous>",
2087
- kind: node.type.includes("Interface") ? "interface" : "type",
2088
- range: node.range,
2089
- definingScope: parentId,
2090
- references: [],
2091
- metadata: {}
2092
- };
2093
- }
2094
- function createImportDeclaration(node) {
2095
- const imports = [];
2096
- for (const child of node.children) {
2097
- if (child.type === "ImportSpecifier" || child.type === "ImportDefaultSpecifier") {
2098
- imports.push({
2099
- name: child.name,
2100
- alias: child.metadata.local,
2101
- kind: child.type.replace("Import", "").toLowerCase()
2102
- });
2103
- }
2104
- }
2105
- return {
2106
- module: node.metadata.source || "",
2107
- imports,
2108
- range: node.range,
2109
- isDynamic: node.type === "ImportExpression"
2110
- };
2111
- }
2112
-
2113
- class IncrementalIndexer {
2114
- index;
2115
- storage;
2116
- dependencyGraph = /* @__PURE__ */ new Map();
2117
- reverseDependencyGraph = /* @__PURE__ */ new Map();
2118
- constructor() {
2119
- this.index = getGlobalIndex();
2120
- this.storage = new LevelDBStorage("./actionbook-cache");
2121
- }
2122
- /**
2123
- * Index a single file
2124
- */
2125
- async indexFile(filePath) {
2126
- try {
2127
- const stats = await fs.stat(filePath).catch(() => null);
2128
- if (!stats?.isFile()) {
2129
- return null;
2130
- }
2131
- const source = await fs.readFile(filePath, "utf-8");
2132
- const ast = await parseAST(filePath);
2133
- if (!ast) {
2134
- return null;
2135
- }
2136
- const symbols = extractSymbols(filePath, ast);
2137
- const callGraph = generateCallGraph(filePath, ast);
2138
- const complexity = calculateComplexity(ast, source);
2139
- const patterns = detectPatterns(ast);
2140
- const data = {
2141
- filePath,
2142
- ast,
2143
- symbols,
2144
- callGraph,
2145
- complexity,
2146
- patterns,
2147
- lastModified: stats.mtimeMs,
2148
- checksum: this.generateChecksum(source)
2149
- };
2150
- await this.storePrecomputedData(data);
2151
- await this.updateDependencies(filePath, symbols);
2152
- return data;
2153
- } catch (error) {
2154
- console.error(`Failed to index file ${filePath}:`, error);
2155
- return null;
2156
- }
2157
- }
2158
- /**
2159
- * Index multiple files
2160
- */
2161
- async indexFiles(filePaths) {
2162
- const startTime = Date.now();
2163
- const stats = {
2164
- filesIndexed: 0,
2165
- totalFiles: filePaths.length,
2166
- errors: [],
2167
- duration: 0
2168
- };
2169
- const batchSize = 10;
2170
- for (let i = 0; i < filePaths.length; i += batchSize) {
2171
- const batch = filePaths.slice(i, i + batchSize);
2172
- const results = await Promise.allSettled(
2173
- batch.map((path) => this.indexFile(path))
2174
- );
2175
- for (const result of results) {
2176
- if (result.status === "fulfilled" && result.value) {
2177
- stats.filesIndexed++;
2178
- } else if (result.status === "rejected") {
2179
- stats.errors.push(result.reason?.message || "Unknown error");
2180
- }
2181
- }
2182
- }
2183
- stats.duration = Date.now() - startTime;
2184
- return stats;
2185
- }
2186
- /**
2187
- * Remove file from index
2188
- */
2189
- async removeFile(filePath) {
2190
- await this.index.delete(`${filePath}|ast`);
2191
- await this.index.delete(`${filePath}|symbol`);
2192
- await this.index.delete(`${filePath}|call-graph`);
2193
- await this.index.delete(`${filePath}|complexity`);
2194
- await this.index.delete(`${filePath}|patterns`);
2195
- const dependencies = this.dependencyGraph.get(filePath);
2196
- if (dependencies) {
2197
- const depsArray = Array.from(dependencies);
2198
- for (const dep of depsArray) {
2199
- const dependents = this.reverseDependencyGraph.get(dep);
2200
- if (dependents) {
2201
- dependents.delete(filePath);
2202
- }
2203
- }
2204
- this.dependencyGraph.delete(filePath);
2205
- }
2206
- }
2207
- /**
2208
- * Remove multiple files
2209
- */
2210
- async removeFiles(filePaths) {
2211
- await Promise.all(filePaths.map((path) => this.removeFile(path)));
2212
- }
2213
- /**
2214
- * Reindex file and its dependents
2215
- */
2216
- async reindexFile(filePath) {
2217
- const dependents = this.reverseDependencyGraph.get(filePath) || /* @__PURE__ */ new Set();
2218
- const stats = await this.indexFiles([filePath]);
2219
- if (dependents.size > 0) {
2220
- const dependentStats = await this.indexFiles(Array.from(dependents));
2221
- stats.filesIndexed += dependentStats.filesIndexed;
2222
- stats.errors.push(...dependentStats.errors);
2223
- }
2224
- return stats;
2225
- }
2226
- /**
2227
- * Check if file needs reindexing
2228
- */
2229
- async needsReindex(filePath) {
2230
- try {
2231
- const stats = await fs.stat(filePath);
2232
- const key = `${filePath}|ast`;
2233
- const entry = await this.index.get(key);
2234
- if (!entry) {
2235
- return true;
2236
- }
2237
- return stats.mtimeMs > entry.timestamp;
2238
- } catch {
2239
- return true;
2240
- }
2241
- }
2242
- /**
2243
- * Store precomputed data in cache
2244
- */
2245
- async storePrecomputedData(data) {
2246
- const timestamp = Date.now();
2247
- await this.index.set(`${data.filePath}|ast`, {
2248
- key: `${data.filePath}|ast`,
2249
- type: "ast",
2250
- data: data.ast,
2251
- checksum: data.checksum,
2252
- timestamp,
2253
- compressed: false
2254
- });
2255
- await this.index.set(`${data.filePath}|symbol`, {
2256
- key: `${data.filePath}|symbol`,
2257
- type: "symbol",
2258
- data: data.symbols,
2259
- checksum: data.checksum,
2260
- timestamp,
2261
- compressed: false
2262
- });
2263
- await this.index.set(`${data.filePath}|call-graph`, {
2264
- key: `${data.filePath}|call-graph`,
2265
- type: "call-graph",
2266
- data: data.callGraph,
2267
- checksum: data.checksum,
2268
- timestamp,
2269
- compressed: false
2270
- });
2271
- await this.index.set(`${data.filePath}|complexity`, {
2272
- key: `${data.filePath}|complexity`,
2273
- type: "complexity",
2274
- data: data.complexity,
2275
- checksum: data.checksum,
2276
- timestamp,
2277
- compressed: false
2278
- });
2279
- await this.index.set(`${data.filePath}|patterns`, {
2280
- key: `${data.filePath}|patterns`,
2281
- type: "patterns",
2282
- data: data.patterns,
2283
- checksum: data.checksum,
2284
- timestamp,
2285
- compressed: false
2286
- });
2287
- }
2288
- /**
2289
- * Update dependency graph based on imports
2290
- */
2291
- async updateDependencies(filePath, symbols) {
2292
- const imports = /* @__PURE__ */ new Set();
2293
- for (const importDecl of symbols.imports) {
2294
- const resolvedPath = await this.resolveImportPath(filePath, importDecl.module);
2295
- if (resolvedPath) {
2296
- imports.add(resolvedPath);
2297
- }
2298
- }
2299
- this.dependencyGraph.set(filePath, imports);
2300
- const importsArray = Array.from(imports);
2301
- for (const dep of importsArray) {
2302
- if (!this.reverseDependencyGraph.has(dep)) {
2303
- this.reverseDependencyGraph.set(dep, /* @__PURE__ */ new Set());
2304
- }
2305
- this.reverseDependencyGraph.get(dep).add(filePath);
2306
- }
2307
- }
2308
- /**
2309
- * Resolve import path to absolute file path
2310
- */
2311
- async resolveImportPath(fromFile, importPath) {
2312
- if (importPath.startsWith(".")) {
2313
- const fromDir = fromFile.substring(0, fromFile.lastIndexOf("/"));
2314
- const resolved = `${fromDir}/${importPath}`;
2315
- const extensions = [".ts", ".tsx", ".js", ".jsx", ".json"];
2316
- for (const ext of extensions) {
2317
- const path = resolved + ext;
2318
- try {
2319
- await fs.access(path);
2320
- return path;
2321
- } catch {
2322
- try {
2323
- const indexPath = `${resolved}/index${ext}`;
2324
- await fs.access(indexPath);
2325
- return indexPath;
2326
- } catch {
2327
- continue;
2328
- }
2329
- }
2330
- }
2331
- }
2332
- return null;
2333
- }
2334
- /**
2335
- * Generate checksum for source code
2336
- */
2337
- generateChecksum(source) {
2338
- const crypto = require("node:crypto");
2339
- return crypto.createHash("sha256").update(source).digest("hex");
2340
- }
2341
- /**
2342
- * Get dependency graph
2343
- */
2344
- getDependencyGraph() {
2345
- return new Map(this.dependencyGraph);
2346
- }
2347
- /**
2348
- * Get reverse dependency graph
2349
- */
2350
- getReverseDependencyGraph() {
2351
- return new Map(this.reverseDependencyGraph);
2352
- }
2353
- /**
2354
- * Clear all indexes
2355
- */
2356
- async clear() {
2357
- await this.index.clear();
2358
- this.dependencyGraph.clear();
2359
- this.reverseDependencyGraph.clear();
2360
- }
2361
- }
2362
- let globalIndexer = null;
2363
- function getGlobalIndexer() {
2364
- if (!globalIndexer) {
2365
- globalIndexer = new IncrementalIndexer();
2366
- }
2367
- return globalIndexer;
2368
- }
2369
- function resetGlobalIndexer() {
2370
- globalIndexer = null;
2371
- }
2372
-
2373
- class FileWatcher extends EventEmitter {
2374
- watcher = null;
2375
- indexer;
2376
- watchedPaths = /* @__PURE__ */ new Set();
2377
- changeQueue = /* @__PURE__ */ new Map();
2378
- flushTimer = null;
2379
- FLUSH_DELAY = 100;
2380
- // ms
2381
- constructor(indexer) {
2382
- super();
2383
- this.indexer = indexer;
2384
- }
2385
- /**
2386
- * Start watching a path
2387
- */
2388
- watch(path, options = {}) {
2389
- if (this.watchedPaths.has(path)) {
2390
- return;
2391
- }
2392
- const watcherOptions = {
2393
- ignored: [
2394
- /(^|[\\/])\../,
2395
- // dot files
2396
- "**/node_modules/**",
2397
- "**/.git/**",
2398
- "**/dist/**",
2399
- "**/build/**",
2400
- "**/.next/**",
2401
- "**/coverage/**"
2402
- ],
2403
- persistent: options.persistent ?? true,
2404
- ignoreInitial: options.ignoreInitial ?? true,
2405
- ignorePermissionErrors: options.ignorePermissionErrors ?? true,
2406
- awaitWriteFinish: options.awaitWriteFinish ?? {
2407
- stabilityThreshold: 300,
2408
- pollInterval: 100
2409
- }
2410
- };
2411
- this.watcher = chokidar.watch(path, watcherOptions);
2412
- this.watcher.on("add", (filePath) => this.handleFileChange("add", filePath)).on("change", (filePath) => this.handleFileChange("change", filePath)).on("unlink", (filePath) => this.handleFileChange("unlink", filePath)).on("addDir", (dirPath) => this.handleFileChange("addDir", dirPath)).on("unlinkDir", (dirPath) => this.handleFileChange("unlinkDir", dirPath)).on("error", (error) => this.emit("error", error)).on("ready", () => this.emit("ready"));
2413
- this.watchedPaths.add(path);
2414
- }
2415
- /**
2416
- * Stop watching a path
2417
- */
2418
- async unwatch(path) {
2419
- if (!this.watchedPaths.has(path)) {
2420
- return;
2421
- }
2422
- if (this.watcher) {
2423
- await this.watcher.unwatch(path);
2424
- }
2425
- this.watchedPaths.delete(path);
2426
- }
2427
- /**
2428
- * Stop all watching
2429
- */
2430
- async close() {
2431
- if (this.watcher) {
2432
- await this.watcher.close();
2433
- this.watcher = null;
2434
- }
2435
- this.watchedPaths.clear();
2436
- this.changeQueue.clear();
2437
- if (this.flushTimer) {
2438
- clearTimeout(this.flushTimer);
2439
- this.flushTimer = null;
2440
- }
2441
- }
2442
- /**
2443
- * Handle file change event
2444
- */
2445
- handleFileChange(type, path) {
2446
- const event = {
2447
- type,
2448
- path,
2449
- timestamp: Date.now()
2450
- };
2451
- this.changeQueue.set(path, event);
2452
- this.emit("change", event);
2453
- this.scheduleFlush();
2454
- }
2455
- /**
2456
- * Schedule flushing the change queue
2457
- */
2458
- scheduleFlush() {
2459
- if (this.flushTimer) {
2460
- return;
2461
- }
2462
- this.flushTimer = setTimeout(() => {
2463
- this.flushChanges().catch((error) => this.emit("error", error));
2464
- this.flushTimer = null;
2465
- }, this.FLUSH_DELAY);
2466
- }
2467
- /**
2468
- * Flush queued changes to indexer
2469
- */
2470
- async flushChanges() {
2471
- if (this.changeQueue.size === 0) {
2472
- return;
2473
- }
2474
- const changes = Array.from(this.changeQueue.values());
2475
- this.changeQueue.clear();
2476
- const toAdd = changes.filter((c) => c.type === "add" || c.type === "change").map((c) => c.path);
2477
- const toRemove = changes.filter((c) => c.type === "unlink").map((c) => c.path);
2478
- try {
2479
- if (toAdd.length > 0) {
2480
- await this.indexer.indexFiles(toAdd);
2481
- }
2482
- if (toRemove.length > 0) {
2483
- await this.indexer.removeFiles(toRemove);
2484
- }
2485
- this.emit("indexed", { added: toAdd.length, removed: toRemove.length });
2486
- } catch (error) {
2487
- this.emit("error", error);
2488
- }
2489
- }
2490
- /**
2491
- * Get list of watched paths
2492
- */
2493
- getWatchedPaths() {
2494
- return Array.from(this.watchedPaths);
2495
- }
2496
- /**
2497
- * Check if path is being watched
2498
- */
2499
- isWatching(path) {
2500
- return this.watchedPaths.has(path);
2501
- }
2502
- /**
2503
- * Get current change queue size
2504
- */
2505
- getQueueSize() {
2506
- return this.changeQueue.size;
2507
- }
2508
- }
2509
- let globalWatcher = null;
2510
- function getGlobalWatcher() {
2511
- if (!globalWatcher) {
2512
- const indexer = new IncrementalIndexer();
2513
- globalWatcher = new FileWatcher(indexer);
2514
- }
2515
- return globalWatcher;
2516
- }
2517
- async function closeGlobalWatcher() {
2518
- if (globalWatcher) {
2519
- await globalWatcher.close();
2520
- globalWatcher = null;
2521
- }
2522
- }
2523
-
2524
- class ActionbookEngine {
2525
- config;
2526
- queryAPI;
2527
- indexer;
2528
- watcher;
2529
- tracker;
2530
- isInitialized = false;
2531
- constructor(config = {}) {
2532
- this.config = {
2533
- cachePath: config.cachePath || "./.actionbook-cache",
2534
- watchMode: config.watchMode ?? true,
2535
- compressionEnabled: config.compressionEnabled ?? true,
2536
- maxMemoryCacheSize: config.maxMemoryCacheSize || 1e3,
2537
- logLevel: config.logLevel || "info"
2538
- };
2539
- this.queryAPI = getQueryAPI();
2540
- this.indexer = getGlobalIndexer();
2541
- this.watcher = getGlobalWatcher();
2542
- this.tracker = getGlobalTracker();
2543
- }
2544
- /**
2545
- * Initialize the engine
2546
- */
2547
- async initialize() {
2548
- if (this.isInitialized) {
2549
- return;
2550
- }
2551
- this.log("info", "Initializing Actionbook Engine...");
2552
- const index = getGlobalIndex(this.config.cachePath);
2553
- await index.compact();
2554
- if (this.config.watchMode) {
2555
- this.tracker.enableWatchMode();
2556
- this.setupWatcher();
2557
- }
2558
- this.isInitialized = true;
2559
- this.log("info", "Actionbook Engine initialized");
2560
- }
2561
- /**
2562
- * Index a single file
2563
- */
2564
- async indexFile(filePath) {
2565
- this.ensureInitialized();
2566
- this.log("debug", `Indexing file: ${filePath}`);
2567
- return this.indexer.indexFile(filePath);
2568
- }
2569
- /**
2570
- * Index multiple files
2571
- */
2572
- async indexFiles(filePaths) {
2573
- this.ensureInitialized();
2574
- this.log("info", `Indexing ${filePaths.length} files...`);
2575
- return this.indexer.indexFiles(filePaths);
2576
- }
2577
- /**
2578
- * Index a directory recursively
2579
- */
2580
- async indexDirectory(dirPath, extensions = [".ts", ".tsx", ".js", ".jsx"]) {
2581
- this.ensureInitialized();
2582
- this.log("info", `Indexing directory: ${dirPath}`);
2583
- const files = await this.getFilesRecursively(dirPath, extensions);
2584
- return this.indexFiles(files);
2585
- }
2586
- /**
2587
- * Watch a directory for changes
2588
- */
2589
- async watchDirectory(dirPath) {
2590
- this.ensureInitialized();
2591
- this.log("info", `Watching directory: ${dirPath}`);
2592
- if (!this.config.watchMode) {
2593
- this.log("warn", "Watch mode is disabled");
2594
- return;
2595
- }
2596
- this.watcher.watch(dirPath);
2597
- }
2598
- /**
2599
- * Query precomputed data
2600
- */
2601
- get query() {
2602
- return this.queryAPI;
2603
- }
2604
- /**
2605
- * Get cache statistics
2606
- */
2607
- async getCacheStats() {
2608
- const index = getGlobalIndex();
2609
- return index.getStats();
2610
- }
2611
- /**
2612
- * Get dependency graph
2613
- */
2614
- getDependencyGraph() {
2615
- return this.indexer.getDependencyGraph();
2616
- }
2617
- /**
2618
- * Get circular dependencies
2619
- */
2620
- getCircularDependencies() {
2621
- return this.tracker.detectCircularDependencies();
2622
- }
2623
- /**
2624
- * Warm up cache for specific files
2625
- */
2626
- async warmup(files) {
2627
- this.ensureInitialized();
2628
- this.log("info", `Warming up cache for ${files.length} files...`);
2629
- const index = getGlobalIndex();
2630
- await Promise.all(files.map((file) => index.warmup(file)));
2631
- }
2632
- /**
2633
- * Clear all caches
2634
- */
2635
- async clearCache() {
2636
- this.log("info", "Clearing all caches...");
2637
- const index = getGlobalIndex();
2638
- await index.clear();
2639
- }
2640
- /**
2641
- * Shutdown the engine
2642
- */
2643
- async shutdown() {
2644
- if (!this.isInitialized) {
2645
- return;
2646
- }
2647
- this.log("info", "Shutting down Actionbook Engine...");
2648
- if (this.config.watchMode) {
2649
- await closeGlobalWatcher();
2650
- }
2651
- await closeGlobalIndex();
2652
- resetGlobalIndexer();
2653
- resetGlobalTracker();
2654
- this.isInitialized = false;
2655
- this.log("info", "Actionbook Engine shutdown complete");
2656
- }
2657
- /**
2658
- * Setup file watcher
2659
- */
2660
- setupWatcher() {
2661
- this.watcher.on("change", (event) => {
2662
- this.log("debug", `File changed: ${event.path} (${event.type})`);
2663
- });
2664
- this.watcher.on("indexed", (stats) => {
2665
- this.log("info", `Indexed ${stats.added + stats.removed} files`);
2666
- });
2667
- this.watcher.on("error", (error) => {
2668
- this.log("error", "Watcher error:", error);
2669
- });
2670
- }
2671
- /**
2672
- * Get files recursively
2673
- */
2674
- async getFilesRecursively(dir, extensions) {
2675
- const files = [];
2676
- try {
2677
- const entries = await fs.readdir(dir, { withFileTypes: true });
2678
- for (const entry of entries) {
2679
- const fullPath = `${dir}/${entry.name}`;
2680
- if (entry.isDirectory()) {
2681
- if (!this.shouldIgnoreDirectory(entry.name)) {
2682
- const subFiles = await this.getFilesRecursively(fullPath, extensions);
2683
- files.push(...subFiles);
2684
- }
2685
- } else if (entry.isFile()) {
2686
- if (extensions.some((ext) => entry.name.endsWith(ext))) {
2687
- files.push(fullPath);
2688
- }
2689
- }
2690
- }
2691
- } catch (error) {
2692
- this.log("warn", `Failed to read directory ${dir}:`, error);
2693
- }
2694
- return files;
2695
- }
2696
- /**
2697
- * Check if directory should be ignored
2698
- */
2699
- shouldIgnoreDirectory(name) {
2700
- const ignorePatterns = [
2701
- "node_modules",
2702
- ".git",
2703
- "dist",
2704
- "build",
2705
- ".next",
2706
- "coverage",
2707
- ".actionbook-cache"
2708
- ];
2709
- return ignorePatterns.some((pattern) => name.includes(pattern));
2710
- }
2711
- /**
2712
- * Ensure engine is initialized
2713
- */
2714
- ensureInitialized() {
2715
- if (!this.isInitialized) {
2716
- throw new Error("Actionbook Engine not initialized. Call initialize() first.");
2717
- }
2718
- }
2719
- /**
2720
- * Log message
2721
- */
2722
- log(level, message, ...args) {
2723
- const levels = ["debug", "info", "warn", "error"];
2724
- const currentLevel = levels.indexOf(this.config.logLevel);
2725
- const messageLevel = levels.indexOf(level);
2726
- if (messageLevel >= currentLevel) {
2727
- console.log(`[Actionbook:${level.toUpperCase()}] ${message}`, ...args);
2728
- }
2729
- }
2730
- }
2731
- function createEngine(config) {
2732
- return new ActionbookEngine(config);
2733
- }
22
+ import 'i18next-fs-backend';
23
+ import './chunks/claude-config.mjs';
24
+ import './chunks/json-config.mjs';
25
+ import './chunks/fs-operations.mjs';
26
+ import 'node:crypto';
27
+ import 'node:fs/promises';
28
+ import 'tinyexec';
2734
29
 
2735
30
  const execAsync$1 = promisify(exec);
2736
31
  class BaseCodeTool {
@@ -3494,1480 +789,6 @@ function createTool(name, config) {
3494
789
  return factory.createTool(name, config);
3495
790
  }
3496
791
 
3497
- const VERSION = "1.0.0";
3498
- function isCommandGroup(entry) {
3499
- return "subcommands" in entry;
3500
- }
3501
- function parseArgs(argv) {
3502
- const args = argv.slice(2);
3503
- const command = [];
3504
- const options = {};
3505
- for (let i = 0; i < args.length; i++) {
3506
- const arg = args[i];
3507
- if (arg.startsWith("--")) {
3508
- const key = arg.slice(2);
3509
- const nextArg = args[i + 1];
3510
- if (nextArg && !nextArg.startsWith("-")) {
3511
- options[key] = nextArg;
3512
- i++;
3513
- } else {
3514
- options[key] = true;
3515
- }
3516
- } else if (arg.startsWith("-")) {
3517
- const key = arg.slice(1);
3518
- const nextArg = args[i + 1];
3519
- if (nextArg && !nextArg.startsWith("-")) {
3520
- options[key] = nextArg;
3521
- i++;
3522
- } else {
3523
- options[key] = true;
3524
- }
3525
- } else {
3526
- command.push(arg);
3527
- }
3528
- }
3529
- return { command, options };
3530
- }
3531
- function showHelp() {
3532
- console.log(`
3533
- ${ansis.bold("CCJK")} - Claude Code JK \u589E\u5F3A\u7248\u914D\u7F6E\u5DE5\u5177
3534
-
3535
- ${ansis.bold("\u7528\u6CD5:")}
3536
- ccjk [\u547D\u4EE4] [\u9009\u9879]
3537
-
3538
- ${ansis.bold("\u547D\u4EE4:")}
3539
- ${ansis.cyan("menu")} \u542F\u52A8\u4EA4\u4E92\u5F0F\u83DC\u5355
3540
- ${ansis.cyan("api setup")} \u5FEB\u901F\u914D\u7F6E API
3541
- ${ansis.cyan("api status")} \u67E5\u770B API \u914D\u7F6E\u72B6\u6001
3542
- ${ansis.cyan("api switch")} \u5207\u6362 API \u914D\u7F6E
3543
- ${ansis.cyan("api providers")} \u67E5\u770B\u53EF\u7528\u7684 API \u63D0\u4F9B\u5546
3544
- ${ansis.cyan("init")} \u521D\u59CB\u5316 CCJK \u914D\u7F6E
3545
- ${ansis.cyan("mcp list")} \u5217\u51FA\u5DF2\u5B89\u88C5\u7684 MCP \u670D\u52A1\u5668
3546
- ${ansis.cyan("mcp search")} <q> \u641C\u7D22 MCP \u670D\u52A1\u5668
3547
- ${ansis.cyan("mcp install")} <n> \u5B89\u88C5 MCP \u670D\u52A1\u5668
3548
- ${ansis.cyan("mcp uninstall")} <n> \u5378\u8F7D MCP \u670D\u52A1\u5668
3549
- ${ansis.cyan("doctor")} \u8FD0\u884C\u8BCA\u65AD\u68C0\u67E5
3550
- ${ansis.cyan("update")} \u68C0\u67E5\u5E76\u5B89\u88C5\u66F4\u65B0
3551
- ${ansis.cyan("config show")} \u663E\u793A\u5F53\u524D\u914D\u7F6E
3552
- ${ansis.cyan("config reset")} \u91CD\u7F6E\u914D\u7F6E
3553
-
3554
- ${ansis.bold("\u9009\u9879:")}
3555
- ${ansis.cyan("-h, --help")} \u663E\u793A\u5E2E\u52A9\u4FE1\u606F
3556
- ${ansis.cyan("-v, --version")} \u663E\u793A\u7248\u672C\u53F7
3557
- ${ansis.cyan("-l, --locale")} \u8BED\u8A00\u8BBE\u7F6E (\u9ED8\u8BA4: zh-CN)
3558
-
3559
- ${ansis.bold("\u793A\u4F8B:")}
3560
- ccjk \u542F\u52A8\u4EA4\u4E92\u5F0F\u83DC\u5355
3561
- ccjk api setup \u5FEB\u901F\u914D\u7F6E API
3562
- ccjk mcp install github \u5B89\u88C5 GitHub MCP \u670D\u52A1\u5668
3563
- `);
3564
- }
3565
- function getCommands() {
3566
- return {
3567
- menu: {
3568
- description: "\u542F\u52A8\u4EA4\u4E92\u5F0F\u83DC\u5355",
3569
- action: async (args) => {
3570
- const { startMenu } = await Promise.resolve().then(function () { return index$7; });
3571
- const locale = args[0] || "zh-CN";
3572
- await startMenu({ locale });
3573
- }
3574
- },
3575
- api: {
3576
- description: "API \u914D\u7F6E\u7BA1\u7406",
3577
- subcommands: {
3578
- setup: {
3579
- description: "\u5FEB\u901F\u914D\u7F6E API",
3580
- action: async (args) => {
3581
- const { quickApiSetup } = await import('./chunks/api-adapter.mjs');
3582
- const locale = args[0] || "zh-CN";
3583
- await quickApiSetup(locale);
3584
- }
3585
- },
3586
- status: {
3587
- description: "\u67E5\u770B API \u914D\u7F6E\u72B6\u6001",
3588
- action: async (args) => {
3589
- const { detectApiStatus, getApiStatusSummary } = await import('./chunks/api-adapter.mjs');
3590
- const status = await detectApiStatus();
3591
- const locale = args[0] || "zh-CN";
3592
- console.log(getApiStatusSummary(status, locale));
3593
- }
3594
- },
3595
- switch: {
3596
- description: "\u5207\u6362 API \u914D\u7F6E",
3597
- action: async () => {
3598
- const { configSwitchCommand } = await import('./chunks/config-switch.mjs');
3599
- await configSwitchCommand({ codeType: "claude-code" });
3600
- }
3601
- },
3602
- providers: {
3603
- description: "\u67E5\u770B\u53EF\u7528\u7684 API \u63D0\u4F9B\u5546",
3604
- action: async () => {
3605
- const { providerRegistry } = await import('./chunks/provider-registry.mjs');
3606
- const providers = providerRegistry.getAllMetadata();
3607
- console.log("\n\u53EF\u7528\u7684 API \u63D0\u4F9B\u5546:\n");
3608
- providers.forEach((p) => {
3609
- const popular = p.popular ? " \u2B50" : "";
3610
- console.log(` ${p.icon || "\u2022"} ${p.name}${popular}`);
3611
- console.log(` ${p.description}`);
3612
- console.log(` \u8BBE\u7F6E\u65F6\u95F4: ${p.setupTime}
3613
- `);
3614
- });
3615
- }
3616
- }
3617
- }
3618
- },
3619
- init: {
3620
- description: "\u521D\u59CB\u5316 CCJK \u914D\u7F6E",
3621
- action: async () => {
3622
- const { init } = await import('./chunks/init.mjs');
3623
- await init({});
3624
- }
3625
- },
3626
- mcp: {
3627
- description: "MCP \u670D\u52A1\u5668\u7BA1\u7406",
3628
- subcommands: {
3629
- list: {
3630
- description: "\u5217\u51FA\u5DF2\u5B89\u88C5\u7684 MCP \u670D\u52A1\u5668",
3631
- action: async () => {
3632
- const { mcpList } = await import('./chunks/mcp-market.mjs');
3633
- await mcpList();
3634
- }
3635
- },
3636
- search: {
3637
- description: "\u641C\u7D22 MCP \u670D\u52A1\u5668",
3638
- action: async (args) => {
3639
- const query = args[0];
3640
- if (!query) {
3641
- console.log(ansis.red("\u9519\u8BEF: \u8BF7\u63D0\u4F9B\u641C\u7D22\u5173\u952E\u8BCD"));
3642
- return;
3643
- }
3644
- const { mcpSearch } = await import('./chunks/mcp-market.mjs');
3645
- await mcpSearch(query);
3646
- }
3647
- },
3648
- install: {
3649
- description: "\u5B89\u88C5 MCP \u670D\u52A1\u5668",
3650
- action: async (args) => {
3651
- const name = args[0];
3652
- if (!name) {
3653
- console.log(ansis.red("\u9519\u8BEF: \u8BF7\u63D0\u4F9B MCP \u670D\u52A1\u5668\u540D\u79F0"));
3654
- return;
3655
- }
3656
- const { mcpInstall } = await import('./chunks/mcp-market.mjs');
3657
- await mcpInstall(name);
3658
- }
3659
- },
3660
- uninstall: {
3661
- description: "\u5378\u8F7D MCP \u670D\u52A1\u5668",
3662
- action: async (args) => {
3663
- const name = args[0];
3664
- if (!name) {
3665
- console.log(ansis.red("\u9519\u8BEF: \u8BF7\u63D0\u4F9B MCP \u670D\u52A1\u5668\u540D\u79F0"));
3666
- return;
3667
- }
3668
- const { mcpUninstall } = await import('./chunks/mcp-market.mjs');
3669
- await mcpUninstall(name);
3670
- }
3671
- }
3672
- }
3673
- },
3674
- doctor: {
3675
- description: "\u8FD0\u884C\u8BCA\u65AD\u68C0\u67E5",
3676
- action: async () => {
3677
- const { doctor } = await import('./chunks/doctor.mjs');
3678
- await doctor();
3679
- }
3680
- },
3681
- update: {
3682
- description: "\u68C0\u67E5\u5E76\u5B89\u88C5\u66F4\u65B0",
3683
- action: async () => {
3684
- const { update } = await import('./chunks/update.mjs');
3685
- await update({});
3686
- }
3687
- },
3688
- config: {
3689
- description: "\u914D\u7F6E\u7BA1\u7406",
3690
- subcommands: {
3691
- show: {
3692
- description: "\u663E\u793A\u5F53\u524D\u914D\u7F6E",
3693
- action: async () => {
3694
- const { readZcfConfig } = await import('./chunks/ccjk-config.mjs');
3695
- const config = readZcfConfig();
3696
- console.log(JSON.stringify(config, null, 2));
3697
- }
3698
- },
3699
- reset: {
3700
- description: "\u91CD\u7F6E\u914D\u7F6E",
3701
- action: async () => {
3702
- const { confirm } = await inquirer.prompt([
3703
- {
3704
- type: "confirm",
3705
- name: "confirm",
3706
- message: "\u786E\u5B9A\u8981\u91CD\u7F6E\u6240\u6709\u914D\u7F6E\u5417\uFF1F",
3707
- default: false
3708
- }
3709
- ]);
3710
- if (!confirm) {
3711
- console.log("\u5DF2\u53D6\u6D88");
3712
- return;
3713
- }
3714
- console.log("\u914D\u7F6E\u5DF2\u91CD\u7F6E");
3715
- }
3716
- }
3717
- }
3718
- }
3719
- };
3720
- }
3721
- async function defaultAction() {
3722
- const { needsApiSetup, quickApiSetup } = await import('./chunks/api-adapter.mjs');
3723
- if (await needsApiSetup()) {
3724
- console.log("");
3725
- console.log(ansis.yellow("\u26A0\uFE0F \u68C0\u6D4B\u5230 API \u5C1A\u672A\u914D\u7F6E"));
3726
- console.log(ansis.dim("\u5EFA\u8BAE\u5148\u914D\u7F6E API \u4EE5\u4F7F\u7528 Claude Code"));
3727
- console.log("");
3728
- const { action } = await inquirer.prompt([
3729
- {
3730
- type: "list",
3731
- name: "action",
3732
- message: "\u8BF7\u9009\u62E9:",
3733
- choices: [
3734
- { name: "\u26A1 \u5FEB\u901F\u914D\u7F6E API", value: "setup" },
3735
- { name: "\u{1F4CB} \u8FDB\u5165\u4E3B\u83DC\u5355", value: "menu" },
3736
- { name: "\u274C \u9000\u51FA", value: "exit" }
3737
- ]
3738
- }
3739
- ]);
3740
- if (action === "setup") {
3741
- await quickApiSetup("zh-CN");
3742
- } else if (action === "menu") {
3743
- const { startMenu } = await Promise.resolve().then(function () { return index$7; });
3744
- await startMenu();
3745
- }
3746
- } else {
3747
- const { startMenu } = await Promise.resolve().then(function () { return index$7; });
3748
- await startMenu();
3749
- }
3750
- }
3751
- async function runCli() {
3752
- const { command, options } = parseArgs(process__default.argv);
3753
- if (options.help || options.h) {
3754
- showHelp();
3755
- return;
3756
- }
3757
- if (options.version || options.v) {
3758
- console.log(`ccjk v${VERSION}`);
3759
- return;
3760
- }
3761
- const commands = getCommands();
3762
- if (command.length === 0) {
3763
- await defaultAction();
3764
- return;
3765
- }
3766
- const mainCmd = command[0];
3767
- const cmdEntry = commands[mainCmd];
3768
- if (!cmdEntry) {
3769
- console.log(ansis.red(`\u9519\u8BEF: \u672A\u77E5\u547D\u4EE4 '${mainCmd}'`));
3770
- console.log(ansis.dim("\u8FD0\u884C ccjk --help \u67E5\u770B\u53EF\u7528\u547D\u4EE4"));
3771
- process__default.exit(1);
3772
- }
3773
- if (isCommandGroup(cmdEntry)) {
3774
- const subCmd = command[1];
3775
- if (!subCmd) {
3776
- console.log(ansis.yellow(`${mainCmd} \u5B50\u547D\u4EE4:`));
3777
- Object.entries(cmdEntry.subcommands).forEach(([name, def]) => {
3778
- console.log(` ${ansis.cyan(name)} ${def.description}`);
3779
- });
3780
- return;
3781
- }
3782
- const subCmdDef = cmdEntry.subcommands[subCmd];
3783
- if (!subCmdDef) {
3784
- console.log(ansis.red(`\u9519\u8BEF: \u672A\u77E5\u5B50\u547D\u4EE4 '${mainCmd} ${subCmd}'`));
3785
- console.log(ansis.dim(`\u8FD0\u884C ccjk ${mainCmd} \u67E5\u770B\u53EF\u7528\u5B50\u547D\u4EE4`));
3786
- process__default.exit(1);
3787
- }
3788
- await subCmdDef.action(command.slice(2));
3789
- } else {
3790
- await cmdEntry.action(command.slice(1));
3791
- }
3792
- }
3793
-
3794
- const cli = {
3795
- __proto__: null,
3796
- runCli: runCli
3797
- };
3798
-
3799
- const coreGroup = {
3800
- id: "core",
3801
- label: {
3802
- "en": "Core Features",
3803
- "zh-CN": "\u6838\u5FC3\u529F\u80FD"
3804
- },
3805
- icon: "\u2B50",
3806
- priority: "core",
3807
- items: [
3808
- {
3809
- id: "api-config",
3810
- label: {
3811
- "en": "\u{1F511} API Configuration",
3812
- "zh-CN": "\u{1F511} API \u914D\u7F6E\u7BA1\u7406"
3813
- },
3814
- description: {
3815
- "en": "One-click setup for AI providers (Anthropic, OpenAI, Azure, etc.)",
3816
- "zh-CN": "\u4E00\u952E\u914D\u7F6E AI \u670D\u52A1\u63D0\u4F9B\u5546\uFF08Anthropic\u3001OpenAI\u3001Azure \u7B49\uFF09"
3817
- },
3818
- shortcut: "a",
3819
- action: {
3820
- type: "command",
3821
- handler: "api-config"
3822
- },
3823
- priority: "core"
3824
- }
3825
- ]
3826
- };
3827
- const featuresGroup = {
3828
- id: "features",
3829
- label: {
3830
- "en": "Features",
3831
- "zh-CN": "\u529F\u80FD\u6A21\u5757"
3832
- },
3833
- icon: "\u{1F6E0}\uFE0F",
3834
- priority: "feature",
3835
- items: [
3836
- {
3837
- id: "skills",
3838
- label: {
3839
- "en": "\u{1F4DA} Skills Management",
3840
- "zh-CN": "\u{1F4DA} Skills \u7BA1\u7406"
3841
- },
3842
- description: {
3843
- "en": "Manage and configure AI skill templates",
3844
- "zh-CN": "\u7BA1\u7406\u548C\u914D\u7F6E AI \u6280\u80FD\u6A21\u677F"
3845
- },
3846
- shortcut: "s",
3847
- action: {
3848
- type: "command",
3849
- handler: "skills"
3850
- },
3851
- priority: "feature"
3852
- },
3853
- {
3854
- id: "mcp",
3855
- label: {
3856
- "en": "\u{1F50C} MCP Servers",
3857
- "zh-CN": "\u{1F50C} MCP \u670D\u52A1\u5668"
3858
- },
3859
- description: {
3860
- "en": "Configure Model Context Protocol servers",
3861
- "zh-CN": "\u914D\u7F6E Model Context Protocol \u670D\u52A1\u5668"
3862
- },
3863
- shortcut: "m",
3864
- action: {
3865
- type: "command",
3866
- handler: "mcp"
3867
- },
3868
- priority: "feature"
3869
- },
3870
- {
3871
- id: "session",
3872
- label: {
3873
- "en": "\u{1F4BE} Session Management",
3874
- "zh-CN": "\u{1F4BE} Session \u7BA1\u7406"
3875
- },
3876
- description: {
3877
- "en": "Manage session history and recovery",
3878
- "zh-CN": "\u7BA1\u7406\u4F1A\u8BDD\u5386\u53F2\u548C\u6062\u590D"
3879
- },
3880
- shortcut: "e",
3881
- action: {
3882
- type: "command",
3883
- handler: "session"
3884
- },
3885
- priority: "feature"
3886
- }
3887
- ]
3888
- };
3889
- const settingsGroup$1 = {
3890
- id: "settings",
3891
- label: {
3892
- "en": "Settings",
3893
- "zh-CN": "\u7CFB\u7EDF\u8BBE\u7F6E"
3894
- },
3895
- icon: "\u2699\uFE0F",
3896
- priority: "setting",
3897
- items: [
3898
- {
3899
- id: "settings",
3900
- label: {
3901
- "en": "\u2699\uFE0F Settings",
3902
- "zh-CN": "\u2699\uFE0F \u8BBE\u7F6E"
3903
- },
3904
- description: {
3905
- "en": "Configure CCJK global settings",
3906
- "zh-CN": "\u914D\u7F6E CCJK \u5168\u5C40\u8BBE\u7F6E"
3907
- },
3908
- shortcut: "t",
3909
- action: {
3910
- type: "command",
3911
- handler: "settings"
3912
- },
3913
- priority: "setting"
3914
- }
3915
- ]
3916
- };
3917
- const footerItems = [
3918
- {
3919
- id: "help",
3920
- label: {
3921
- "en": "\u2753 Help",
3922
- "zh-CN": "\u2753 \u5E2E\u52A9"
3923
- },
3924
- description: {
3925
- "en": "View help information and documentation",
3926
- "zh-CN": "\u67E5\u770B\u5E2E\u52A9\u4FE1\u606F\u548C\u6587\u6863"
3927
- },
3928
- shortcut: "h",
3929
- action: {
3930
- type: "command",
3931
- handler: "help"
3932
- },
3933
- priority: "help"
3934
- },
3935
- {
3936
- id: "exit",
3937
- label: {
3938
- "en": "\u{1F6AA} Exit",
3939
- "zh-CN": "\u{1F6AA} \u9000\u51FA"
3940
- },
3941
- description: {
3942
- "en": "Exit CCJK menu",
3943
- "zh-CN": "\u9000\u51FA CCJK \u83DC\u5355"
3944
- },
3945
- shortcut: "q",
3946
- action: {
3947
- type: "command",
3948
- handler: "exit"
3949
- },
3950
- priority: "help"
3951
- }
3952
- ];
3953
- const mainMenuConfig$1 = {
3954
- title: {
3955
- "en": "CCJK - Claude Code Chinese Enhanced",
3956
- "zh-CN": "CCJK - Claude Code \u4E2D\u6587\u589E\u5F3A\u7248"
3957
- },
3958
- groups: [coreGroup, featuresGroup, settingsGroup$1],
3959
- footer: footerItems
3960
- };
3961
-
3962
- const apiConfigMenu = {
3963
- id: "api-config",
3964
- label: {
3965
- "en": "API Configuration",
3966
- "zh-CN": "API \u914D\u7F6E\u7BA1\u7406"
3967
- },
3968
- description: {
3969
- "en": "One-click API setup, start using Claude",
3970
- "zh-CN": "\u4E00\u952E\u914D\u7F6E API\uFF0C\u5F00\u59CB\u4F7F\u7528 Claude"
3971
- },
3972
- icon: "\u{1F511}",
3973
- shortcut: "1",
3974
- priority: "core",
3975
- submenu: [
3976
- {
3977
- id: "api-official",
3978
- label: {
3979
- "en": "Use Official Login (No API needed)",
3980
- "zh-CN": "\u4F7F\u7528\u5B98\u65B9\u767B\u5F55\uFF08\u4E0D\u914D\u7F6E API\uFF09"
3981
- },
3982
- description: {
3983
- "en": "Recommended for new users",
3984
- "zh-CN": "\u63A8\u8350\u65B0\u7528\u6237\u4F7F\u7528"
3985
- },
3986
- icon: "\u2728",
3987
- shortcut: "1",
3988
- action: { type: "command", handler: "api:official" }
3989
- },
3990
- {
3991
- id: "api-custom",
3992
- label: {
3993
- "en": "Custom API Configuration",
3994
- "zh-CN": "\u81EA\u5B9A\u4E49 API \u914D\u7F6E"
3995
- },
3996
- description: {
3997
- "en": "Configure API Key and URL",
3998
- "zh-CN": "\u914D\u7F6E API Key \u548C URL"
3999
- },
4000
- icon: "\u2699\uFE0F",
4001
- shortcut: "2",
4002
- action: { type: "command", handler: "api:custom" }
4003
- },
4004
- {
4005
- id: "api-ccr",
4006
- label: {
4007
- "en": "Use CCR Proxy",
4008
- "zh-CN": "\u4F7F\u7528 CCR \u4EE3\u7406"
4009
- },
4010
- description: {
4011
- "en": "Access API through proxy",
4012
- "zh-CN": "\u901A\u8FC7\u4EE3\u7406\u8BBF\u95EE API"
4013
- },
4014
- icon: "\u{1F310}",
4015
- shortcut: "3",
4016
- action: { type: "command", handler: "api:ccr" }
4017
- },
4018
- {
4019
- id: "api-switch",
4020
- label: {
4021
- "en": "Switch API Configuration",
4022
- "zh-CN": "\u5207\u6362 API \u914D\u7F6E"
4023
- },
4024
- description: {
4025
- "en": "Switch between configurations",
4026
- "zh-CN": "\u5728\u591A\u4E2A\u914D\u7F6E\u95F4\u5207\u6362"
4027
- },
4028
- icon: "\u{1F504}",
4029
- shortcut: "4",
4030
- action: { type: "command", handler: "api:switch" }
4031
- },
4032
- {
4033
- id: "api-status",
4034
- label: {
4035
- "en": "View Current Configuration",
4036
- "zh-CN": "\u67E5\u770B\u5F53\u524D\u914D\u7F6E"
4037
- },
4038
- description: {
4039
- "en": "Show current API settings",
4040
- "zh-CN": "\u663E\u793A\u5F53\u524D API \u8BBE\u7F6E"
4041
- },
4042
- icon: "\u{1F4CB}",
4043
- shortcut: "5",
4044
- action: { type: "command", handler: "api:status" }
4045
- },
4046
- {
4047
- id: "api-skip",
4048
- label: {
4049
- "en": "Skip API Configuration",
4050
- "zh-CN": "\u8DF3\u8FC7 API \u914D\u7F6E"
4051
- },
4052
- description: {
4053
- "en": "Configure later",
4054
- "zh-CN": "\u7A0D\u540E\u914D\u7F6E"
4055
- },
4056
- icon: "\u23ED\uFE0F",
4057
- shortcut: "6",
4058
- action: { type: "function", handler: async () => {
4059
- } }
4060
- }
4061
- ]
4062
- };
4063
- const quickStartGroup = {
4064
- id: "quick-start",
4065
- label: {
4066
- "en": "Quick Start",
4067
- "zh-CN": "\u5FEB\u901F\u5F00\u59CB"
4068
- },
4069
- icon: "\u{1F680}",
4070
- priority: "feature",
4071
- items: [
4072
- {
4073
- id: "init-project",
4074
- label: {
4075
- "en": "Initialize Project",
4076
- "zh-CN": "\u521D\u59CB\u5316\u9879\u76EE"
4077
- },
4078
- description: {
4079
- "en": "Configure CCJK for current project",
4080
- "zh-CN": "\u4E3A\u5F53\u524D\u9879\u76EE\u914D\u7F6E CCJK"
4081
- },
4082
- icon: "\u{1F4E6}",
4083
- action: { type: "command", handler: "ccjk:init" }
4084
- },
4085
- {
4086
- id: "install-skills",
4087
- label: {
4088
- "en": "Install Skills",
4089
- "zh-CN": "\u5B89\u88C5\u6280\u80FD"
4090
- },
4091
- description: {
4092
- "en": "One-click install common skill packs",
4093
- "zh-CN": "\u4E00\u952E\u5B89\u88C5\u5E38\u7528\u6280\u80FD\u5305"
4094
- },
4095
- icon: "\u{1F3AF}",
4096
- action: { type: "command", handler: "ccjk:skills" }
4097
- },
4098
- {
4099
- id: "setup-mcp",
4100
- label: {
4101
- "en": "Setup MCP Services",
4102
- "zh-CN": "\u914D\u7F6E MCP \u670D\u52A1"
4103
- },
4104
- description: {
4105
- "en": "Configure MCP service connections",
4106
- "zh-CN": "\u8BBE\u7F6E MCP \u670D\u52A1\u8FDE\u63A5"
4107
- },
4108
- icon: "\u{1F50C}",
4109
- action: { type: "command", handler: "ccjk:mcp" }
4110
- }
4111
- ]
4112
- };
4113
- const projectManagementGroup = {
4114
- id: "project-management",
4115
- label: {
4116
- "en": "Project Management",
4117
- "zh-CN": "\u9879\u76EE\u7BA1\u7406"
4118
- },
4119
- icon: "\u{1F6E0}\uFE0F",
4120
- priority: "feature",
4121
- items: [
4122
- {
4123
- id: "skills-management",
4124
- label: {
4125
- "en": "Skills Management",
4126
- "zh-CN": "Skills \u6280\u80FD\u7BA1\u7406"
4127
- },
4128
- description: {
4129
- "en": "Install, view, remove skills",
4130
- "zh-CN": "\u5B89\u88C5\u3001\u67E5\u770B\u3001\u5220\u9664\u6280\u80FD"
4131
- },
4132
- icon: "\u{1F3AF}",
4133
- action: { type: "command", handler: "ccjk:skills" }
4134
- },
4135
- {
4136
- id: "mcp-management",
4137
- label: {
4138
- "en": "MCP Services",
4139
- "zh-CN": "MCP \u670D\u52A1\u7BA1\u7406"
4140
- },
4141
- description: {
4142
- "en": "Configure, test MCP services",
4143
- "zh-CN": "\u914D\u7F6E\u3001\u6D4B\u8BD5 MCP \u670D\u52A1"
4144
- },
4145
- icon: "\u{1F50C}",
4146
- action: { type: "command", handler: "ccjk:mcp" }
4147
- },
4148
- {
4149
- id: "agents-management",
4150
- label: {
4151
- "en": "Agents Management",
4152
- "zh-CN": "Agents \u4EE3\u7406\u7BA1\u7406"
4153
- },
4154
- description: {
4155
- "en": "Create, edit AI agents",
4156
- "zh-CN": "\u521B\u5EFA\u3001\u7F16\u8F91 AI \u4EE3\u7406"
4157
- },
4158
- icon: "\u{1F916}",
4159
- action: { type: "command", handler: "ccjk:agents" }
4160
- },
4161
- {
4162
- id: "hooks-management",
4163
- label: {
4164
- "en": "Hooks Management",
4165
- "zh-CN": "Hooks \u94A9\u5B50\u7BA1\u7406"
4166
- },
4167
- description: {
4168
- "en": "Configure Git hooks",
4169
- "zh-CN": "\u914D\u7F6E Git \u94A9\u5B50"
4170
- },
4171
- icon: "\u{1FA9D}",
4172
- action: { type: "command", handler: "ccjk:hooks" }
4173
- }
4174
- ]
4175
- };
4176
- const sessionManagementGroup = {
4177
- id: "session-management",
4178
- label: {
4179
- "en": "Session & Context",
4180
- "zh-CN": "\u4F1A\u8BDD\u7BA1\u7406"
4181
- },
4182
- icon: "\u{1F4AC}",
4183
- priority: "feature",
4184
- items: [
4185
- {
4186
- id: "session-create",
4187
- label: {
4188
- "en": "Create New Session",
4189
- "zh-CN": "\u521B\u5EFA\u65B0\u4F1A\u8BDD"
4190
- },
4191
- description: {
4192
- "en": "Start a new conversation session",
4193
- "zh-CN": "\u5F00\u59CB\u65B0\u7684\u5BF9\u8BDD\u4F1A\u8BDD"
4194
- },
4195
- icon: "\u2795",
4196
- action: { type: "command", handler: "session:create" }
4197
- },
4198
- {
4199
- id: "session-restore",
4200
- label: {
4201
- "en": "Restore Session",
4202
- "zh-CN": "\u6062\u590D\u4F1A\u8BDD"
4203
- },
4204
- description: {
4205
- "en": "Continue from history",
4206
- "zh-CN": "\u4ECE\u5386\u53F2\u4F1A\u8BDD\u7EE7\u7EED"
4207
- },
4208
- icon: "\u{1F504}",
4209
- action: { type: "command", handler: "session:restore" }
4210
- },
4211
- {
4212
- id: "session-list",
4213
- label: {
4214
- "en": "Session List",
4215
- "zh-CN": "\u4F1A\u8BDD\u5217\u8868"
4216
- },
4217
- description: {
4218
- "en": "View all saved sessions",
4219
- "zh-CN": "\u67E5\u770B\u6240\u6709\u4FDD\u5B58\u7684\u4F1A\u8BDD"
4220
- },
4221
- icon: "\u{1F4CB}",
4222
- action: { type: "command", handler: "session:list" }
4223
- },
4224
- {
4225
- id: "context-management",
4226
- label: {
4227
- "en": "Context Management",
4228
- "zh-CN": "\u4E0A\u4E0B\u6587\u7BA1\u7406"
4229
- },
4230
- description: {
4231
- "en": "Compact, clean context",
4232
- "zh-CN": "\u538B\u7F29\u3001\u6E05\u7406\u4E0A\u4E0B\u6587"
4233
- },
4234
- icon: "\u{1F4CA}",
4235
- action: { type: "command", handler: "context:manage" }
4236
- },
4237
- {
4238
- id: "session-export",
4239
- label: {
4240
- "en": "Export Session",
4241
- "zh-CN": "\u5BFC\u51FA\u4F1A\u8BDD"
4242
- },
4243
- description: {
4244
- "en": "Export as Markdown",
4245
- "zh-CN": "\u5BFC\u51FA\u4E3A Markdown"
4246
- },
4247
- icon: "\u{1F4E4}",
4248
- action: { type: "command", handler: "session:export" }
4249
- },
4250
- {
4251
- id: "session-cleanup",
4252
- label: {
4253
- "en": "Cleanup Cache",
4254
- "zh-CN": "\u6E05\u7406\u7F13\u5B58"
4255
- },
4256
- description: {
4257
- "en": "Clean session cache data",
4258
- "zh-CN": "\u6E05\u7406\u4F1A\u8BDD\u7F13\u5B58\u6570\u636E"
4259
- },
4260
- icon: "\u{1F9F9}",
4261
- action: { type: "command", handler: "session:cleanup" }
4262
- }
4263
- ]
4264
- };
4265
- const settingsGroup = {
4266
- id: "settings",
4267
- label: {
4268
- "en": "Settings",
4269
- "zh-CN": "\u7CFB\u7EDF\u8BBE\u7F6E"
4270
- },
4271
- icon: "\u2699\uFE0F",
4272
- priority: "setting",
4273
- items: [
4274
- {
4275
- id: "language-setting",
4276
- label: {
4277
- "en": "Language",
4278
- "zh-CN": "\u8BED\u8A00\u8BBE\u7F6E"
4279
- },
4280
- description: {
4281
- "en": "Switch interface language",
4282
- "zh-CN": "\u5207\u6362\u754C\u9762\u8BED\u8A00 (en/zh-CN)"
4283
- },
4284
- icon: "\u{1F310}",
4285
- action: { type: "command", handler: "settings:language" }
4286
- },
4287
- {
4288
- id: "advanced-setting",
4289
- label: {
4290
- "en": "Advanced Settings",
4291
- "zh-CN": "\u9AD8\u7EA7\u8BBE\u7F6E"
4292
- },
4293
- description: {
4294
- "en": "Debug, logs, performance",
4295
- "zh-CN": "\u8C03\u8BD5\u3001\u65E5\u5FD7\u3001\u6027\u80FD"
4296
- },
4297
- icon: "\u{1F527}",
4298
- action: { type: "command", handler: "settings:advanced" }
4299
- },
4300
- {
4301
- id: "reset-setting",
4302
- label: {
4303
- "en": "Reset Settings",
4304
- "zh-CN": "\u91CD\u7F6E\u8BBE\u7F6E"
4305
- },
4306
- description: {
4307
- "en": "Restore default settings",
4308
- "zh-CN": "\u6062\u590D\u9ED8\u8BA4\u8BBE\u7F6E"
4309
- },
4310
- icon: "\u21A9\uFE0F",
4311
- action: { type: "command", handler: "settings:reset" }
4312
- }
4313
- ]
4314
- };
4315
- const helpGroup = {
4316
- id: "help",
4317
- label: {
4318
- "en": "Help & Documentation",
4319
- "zh-CN": "\u5E2E\u52A9\u6587\u6863"
4320
- },
4321
- icon: "\u{1F4DA}",
4322
- priority: "help",
4323
- items: [
4324
- {
4325
- id: "command-reference",
4326
- label: {
4327
- "en": "Command Reference",
4328
- "zh-CN": "\u547D\u4EE4\u53C2\u8003"
4329
- },
4330
- description: {
4331
- "en": "Detailed command documentation",
4332
- "zh-CN": "\u6240\u6709\u547D\u4EE4\u7684\u8BE6\u7EC6\u8BF4\u660E"
4333
- },
4334
- icon: "\u{1F4D6}",
4335
- action: { type: "command", handler: "help:commands" }
4336
- },
4337
- {
4338
- id: "quick-tutorial",
4339
- label: {
4340
- "en": "Quick Tutorial",
4341
- "zh-CN": "\u5FEB\u901F\u6559\u7A0B"
4342
- },
4343
- description: {
4344
- "en": "5-minute getting started guide",
4345
- "zh-CN": "5 \u5206\u949F\u4E0A\u624B\u6307\u5357"
4346
- },
4347
- icon: "\u{1F393}",
4348
- action: { type: "command", handler: "help:tutorial" }
4349
- },
4350
- {
4351
- id: "faq",
4352
- label: {
4353
- "en": "FAQ",
4354
- "zh-CN": "\u5E38\u89C1\u95EE\u9898"
4355
- },
4356
- description: {
4357
- "en": "FAQ and troubleshooting",
4358
- "zh-CN": "FAQ \u548C\u6545\u969C\u6392\u9664"
4359
- },
4360
- icon: "\u2753",
4361
- action: { type: "command", handler: "help:faq" }
4362
- },
4363
- {
4364
- id: "about",
4365
- label: {
4366
- "en": "About CCJK",
4367
- "zh-CN": "\u5173\u4E8E CCJK"
4368
- },
4369
- description: {
4370
- "en": "Version info and credits",
4371
- "zh-CN": "\u7248\u672C\u4FE1\u606F\u548C\u81F4\u8C22"
4372
- },
4373
- icon: "\u2139\uFE0F",
4374
- action: { type: "command", handler: "help:about" }
4375
- }
4376
- ]
4377
- };
4378
- const mainMenuConfig = {
4379
- title: {
4380
- "en": "CCJK - Claude Code JK",
4381
- "zh-CN": "CCJK - Claude Code JK"
4382
- },
4383
- groups: [
4384
- // 核心功能组(API 配置在第一位)
4385
- {
4386
- id: "core",
4387
- label: {
4388
- "en": "Core Features",
4389
- "zh-CN": "\u6838\u5FC3\u529F\u80FD"
4390
- },
4391
- icon: "\u2605",
4392
- priority: "core",
4393
- items: [apiConfigMenu]
4394
- },
4395
- // 功能模块组
4396
- quickStartGroup,
4397
- projectManagementGroup,
4398
- sessionManagementGroup,
4399
- settingsGroup,
4400
- helpGroup
4401
- ],
4402
- footer: [
4403
- {
4404
- id: "exit",
4405
- label: {
4406
- "en": "Exit",
4407
- "zh-CN": "\u9000\u51FA"
4408
- },
4409
- icon: "\u{1F6AA}",
4410
- shortcut: "q",
4411
- action: { type: "function", handler: async () => process.exit(0) }
4412
- }
4413
- ]
4414
- };
4415
- function getLocalizedLabel(label, locale = "zh-CN") {
4416
- if (typeof label === "string") {
4417
- return label;
4418
- }
4419
- return label[locale] || label.en || Object.values(label)[0] || "";
4420
- }
4421
-
4422
- class MenuRenderer {
4423
- locale;
4424
- showStatusBar;
4425
- showBreadcrumb;
4426
- showShortcuts;
4427
- constructor(options = {}) {
4428
- this.locale = options.locale || "zh-CN";
4429
- this.showStatusBar = options.showStatusBar ?? true;
4430
- this.showBreadcrumb = options.showBreadcrumb ?? true;
4431
- this.showShortcuts = options.showShortcuts ?? true;
4432
- }
4433
- /**
4434
- * 渲染状态栏
4435
- */
4436
- renderStatusBar(projectInfo, apiStatus) {
4437
- if (!this.showStatusBar)
4438
- return "";
4439
- const lines = [];
4440
- const width = 60;
4441
- const border = "\u2500".repeat(width - 2);
4442
- lines.push(ansis.dim(`\u256D${border}\u256E`));
4443
- if (projectInfo) {
4444
- const projectLine = [
4445
- `Project: ${ansis.cyan(projectInfo.name || "Unknown")}`,
4446
- projectInfo.type ? `Type: ${ansis.yellow(projectInfo.type)}` : "",
4447
- projectInfo.language ? `Lang: ${ansis.green(projectInfo.language)}` : ""
4448
- ].filter(Boolean).join(" | ");
4449
- lines.push(ansis.dim("\u2502 ") + projectLine.padEnd(width - 4) + ansis.dim(" \u2502"));
4450
- }
4451
- if (apiStatus) {
4452
- const statusIcon = apiStatus.configured ? ansis.green("\u2713") : ansis.yellow("\u26A0");
4453
- const statusText = apiStatus.configured ? `API: ${statusIcon} ${apiStatus.mode || "configured"}` : `API: ${statusIcon} \u672A\u914D\u7F6E`;
4454
- lines.push(ansis.dim("\u2502 ") + statusText.padEnd(width - 4) + ansis.dim(" \u2502"));
4455
- }
4456
- lines.push(ansis.dim(`\u2570${border}\u256F`));
4457
- return `${lines.join("\n")}
4458
- `;
4459
- }
4460
- /**
4461
- * 渲染面包屑导航
4462
- */
4463
- renderBreadcrumb(path) {
4464
- if (!this.showBreadcrumb || path.length === 0)
4465
- return "";
4466
- const breadcrumb = ["Home", ...path].join(" > ");
4467
- return `${ansis.dim(`\u{1F4CD} ${breadcrumb}`)}
4468
-
4469
- `;
4470
- }
4471
- /**
4472
- * 渲染菜单项
4473
- */
4474
- formatMenuItem(item, index) {
4475
- const icon = item.icon || "";
4476
- const label = getLocalizedLabel(item.label, this.locale);
4477
- const description = item.description ? ansis.dim(getLocalizedLabel(item.description, this.locale)) : "";
4478
- item.shortcut ? `${item.shortcut}.` : `${index + 1}.`;
4479
- const mainText = `${icon} ${label}`.trim();
4480
- const padding = " ".repeat(Math.max(1, 30 - mainText.length));
4481
- return `${mainText}${padding}${description}`;
4482
- }
4483
- /**
4484
- * 渲染菜单组分隔符
4485
- */
4486
- renderGroupSeparator(group) {
4487
- const label = getLocalizedLabel(group.label, this.locale);
4488
- const icon = group.icon || "\u25C6";
4489
- const separator = "\u2500".repeat(50);
4490
- if (group.priority === "core") {
4491
- return `
4492
- ${ansis.bold("\u2605")} ${ansis.bold(label)} ${ansis.dim(separator)}`;
4493
- }
4494
- return `
4495
- ${icon} ${label} ${ansis.dim(separator)}`;
4496
- }
4497
- /**
4498
- * 渲染主菜单
4499
- */
4500
- async renderMainMenu(config, projectInfo, apiStatus) {
4501
- console.clear();
4502
- console.log(this.renderStatusBar(projectInfo, apiStatus));
4503
- const choices = [];
4504
- for (const group of config.groups) {
4505
- choices.push(new inquirer.Separator(this.renderGroupSeparator(group)));
4506
- for (let i = 0; i < group.items.length; i++) {
4507
- const item = group.items[i];
4508
- if (item.condition) {
4509
- const visible = await item.condition();
4510
- if (!visible)
4511
- continue;
4512
- }
4513
- choices.push({
4514
- name: this.formatMenuItem(item, i),
4515
- value: item,
4516
- short: getLocalizedLabel(item.label, this.locale)
4517
- });
4518
- }
4519
- }
4520
- choices.push(new inquirer.Separator(ansis.dim(`
4521
- ${"\u2500".repeat(55)}`)));
4522
- if (config.footer) {
4523
- for (const item of config.footer) {
4524
- choices.push({
4525
- name: this.formatMenuItem(item, 0),
4526
- value: item,
4527
- short: getLocalizedLabel(item.label, this.locale)
4528
- });
4529
- }
4530
- }
4531
- if (this.showShortcuts) {
4532
- const shortcuts = ansis.dim(" q. \u9000\u51FA h. \u5E2E\u52A9 /. \u641C\u7D22");
4533
- choices.push(new inquirer.Separator(shortcuts));
4534
- }
4535
- const { selection } = await inquirer.prompt([
4536
- {
4537
- type: "list",
4538
- name: "selection",
4539
- message: this.locale === "zh-CN" ? "\u8BF7\u9009\u62E9\u64CD\u4F5C:" : "Select an action:",
4540
- choices,
4541
- pageSize: 20,
4542
- loop: false
4543
- }
4544
- ]);
4545
- return {
4546
- item: selection,
4547
- action: "select"
4548
- };
4549
- }
4550
- /**
4551
- * 渲染子菜单
4552
- */
4553
- async renderSubmenu(item, breadcrumb) {
4554
- if (!item.submenu || item.submenu.length === 0) {
4555
- return { item, action: "select" };
4556
- }
4557
- console.clear();
4558
- console.log(this.renderBreadcrumb(breadcrumb));
4559
- const title = getLocalizedLabel(item.label, this.locale);
4560
- const description = item.description ? getLocalizedLabel(item.description, this.locale) : "";
4561
- console.log(`${item.icon || ""} ${ansis.bold(title)}`);
4562
- if (description) {
4563
- console.log(ansis.dim(description));
4564
- }
4565
- console.log("");
4566
- const choices = [];
4567
- for (let i = 0; i < item.submenu.length; i++) {
4568
- const subItem = item.submenu[i];
4569
- if (subItem.condition) {
4570
- const visible = await subItem.condition();
4571
- if (!visible)
4572
- continue;
4573
- }
4574
- choices.push({
4575
- name: this.formatMenuItem(subItem, i),
4576
- value: subItem,
4577
- short: getLocalizedLabel(subItem.label, this.locale)
4578
- });
4579
- }
4580
- choices.push(new inquirer.Separator(ansis.dim(`
4581
- ${"\u2500".repeat(55)}`)));
4582
- choices.push({
4583
- name: `${ansis.dim("\u2190")} ${this.locale === "zh-CN" ? "\u8FD4\u56DE\u4E3B\u83DC\u5355" : "Back to main menu"}`,
4584
- value: "back",
4585
- short: "Back"
4586
- });
4587
- const { selection } = await inquirer.prompt([
4588
- {
4589
- type: "list",
4590
- name: "selection",
4591
- message: this.locale === "zh-CN" ? "\u8BF7\u9009\u62E9:" : "Select:",
4592
- choices,
4593
- pageSize: 15,
4594
- loop: false
4595
- }
4596
- ]);
4597
- if (selection === "back") {
4598
- return { item, action: "back" };
4599
- }
4600
- return {
4601
- item: selection,
4602
- action: "select"
4603
- };
4604
- }
4605
- /**
4606
- * 设置语言
4607
- */
4608
- setLocale(locale) {
4609
- this.locale = locale;
4610
- }
4611
- /**
4612
- * 获取当前语言
4613
- */
4614
- getLocale() {
4615
- return this.locale;
4616
- }
4617
- }
4618
- function createMenuRenderer(options) {
4619
- return new MenuRenderer(options);
4620
- }
4621
-
4622
- class MenuEngine {
4623
- renderer;
4624
- state;
4625
- config;
4626
- commandHandlers;
4627
- constructor(options = {}) {
4628
- this.renderer = new MenuRenderer(options);
4629
- this.state = {
4630
- currentPath: [],
4631
- history: [],
4632
- searchQuery: void 0,
4633
- projectInfo: void 0,
4634
- apiStatus: void 0
4635
- };
4636
- this.config = mainMenuConfig;
4637
- this.commandHandlers = /* @__PURE__ */ new Map();
4638
- }
4639
- /**
4640
- * 注册命令处理器
4641
- */
4642
- registerHandler(command, handler) {
4643
- this.commandHandlers.set(command, handler);
4644
- }
4645
- /**
4646
- * 批量注册命令处理器
4647
- */
4648
- registerHandlers(handlers) {
4649
- for (const [command, handler] of Object.entries(handlers)) {
4650
- this.commandHandlers.set(command, handler);
4651
- }
4652
- }
4653
- /**
4654
- * 设置项目信息
4655
- */
4656
- setProjectInfo(info) {
4657
- this.state.projectInfo = info;
4658
- }
4659
- /**
4660
- * 设置 API 状态
4661
- */
4662
- setApiStatus(status) {
4663
- this.state.apiStatus = status;
4664
- }
4665
- /**
4666
- * 刷新项目信息(按需分析)
4667
- */
4668
- async refreshProjectInfo() {
4669
- try {
4670
- const { detectProjectInfo } = await import('./chunks/project-detector.mjs');
4671
- this.state.projectInfo = await detectProjectInfo();
4672
- } catch {
4673
- this.state.projectInfo = {
4674
- name: "Unknown",
4675
- type: void 0,
4676
- language: void 0
4677
- };
4678
- }
4679
- }
4680
- /**
4681
- * 刷新 API 状态
4682
- */
4683
- async refreshApiStatus() {
4684
- try {
4685
- const { detectApiStatus } = await import('./chunks/api-adapter.mjs');
4686
- this.state.apiStatus = await detectApiStatus();
4687
- } catch {
4688
- this.state.apiStatus = {
4689
- configured: false,
4690
- mode: "none"
4691
- };
4692
- }
4693
- }
4694
- /**
4695
- * 执行菜单动作
4696
- */
4697
- async executeAction(item) {
4698
- if (!item.action)
4699
- return false;
4700
- const { type, handler } = item.action;
4701
- switch (type) {
4702
- case "command": {
4703
- if (typeof handler === "string") {
4704
- const commandHandler = this.commandHandlers.get(handler);
4705
- if (commandHandler) {
4706
- await commandHandler();
4707
- return true;
4708
- }
4709
- console.log(`Command not found: ${handler}`);
4710
- return false;
4711
- }
4712
- return false;
4713
- }
4714
- case "function": {
4715
- if (typeof handler === "function") {
4716
- await handler();
4717
- return true;
4718
- }
4719
- return false;
4720
- }
4721
- case "submenu": {
4722
- return true;
4723
- }
4724
- case "external": {
4725
- if (typeof handler === "string") {
4726
- const { exec } = await import('node:child_process');
4727
- exec(`open ${handler}`);
4728
- return true;
4729
- }
4730
- return false;
4731
- }
4732
- default:
4733
- return false;
4734
- }
4735
- }
4736
- /**
4737
- * 处理菜单选择
4738
- */
4739
- async handleSelection(selection) {
4740
- const { item, action } = selection;
4741
- switch (action) {
4742
- case "exit":
4743
- return "exit";
4744
- case "back":
4745
- if (this.state.currentPath.length > 0) {
4746
- this.state.currentPath.pop();
4747
- this.state.history.pop();
4748
- }
4749
- return "continue";
4750
- case "select": {
4751
- if (item.id === "exit") {
4752
- return "exit";
4753
- }
4754
- if (item.submenu && item.submenu.length > 0) {
4755
- const label = getLocalizedLabel(item.label, this.renderer.getLocale());
4756
- this.state.currentPath.push(label);
4757
- this.state.history.push(this.state.currentPath.slice());
4758
- const subSelection = await this.renderer.renderSubmenu(
4759
- item,
4760
- this.state.currentPath
4761
- );
4762
- return this.handleSelection(subSelection);
4763
- }
4764
- await this.executeAction(item);
4765
- return "continue";
4766
- }
4767
- default:
4768
- return "continue";
4769
- }
4770
- }
4771
- /**
4772
- * 启动菜单
4773
- */
4774
- async start() {
4775
- let running = true;
4776
- while (running) {
4777
- try {
4778
- const selection = await this.renderer.renderMainMenu(
4779
- this.config,
4780
- this.state.projectInfo,
4781
- this.state.apiStatus
4782
- );
4783
- const result = await this.handleSelection(selection);
4784
- if (result === "exit") {
4785
- running = false;
4786
- }
4787
- } catch (error) {
4788
- if (error?.isTtyError || error?.message?.includes("User force closed")) {
4789
- running = false;
4790
- } else {
4791
- console.error("Menu error:", error);
4792
- running = false;
4793
- }
4794
- }
4795
- }
4796
- process.exit(0);
4797
- }
4798
- /**
4799
- * 导航到指定菜单
4800
- */
4801
- async navigate(path) {
4802
- this.state.currentPath = path;
4803
- this.state.history = [path.slice()];
4804
- }
4805
- /**
4806
- * 返回上级
4807
- */
4808
- async back() {
4809
- if (this.state.currentPath.length > 0) {
4810
- this.state.currentPath.pop();
4811
- this.state.history.pop();
4812
- }
4813
- }
4814
- /**
4815
- * 退出菜单
4816
- */
4817
- exit() {
4818
- process.exit(0);
4819
- }
4820
- /**
4821
- * 获取当前状态
4822
- */
4823
- getState() {
4824
- return { ...this.state };
4825
- }
4826
- /**
4827
- * 设置语言
4828
- */
4829
- setLocale(locale) {
4830
- this.renderer.setLocale(locale);
4831
- }
4832
- }
4833
- function createMenuEngine(options) {
4834
- return new MenuEngine(options);
4835
- }
4836
-
4837
- const menuEngine = {
4838
- __proto__: null,
4839
- MenuEngine: MenuEngine,
4840
- createMenuEngine: createMenuEngine
4841
- };
4842
-
4843
- async function showMenu() {
4844
- const { runCli: runCli2 } = await Promise.resolve().then(function () { return cli; });
4845
- await runCli2();
4846
- }
4847
- async function startMenu(options = {}) {
4848
- const { createMenuEngine: createMenuEngine2 } = await Promise.resolve().then(function () { return menuEngine; });
4849
- const engine = createMenuEngine2({
4850
- locale: options.locale,
4851
- showStatusBar: options.showStatusBar ?? true,
4852
- showBreadcrumb: options.showBreadcrumb ?? true,
4853
- showShortcuts: options.showShortcuts ?? true
4854
- });
4855
- await registerDefaultHandlers(engine);
4856
- await engine.start();
4857
- }
4858
- async function registerDefaultHandlers(engine) {
4859
- engine.registerHandlers({
4860
- "api:official": async () => {
4861
- console.log("\u4F7F\u7528\u5B98\u65B9\u767B\u5F55...");
4862
- },
4863
- "api:custom": async () => {
4864
- const { runWizard } = await import('./chunks/api.mjs');
4865
- await runWizard();
4866
- },
4867
- "api:ccr": async () => {
4868
- const { runCcrMenuFeature } = await import('./chunks/tools.mjs').then(function (n) { return n.t; });
4869
- await runCcrMenuFeature();
4870
- },
4871
- "api:switch": async () => {
4872
- const { configSwitchCommand } = await import('./chunks/config-switch.mjs');
4873
- await configSwitchCommand({ codeType: "claude-code" });
4874
- },
4875
- "api:status": async () => {
4876
- console.log("\u67E5\u770B\u5F53\u524D API \u914D\u7F6E...");
4877
- }
4878
- });
4879
- engine.registerHandlers({
4880
- "ccjk:init": async () => {
4881
- const { init } = await import('./chunks/init.mjs');
4882
- await init({ skipBanner: true });
4883
- },
4884
- "ccjk:skills": async () => {
4885
- console.log("\u6280\u80FD\u7BA1\u7406...");
4886
- },
4887
- "ccjk:mcp": async () => {
4888
- const { mcpHelp } = await import('./chunks/mcp.mjs');
4889
- mcpHelp();
4890
- },
4891
- "ccjk:agents": async () => {
4892
- console.log("\u4EE3\u7406\u7BA1\u7406...");
4893
- },
4894
- "ccjk:hooks": async () => {
4895
- const { hooksSync } = await import('./chunks/hooks-sync.mjs');
4896
- await hooksSync({});
4897
- }
4898
- });
4899
- engine.registerHandlers({
4900
- "session:create": async () => {
4901
- console.log("\u521B\u5EFA\u65B0\u4F1A\u8BDD...");
4902
- },
4903
- "session:restore": async () => {
4904
- console.log("\u6062\u590D\u4F1A\u8BDD...");
4905
- },
4906
- "session:list": async () => {
4907
- console.log("\u4F1A\u8BDD\u5217\u8868...");
4908
- },
4909
- "context:manage": async () => {
4910
- const { showContextMenu } = await import('./chunks/context-menu.mjs');
4911
- await showContextMenu();
4912
- },
4913
- "session:export": async () => {
4914
- console.log("\u5BFC\u51FA\u4F1A\u8BDD...");
4915
- },
4916
- "session:cleanup": async () => {
4917
- console.log("\u6E05\u7406\u7F13\u5B58...");
4918
- }
4919
- });
4920
- engine.registerHandlers({
4921
- "settings:language": async () => {
4922
- const { changeScriptLanguageFeature } = await import('./chunks/features.mjs');
4923
- const { i18n } = await import('./chunks/index.mjs');
4924
- await changeScriptLanguageFeature(i18n.language);
4925
- },
4926
- "settings:advanced": async () => {
4927
- console.log("\u9AD8\u7EA7\u8BBE\u7F6E...");
4928
- },
4929
- "settings:reset": async () => {
4930
- console.log("\u91CD\u7F6E\u8BBE\u7F6E...");
4931
- }
4932
- });
4933
- engine.registerHandlers({
4934
- "help:commands": async () => {
4935
- console.log("\u547D\u4EE4\u53C2\u8003...");
4936
- },
4937
- "help:tutorial": async () => {
4938
- console.log("\u5FEB\u901F\u6559\u7A0B...");
4939
- },
4940
- "help:faq": async () => {
4941
- console.log("\u5E38\u89C1\u95EE\u9898...");
4942
- },
4943
- "help:about": async () => {
4944
- const VERSION = "1.0.0";
4945
- console.log(`CCJK - Claude Code JK v${VERSION}`);
4946
- console.log("https://github.com/anthropics/claude-code");
4947
- }
4948
- });
4949
- }
4950
-
4951
- const index$7 = {
4952
- __proto__: null,
4953
- MenuEngine: MenuEngine,
4954
- MenuRenderer: MenuRenderer,
4955
- apiConfigMenu: apiConfigMenu,
4956
- createMenuEngine: createMenuEngine,
4957
- createMenuRenderer: createMenuRenderer,
4958
- getLocalizedLabel: getLocalizedLabel,
4959
- helpGroup: helpGroup,
4960
- mainMenuConfig: mainMenuConfig,
4961
- newMainMenuConfig: mainMenuConfig$1,
4962
- projectManagementGroup: projectManagementGroup,
4963
- quickStartGroup: quickStartGroup,
4964
- runCli: runCli,
4965
- sessionManagementGroup: sessionManagementGroup,
4966
- settingsGroup: settingsGroup,
4967
- showMenu: showMenu,
4968
- startMenu: startMenu
4969
- };
4970
-
4971
792
  function unique(arr) {
4972
793
  return [...new Set(arr)];
4973
794
  }
@@ -7844,4 +3665,4 @@ function assert(condition, message) {
7844
3665
  }
7845
3666
  }
7846
3667
 
7847
- export { ActionbookEngine, AiderTool, BaseCodeTool, BaseError, ClaudeCodeTool, ClineTool, CodexTool, ConfigManager, ConfigValidator, ConfigurationError, ContinueTool, CursorTool, DependencyTracker, FileWatcher, IncrementalIndexer, InternalError, Logger, MenuEngine, MenuRenderer, MultiLevelIndex, Mutex, NotFoundError, QueryAPIRouter, Semaphore, TimeoutError, ToolFactory, ToolRegistry, UnauthorizedError, ValidationError, apiConfigMenu, index$6 as array, assert, assertDefined, ast, index$5 as async, batchProcessFiles, callGraph, camelCase, capitalize, chunk, closeGlobalIndex, closeGlobalWatcher, index$4 as command, commandExists, copyFile, countLines, createConfigManager, createEngine, createLogger, createMenuEngine, createMenuRenderer, createTool, createValidator, debounce, deepClone, deepMerge, deleteDir, deleteFile, difference, ensureDir, index$3 as error, executeCommand, executeCommandStream, exists, flatten, flatten$1 as flattenArray, formatError, index$2 as fs, generateCompactWelcome, generateRecommendations, generateWelcome, get, getArchitecture, getCacheDir, getCapabilitiesByType, getCapability, getCommandPath, getCommandVersion, getConfigDir, getDataDir, getErrorMessage, getFileInfo, getFileSize, getGlobalIndex, getGlobalIndexer, getGlobalTracker, getGlobalWatcher, getHomeDir, getLocalizedLabel, getPlatform, getPlatformInfo, getQueryAPI, getRegistry, getTempDir, has, helpGroup, intersection, isArray, isBoolean, isDefined, isDirectory, isEmail, isFile, isLargeFile, isLinux, isMacOS, isNumber, isObject, isString, isURL, isUnix, isWindows, kebabCase, listDirs, listFiles, logger, logger$2 as loggerUtils, mainMenuConfig, moveFile, mainMenuConfig$1 as newMainMenuConfig, index$1 as object, omit, parallelLimit, partition, pascalCase, pick, processLargeFile, processLineByLine, projectManagementGroup, quickStartGroup, readFile, readJSON, resetGlobalIndexer, resetGlobalTracker, retry, runCli, scanCapabilities, sequence, sessionManagementGroup, set, settingsGroup, showMenu, shuffle, sleep, slugify, snakeCase, startMenu, streamJSON, streamWriteJSON, index as string, symbols, template, throttle, timeout, truncate, tryCatch, tryCatchAsync, unflatten, union, unique, validation, validators, waitFor, wrapError, writeFile, writeJSON };
3668
+ export { AiderTool, BaseCodeTool, BaseError, ClaudeCodeTool, ClineTool, CodexTool, ConfigManager, ConfigValidator, ConfigurationError, ContinueTool, CursorTool, InternalError, Logger, Mutex, NotFoundError, Semaphore, TimeoutError, ToolFactory, ToolRegistry, UnauthorizedError, ValidationError, index$6 as array, assert, assertDefined, index$5 as async, batchProcessFiles, camelCase, capitalize, chunk, index$4 as command, commandExists, copyFile, countLines, createConfigManager, createLogger, createTool, createValidator, debounce, deepClone, deepMerge, deleteDir, deleteFile, difference, ensureDir, index$3 as error, executeCommand, executeCommandStream, exists, flatten, flatten$1 as flattenArray, formatError, index$2 as fs, generateCompactWelcome, generateRecommendations, generateWelcome, get, getArchitecture, getCacheDir, getCapabilitiesByType, getCapability, getCommandPath, getCommandVersion, getConfigDir, getDataDir, getErrorMessage, getFileInfo, getFileSize, getHomeDir, getPlatform, getPlatformInfo, getRegistry, getTempDir, has, intersection, isArray, isBoolean, isDefined, isDirectory, isEmail, isFile, isLargeFile, isLinux, isMacOS, isNumber, isObject, isString, isURL, isUnix, isWindows, kebabCase, listDirs, listFiles, logger, logger$2 as loggerUtils, moveFile, index$1 as object, omit, parallelLimit, partition, pascalCase, pick, processLargeFile, processLineByLine, readFile, readJSON, retry, scanCapabilities, sequence, set, shuffle, sleep, slugify, snakeCase, streamJSON, streamWriteJSON, index as string, template, throttle, timeout, truncate, tryCatch, tryCatchAsync, unflatten, union, unique, validation, validators, waitFor, wrapError, writeFile, writeJSON };