@leejungkiin/awkit 1.6.0 → 1.6.1

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.
package/bin/awk.js CHANGED
@@ -2051,6 +2051,18 @@ function cmdHelp() {
2051
2051
  log(` ${C.gray} --parse-mode <md|html>${C.reset} Formatting mode`);
2052
2052
  log('');
2053
2053
 
2054
+ // GitNexus
2055
+ log(`${C.bold}🔍 GitNexus${C.reset}`);
2056
+ log(line);
2057
+ log(` ${C.green}gitnexus list${C.reset} List all indexed repositories`);
2058
+ log(` ${C.green}gitnexus status${C.reset} Show current project index info`);
2059
+ log(` ${C.green}gitnexus clean${C.reset} Interactive cleanup of old indexes`);
2060
+ log(` ${C.green}gitnexus clean${C.reset} ${C.gray}<name...>${C.reset} Remove specific repo(s) by name`);
2061
+ log(` ${C.gray} --stale${C.reset} Auto-remove repos with missing paths`);
2062
+ log(` ${C.gray} --all${C.reset} Remove all except current project`);
2063
+ log(` ${C.gray} Alias: gn${C.reset}`);
2064
+ log('');
2065
+
2054
2066
  // Available packs
2055
2067
  const packsDir = path.join(AWK_ROOT, 'skill-packs');
2056
2068
  if (fs.existsSync(packsDir)) {
@@ -3383,6 +3395,309 @@ function checkAutoUpdate() {
3383
3395
  }
3384
3396
  }
3385
3397
 
3398
+ // ─── GitNexus Management ──────────────────────────────────────────────────────
3399
+
3400
+ const GITNEXUS_REGISTRY = path.join(HOME, '.gitnexus', 'registry.json');
3401
+
3402
+ function readGitnexusRegistry() {
3403
+ if (!fs.existsSync(GITNEXUS_REGISTRY)) return [];
3404
+ try {
3405
+ return JSON.parse(fs.readFileSync(GITNEXUS_REGISTRY, 'utf8'));
3406
+ } catch (_) {
3407
+ return [];
3408
+ }
3409
+ }
3410
+
3411
+ function writeGitnexusRegistry(registry) {
3412
+ fs.mkdirSync(path.dirname(GITNEXUS_REGISTRY), { recursive: true });
3413
+ fs.writeFileSync(GITNEXUS_REGISTRY, JSON.stringify(registry, null, 2) + '\n');
3414
+ }
3415
+
3416
+ function cmdGitnexus(args) {
3417
+ const sub = args[0] || 'list';
3418
+
3419
+ switch (sub) {
3420
+ case 'list':
3421
+ case 'ls': {
3422
+ const registry = readGitnexusRegistry();
3423
+ if (registry.length === 0) {
3424
+ info('No GitNexus indexed repositories found.');
3425
+ dim('Run `npx gitnexus analyze` in a project to index it.');
3426
+ return;
3427
+ }
3428
+
3429
+ log('');
3430
+ log(`${C.cyan}${C.bold}🔍 GitNexus Indexed Repositories (${registry.length})${C.reset}`);
3431
+ log(`${C.gray}${'─'.repeat(56)}${C.reset}`);
3432
+
3433
+ const cwd = process.cwd();
3434
+ for (const repo of registry) {
3435
+ const isCurrent = repo.path === cwd;
3436
+ const marker = isCurrent ? `${C.green}◉` : `${C.gray}○`;
3437
+ const nameColor = isCurrent ? C.green : C.reset;
3438
+ const stats = repo.stats || {};
3439
+ const indexDate = repo.indexedAt ? new Date(repo.indexedAt).toLocaleDateString() : 'unknown';
3440
+ const pathExists = fs.existsSync(repo.path);
3441
+ const pathStatus = pathExists ? '' : ` ${C.red}(path missing!)${C.reset}`;
3442
+
3443
+ log(` ${marker} ${nameColor}${C.bold}${repo.name}${C.reset}${pathStatus}`);
3444
+ dim(` Path: ${repo.path}`);
3445
+ dim(` Indexed: ${indexDate} | ${stats.nodes || 0} symbols, ${stats.edges || 0} edges, ${stats.processes || 0} flows`);
3446
+ }
3447
+ log('');
3448
+ break;
3449
+ }
3450
+
3451
+ case 'status': {
3452
+ const cwd = process.cwd();
3453
+ const registry = readGitnexusRegistry();
3454
+ const match = registry.find(r => r.path === cwd);
3455
+
3456
+ if (!match) {
3457
+ warn('Current directory is not indexed by GitNexus.');
3458
+ dim('Run `npx gitnexus analyze` to index this project.');
3459
+ return;
3460
+ }
3461
+
3462
+ const stats = match.stats || {};
3463
+ const indexDate = match.indexedAt ? new Date(match.indexedAt) : null;
3464
+ const ageMs = indexDate ? Date.now() - indexDate.getTime() : 0;
3465
+ const ageDays = Math.floor(ageMs / (1000 * 60 * 60 * 24));
3466
+ const stale = ageDays > 7;
3467
+
3468
+ log('');
3469
+ log(`${C.cyan}${C.bold}🔍 GitNexus: ${match.name}${C.reset}`);
3470
+ log(`${C.gray}${'─'.repeat(56)}${C.reset}`);
3471
+ dim(`Path: ${match.path}`);
3472
+ dim(`Storage: ${match.storagePath}`);
3473
+ dim(`Indexed: ${indexDate ? indexDate.toLocaleString() : 'unknown'} (${ageDays}d ago)`);
3474
+ dim(`Last Commit: ${match.lastCommit || 'n/a'}`);
3475
+ dim(`Symbols: ${stats.nodes || 0}`);
3476
+ dim(`Relations: ${stats.edges || 0}`);
3477
+ dim(`Communities: ${stats.communities || 0}`);
3478
+ dim(`Processes: ${stats.processes || 0}`);
3479
+ dim(`Embeddings: ${stats.embeddings || 0}`);
3480
+
3481
+ if (stale) {
3482
+ log('');
3483
+ warn(`Index is ${ageDays} days old. Consider refreshing:`);
3484
+ dim(' npx gitnexus analyze');
3485
+ }
3486
+
3487
+ // Show projectId from .project-identity if available
3488
+ const identityPath = path.join(cwd, '.project-identity');
3489
+ const identity = readJsonFile(identityPath);
3490
+ if (identity && identity.projectId) {
3491
+ dim(`Project ID: ${identity.projectId}`);
3492
+ }
3493
+ log('');
3494
+ break;
3495
+ }
3496
+
3497
+ case 'clean': {
3498
+ const registry = readGitnexusRegistry();
3499
+ if (registry.length === 0) {
3500
+ info('No GitNexus indexed repositories to clean.');
3501
+ return;
3502
+ }
3503
+
3504
+ const cwd = process.cwd();
3505
+ const isStaleMode = args.includes('--stale');
3506
+ const isAllMode = args.includes('--all');
3507
+
3508
+ // Named repos: awkit gn clean <name1> <name2> ...
3509
+ const namedArgs = args.slice(1).filter(a => !a.startsWith('--'));
3510
+ if (namedArgs.length > 0 && !isStaleMode && !isAllMode) {
3511
+ const nameLookup = new Map(registry.map(r => [r.name.toLowerCase(), r]));
3512
+ const matched = [];
3513
+ const notFound = [];
3514
+
3515
+ for (const name of namedArgs) {
3516
+ const entry = nameLookup.get(name.toLowerCase());
3517
+ if (entry) matched.push(entry);
3518
+ else notFound.push(name);
3519
+ }
3520
+
3521
+ if (notFound.length > 0) {
3522
+ warn(`Not found in registry: ${notFound.join(', ')}`);
3523
+ dim('Available repos:');
3524
+ for (const r of registry) dim(` • ${r.name}`);
3525
+ }
3526
+
3527
+ if (matched.length === 0) {
3528
+ info('No matching repos to clean.');
3529
+ return;
3530
+ }
3531
+
3532
+ log('');
3533
+ log(`${C.yellow}${C.bold}🧹 Will remove ${matched.length} repo(s):${C.reset}`);
3534
+ for (const r of matched) {
3535
+ log(` ${C.red}✗${C.reset} ${r.name} — ${r.path}`);
3536
+ }
3537
+ log('');
3538
+
3539
+ if (!promptYN('Confirm?')) {
3540
+ info('Cancelled.');
3541
+ return;
3542
+ }
3543
+
3544
+ const removeNames = new Set(matched.map(r => r.name));
3545
+ for (const r of matched) {
3546
+ if (r.storagePath && fs.existsSync(r.storagePath)) {
3547
+ try {
3548
+ fs.rmSync(r.storagePath, { recursive: true, force: true });
3549
+ dim(`Deleted: ${r.storagePath}`);
3550
+ } catch (e) {
3551
+ warn(`Failed to delete ${r.storagePath}: ${e.message}`);
3552
+ }
3553
+ }
3554
+ }
3555
+
3556
+ const remaining = registry.filter(r => !removeNames.has(r.name));
3557
+ writeGitnexusRegistry(remaining);
3558
+ ok(`Removed ${matched.length} repos. ${remaining.length} remaining.`);
3559
+ return;
3560
+ }
3561
+
3562
+ if (isStaleMode) {
3563
+ // Auto-clean repos whose paths no longer exist
3564
+ const stale = registry.filter(r => !fs.existsSync(r.path));
3565
+ if (stale.length === 0) {
3566
+ ok('No stale repos found. All paths are valid.');
3567
+ return;
3568
+ }
3569
+
3570
+ log('');
3571
+ log(`${C.yellow}${C.bold}🧹 Found ${stale.length} stale repo(s):${C.reset}`);
3572
+ for (const r of stale) {
3573
+ log(` ${C.red}✗${C.reset} ${r.name} — ${r.path}`);
3574
+ }
3575
+ log('');
3576
+
3577
+ if (!promptYN('Remove these stale entries?')) {
3578
+ info('Cancelled.');
3579
+ return;
3580
+ }
3581
+
3582
+ for (const r of stale) {
3583
+ // Remove .gitnexus storage if it exists
3584
+ if (r.storagePath && fs.existsSync(r.storagePath)) {
3585
+ try {
3586
+ fs.rmSync(r.storagePath, { recursive: true, force: true });
3587
+ dim(`Deleted: ${r.storagePath}`);
3588
+ } catch (e) {
3589
+ warn(`Failed to delete ${r.storagePath}: ${e.message}`);
3590
+ }
3591
+ }
3592
+ }
3593
+
3594
+ const remaining = registry.filter(r => fs.existsSync(r.path));
3595
+ writeGitnexusRegistry(remaining);
3596
+ ok(`Removed ${stale.length} stale entries. ${remaining.length} repos remaining.`);
3597
+ return;
3598
+ }
3599
+
3600
+ if (isAllMode) {
3601
+ const toRemove = registry.filter(r => r.path !== cwd);
3602
+ if (toRemove.length === 0) {
3603
+ info('Only current project is indexed. Nothing to clean.');
3604
+ return;
3605
+ }
3606
+
3607
+ log('');
3608
+ log(`${C.yellow}${C.bold}🧹 Will remove ${toRemove.length} repo(s) (keeping current project):${C.reset}`);
3609
+ for (const r of toRemove) {
3610
+ log(` ${C.red}✗${C.reset} ${r.name} — ${r.path}`);
3611
+ }
3612
+ log('');
3613
+
3614
+ if (!promptYN('Proceed?')) {
3615
+ info('Cancelled.');
3616
+ return;
3617
+ }
3618
+
3619
+ for (const r of toRemove) {
3620
+ if (r.storagePath && fs.existsSync(r.storagePath)) {
3621
+ try {
3622
+ fs.rmSync(r.storagePath, { recursive: true, force: true });
3623
+ dim(`Deleted: ${r.storagePath}`);
3624
+ } catch (e) {
3625
+ warn(`Failed to delete ${r.storagePath}: ${e.message}`);
3626
+ }
3627
+ }
3628
+ }
3629
+
3630
+ const remaining = registry.filter(r => r.path === cwd);
3631
+ writeGitnexusRegistry(remaining);
3632
+ ok(`Removed ${toRemove.length} repos. Current project preserved.`);
3633
+ return;
3634
+ }
3635
+
3636
+ // Interactive mode: show numbered list, user selects
3637
+ log('');
3638
+ log(`${C.cyan}${C.bold}🔍 Select repos to remove:${C.reset}`);
3639
+ log(`${C.gray}${'─'.repeat(56)}${C.reset}`);
3640
+
3641
+ for (let i = 0; i < registry.length; i++) {
3642
+ const r = registry[i];
3643
+ const isCurrent = r.path === cwd;
3644
+ const pathExists = fs.existsSync(r.path);
3645
+ const tag = isCurrent ? ` ${C.green}(current)${C.reset}` : !pathExists ? ` ${C.red}(missing)${C.reset}` : '';
3646
+ log(` ${C.yellow}${i + 1}${C.reset}. ${r.name}${tag}`);
3647
+ dim(` ${r.path}`);
3648
+ }
3649
+ log('');
3650
+
3651
+ const input = promptChoice('Enter numbers to remove (e.g. 2,3,5)', '').trim();
3652
+ if (!input) {
3653
+ info('No selection. Cancelled.');
3654
+ return;
3655
+ }
3656
+
3657
+ const indices = input.split(',').map(s => parseInt(s.trim(), 10) - 1).filter(i => i >= 0 && i < registry.length);
3658
+ if (indices.length === 0) {
3659
+ info('Invalid selection. Cancelled.');
3660
+ return;
3661
+ }
3662
+
3663
+ const selected = indices.map(i => registry[i]);
3664
+ log('');
3665
+ log(`${C.yellow}Will remove:${C.reset}`);
3666
+ for (const r of selected) {
3667
+ log(` ${C.red}✗${C.reset} ${r.name}`);
3668
+ }
3669
+ log('');
3670
+
3671
+ if (!promptYN('Confirm?')) {
3672
+ info('Cancelled.');
3673
+ return;
3674
+ }
3675
+
3676
+ const removeNames = new Set(selected.map(r => r.name));
3677
+ for (const r of selected) {
3678
+ if (r.storagePath && fs.existsSync(r.storagePath)) {
3679
+ try {
3680
+ fs.rmSync(r.storagePath, { recursive: true, force: true });
3681
+ dim(`Deleted: ${r.storagePath}`);
3682
+ } catch (e) {
3683
+ warn(`Failed to delete ${r.storagePath}: ${e.message}`);
3684
+ }
3685
+ }
3686
+ }
3687
+
3688
+ const remaining = registry.filter(r => !removeNames.has(r.name));
3689
+ writeGitnexusRegistry(remaining);
3690
+ ok(`Removed ${selected.length} repos. ${remaining.length} remaining.`);
3691
+ break;
3692
+ }
3693
+
3694
+ default:
3695
+ err(`Unknown gitnexus sub-command: ${sub}`);
3696
+ log(` Available: list, status, clean`);
3697
+ break;
3698
+ }
3699
+ }
3700
+
3386
3701
  // ─── Native HTTP Server ───────────────────────────────────────────────────────
3387
3702
 
3388
3703
  function cmdServe(args) {
@@ -3545,6 +3860,10 @@ const [, , command, ...args] = process.argv;
3545
3860
  case 'serve':
3546
3861
  cmdServe(args);
3547
3862
  break;
3863
+ case 'gitnexus':
3864
+ case 'gn':
3865
+ cmdGitnexus(args);
3866
+ break;
3548
3867
  case 'gate':
3549
3868
  cmdGate(args);
3550
3869
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leejungkiin/awkit",
3
- "version": "1.6.0",
3
+ "version": "1.6.1",
4
4
  "description": "Antigravity Workflow Kit v12.5. Unified AI agent orchestration system with Mindful Checkpoints.",
5
5
  "main": "bin/awk.js",
6
6
  "bin": {
@@ -41,6 +41,31 @@ INDEX STALE (tool trả warning):
41
41
 
42
42
  ---
43
43
 
44
+ ## 🔑 Auto-Repo Detection Protocol (BẮT BUỘC)
45
+
46
+ ```
47
+ TRƯỚC KHI gọi bất kỳ GitNexus MCP tool nào:
48
+
49
+ 1. Gọi gitnexus_list_repos() → lấy danh sách repos đã index
50
+ 2. Match project directory (từ .project-identity hoặc cwd) với repo paths
51
+ 3. Dùng matched repo name làm `repo` param cho MỌI tool call
52
+
53
+ VÍ DỤ:
54
+ gitnexus_list_repos() trả về repo path "/Users/.../FitWitnessSimple"
55
+ → project đang mở = FitWitnessSimple
56
+ → gitnexus_query({query: "weather", repo: "FitWitnessSimple"})
57
+
58
+ FALLBACK (nếu không match path):
59
+ 1. So sánh projectId (từ .project-identity) với repo names
60
+ 2. Nếu vẫn không match → hỏi user chọn repo
61
+
62
+ ⚠️ KHÔNG BAO GIỜ gọi GitNexus tool mà THIẾU `repo` param khi có >1 repo indexed.
63
+ ```
64
+
65
+ > **Quản lý repos:** `awkit gitnexus list` để xem, `awkit gitnexus clean` để dọn repos cũ.
66
+
67
+ ---
68
+
44
69
  ## 🎯 When to Activate
45
70
 
46
71
  | Trigger | Action |
@@ -187,16 +187,6 @@ If `nmem` is not available AND auto-install fails, falls back to flat-file behav
187
187
  - Shows warning once: "⚠️ NeuralMemory not installed. Using flat-file fallback."
188
188
  ```
189
189
 
190
- ## CLI Fallback (MCP Unresponsive)
191
-
192
- If the `neural-memory` MCP server is unresponsive or fails, you MUST fallback to using the `nmem` CLI directly using the `run_command` tool. The CLI offers full parity with the MCP tools:
193
- - `nmem_remember` -> `nmem remember "..."`
194
- - `nmem_recall` -> `nmem recall "..."`
195
- - `nmem_context` -> `nmem recap` or `nmem recall "..."`
196
- - `nmem_auto` -> `nmem auto process --text "..."`
197
-
198
- Always check the `nmem` CLI help (`nmem --help`) if unsure of the syntax.
199
-
200
190
  ---
201
191
 
202
192
  ## Integration