fivocell 3.1.0 → 4.1.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 (176) hide show
  1. package/dist/__tests__/behavior-intelligence-bug.test.d.ts +2 -0
  2. package/dist/__tests__/behavior-intelligence-bug.test.d.ts.map +1 -0
  3. package/dist/__tests__/behavior-intelligence-bug.test.js +21 -0
  4. package/dist/__tests__/behavior-intelligence-bug.test.js.map +1 -0
  5. package/dist/__tests__/code-scanner-arrow-return-type.test.d.ts +2 -0
  6. package/dist/__tests__/code-scanner-arrow-return-type.test.d.ts.map +1 -0
  7. package/dist/__tests__/code-scanner-arrow-return-type.test.js +76 -0
  8. package/dist/__tests__/code-scanner-arrow-return-type.test.js.map +1 -0
  9. package/dist/__tests__/code-scanner-blindspot.test.d.ts +2 -0
  10. package/dist/__tests__/code-scanner-blindspot.test.d.ts.map +1 -0
  11. package/dist/__tests__/code-scanner-blindspot.test.js +18 -0
  12. package/dist/__tests__/code-scanner-blindspot.test.js.map +1 -0
  13. package/dist/__tests__/code-scanner-error-recovery.test.d.ts +2 -0
  14. package/dist/__tests__/code-scanner-error-recovery.test.d.ts.map +1 -0
  15. package/dist/__tests__/code-scanner-error-recovery.test.js +21 -0
  16. package/dist/__tests__/code-scanner-error-recovery.test.js.map +1 -0
  17. package/dist/__tests__/code-scanner-n1-detection.test.d.ts +2 -0
  18. package/dist/__tests__/code-scanner-n1-detection.test.d.ts.map +1 -0
  19. package/dist/__tests__/code-scanner-n1-detection.test.js +113 -0
  20. package/dist/__tests__/code-scanner-n1-detection.test.js.map +1 -0
  21. package/dist/__tests__/code-scanner-nesting.test.d.ts +2 -0
  22. package/dist/__tests__/code-scanner-nesting.test.d.ts.map +1 -0
  23. package/dist/__tests__/code-scanner-nesting.test.js +113 -0
  24. package/dist/__tests__/code-scanner-nesting.test.js.map +1 -0
  25. package/dist/__tests__/code-scanner-null-check.test.d.ts +2 -0
  26. package/dist/__tests__/code-scanner-null-check.test.d.ts.map +1 -0
  27. package/dist/__tests__/code-scanner-null-check.test.js +126 -0
  28. package/dist/__tests__/code-scanner-null-check.test.js.map +1 -0
  29. package/dist/__tests__/code-scanner-sql-fix.test.d.ts +2 -0
  30. package/dist/__tests__/code-scanner-sql-fix.test.d.ts.map +1 -0
  31. package/dist/__tests__/code-scanner-sql-fix.test.js +21 -0
  32. package/dist/__tests__/code-scanner-sql-fix.test.js.map +1 -0
  33. package/dist/__tests__/code-scanner-trust-score.test.d.ts +1 -0
  34. package/dist/__tests__/code-scanner-trust-score.test.d.ts.map +1 -0
  35. package/dist/__tests__/code-scanner-trust-score.test.js +39 -0
  36. package/dist/__tests__/code-scanner-trust-score.test.js.map +1 -0
  37. package/dist/__tests__/code-scanner-validation.test.d.ts +2 -0
  38. package/dist/__tests__/code-scanner-validation.test.d.ts.map +1 -0
  39. package/dist/__tests__/code-scanner-validation.test.js +131 -0
  40. package/dist/__tests__/code-scanner-validation.test.js.map +1 -0
  41. package/dist/__tests__/community-store.test.d.ts +2 -0
  42. package/dist/__tests__/community-store.test.d.ts.map +1 -0
  43. package/dist/__tests__/community-store.test.js +231 -0
  44. package/dist/__tests__/community-store.test.js.map +1 -0
  45. package/dist/__tests__/enhanced-blind-spots.test.d.ts +2 -0
  46. package/dist/__tests__/enhanced-blind-spots.test.d.ts.map +1 -0
  47. package/dist/__tests__/enhanced-blind-spots.test.js +302 -0
  48. package/dist/__tests__/enhanced-blind-spots.test.js.map +1 -0
  49. package/dist/__tests__/knowledge-graph-store.test.d.ts +2 -0
  50. package/dist/__tests__/knowledge-graph-store.test.d.ts.map +1 -0
  51. package/dist/__tests__/knowledge-graph-store.test.js +252 -0
  52. package/dist/__tests__/knowledge-graph-store.test.js.map +1 -0
  53. package/dist/__tests__/live-watcher.test.d.ts +2 -0
  54. package/dist/__tests__/live-watcher.test.d.ts.map +1 -0
  55. package/dist/__tests__/live-watcher.test.js +312 -0
  56. package/dist/__tests__/live-watcher.test.js.map +1 -0
  57. package/dist/__tests__/mcp-cell-tools.test.d.ts +2 -0
  58. package/dist/__tests__/mcp-cell-tools.test.d.ts.map +1 -0
  59. package/dist/__tests__/mcp-cell-tools.test.js +176 -0
  60. package/dist/__tests__/mcp-cell-tools.test.js.map +1 -0
  61. package/dist/__tests__/multi-project.test.d.ts +2 -0
  62. package/dist/__tests__/multi-project.test.d.ts.map +1 -0
  63. package/dist/__tests__/multi-project.test.js +145 -0
  64. package/dist/__tests__/multi-project.test.js.map +1 -0
  65. package/dist/__tests__/pc-scanner-paths.test.d.ts +2 -0
  66. package/dist/__tests__/pc-scanner-paths.test.d.ts.map +1 -0
  67. package/dist/__tests__/pc-scanner-paths.test.js +16 -0
  68. package/dist/__tests__/pc-scanner-paths.test.js.map +1 -0
  69. package/dist/__tests__/prompt-builder-realdata.test.d.ts +2 -0
  70. package/dist/__tests__/prompt-builder-realdata.test.d.ts.map +1 -0
  71. package/dist/__tests__/prompt-builder-realdata.test.js +94 -0
  72. package/dist/__tests__/prompt-builder-realdata.test.js.map +1 -0
  73. package/dist/__tests__/prompt-builder-sessions.test.d.ts +2 -0
  74. package/dist/__tests__/prompt-builder-sessions.test.d.ts.map +1 -0
  75. package/dist/__tests__/prompt-builder-sessions.test.js +124 -0
  76. package/dist/__tests__/prompt-builder-sessions.test.js.map +1 -0
  77. package/dist/__tests__/security.test.d.ts +1 -0
  78. package/dist/__tests__/security.test.d.ts.map +1 -0
  79. package/dist/__tests__/security.test.js +161 -0
  80. package/dist/__tests__/security.test.js.map +1 -0
  81. package/dist/__tests__/session-bridge.test.d.ts +2 -0
  82. package/dist/__tests__/session-bridge.test.d.ts.map +1 -0
  83. package/dist/__tests__/session-bridge.test.js +158 -0
  84. package/dist/__tests__/session-bridge.test.js.map +1 -0
  85. package/dist/__tests__/session-memory-tables.test.d.ts +2 -0
  86. package/dist/__tests__/session-memory-tables.test.d.ts.map +1 -0
  87. package/dist/__tests__/session-memory-tables.test.js +169 -0
  88. package/dist/__tests__/session-memory-tables.test.js.map +1 -0
  89. package/dist/__tests__/staleness-detection.test.d.ts +2 -0
  90. package/dist/__tests__/staleness-detection.test.d.ts.map +1 -0
  91. package/dist/__tests__/staleness-detection.test.js +105 -0
  92. package/dist/__tests__/staleness-detection.test.js.map +1 -0
  93. package/dist/__tests__/team-collaboration.test.d.ts +2 -0
  94. package/dist/__tests__/team-collaboration.test.d.ts.map +1 -0
  95. package/dist/__tests__/team-collaboration.test.js +224 -0
  96. package/dist/__tests__/team-collaboration.test.js.map +1 -0
  97. package/dist/__tests__/tool-specific-format.test.d.ts +2 -0
  98. package/dist/__tests__/tool-specific-format.test.d.ts.map +1 -0
  99. package/dist/__tests__/tool-specific-format.test.js +132 -0
  100. package/dist/__tests__/tool-specific-format.test.js.map +1 -0
  101. package/dist/__tests__/usage-intelligence-store.test.d.ts +2 -0
  102. package/dist/__tests__/usage-intelligence-store.test.d.ts.map +1 -0
  103. package/dist/__tests__/usage-intelligence-store.test.js +266 -0
  104. package/dist/__tests__/usage-intelligence-store.test.js.map +1 -0
  105. package/dist/ai-bridge.d.ts +20 -0
  106. package/dist/ai-bridge.d.ts.map +1 -0
  107. package/dist/ai-bridge.js +250 -0
  108. package/dist/ai-bridge.js.map +1 -0
  109. package/dist/behavior-intelligence.d.ts.map +1 -1
  110. package/dist/behavior-intelligence.js +12 -1
  111. package/dist/behavior-intelligence.js.map +1 -1
  112. package/dist/cli.js +501 -4
  113. package/dist/cli.js.map +1 -1
  114. package/dist/code-scanner.d.ts.map +1 -1
  115. package/dist/code-scanner.js +426 -69
  116. package/dist/code-scanner.js.map +1 -1
  117. package/dist/core/community-store.d.ts +128 -0
  118. package/dist/core/community-store.d.ts.map +1 -0
  119. package/dist/core/community-store.js +329 -0
  120. package/dist/core/community-store.js.map +1 -0
  121. package/dist/core/database.d.ts +0 -3
  122. package/dist/core/database.d.ts.map +1 -1
  123. package/dist/core/database.js +287 -15
  124. package/dist/core/database.js.map +1 -1
  125. package/dist/core/enhanced-blind-spots.d.ts +27 -0
  126. package/dist/core/enhanced-blind-spots.d.ts.map +1 -0
  127. package/dist/core/enhanced-blind-spots.js +591 -0
  128. package/dist/core/enhanced-blind-spots.js.map +1 -0
  129. package/dist/core/knowledge-graph-store.d.ts +69 -0
  130. package/dist/core/knowledge-graph-store.d.ts.map +1 -0
  131. package/dist/core/knowledge-graph-store.js +269 -0
  132. package/dist/core/knowledge-graph-store.js.map +1 -0
  133. package/dist/core/live-watcher.d.ts +52 -0
  134. package/dist/core/live-watcher.d.ts.map +1 -0
  135. package/dist/core/live-watcher.js +369 -0
  136. package/dist/core/live-watcher.js.map +1 -0
  137. package/dist/core/project-registry.d.ts +24 -0
  138. package/dist/core/project-registry.d.ts.map +1 -0
  139. package/dist/core/project-registry.js +70 -0
  140. package/dist/core/project-registry.js.map +1 -0
  141. package/dist/core/prompt-builder.d.ts +50 -0
  142. package/dist/core/prompt-builder.d.ts.map +1 -1
  143. package/dist/core/prompt-builder.js +533 -79
  144. package/dist/core/prompt-builder.js.map +1 -1
  145. package/dist/core/security.d.ts +23 -0
  146. package/dist/core/security.d.ts.map +1 -0
  147. package/dist/core/security.js +117 -0
  148. package/dist/core/security.js.map +1 -0
  149. package/dist/core/session-memory.d.ts +64 -0
  150. package/dist/core/session-memory.d.ts.map +1 -1
  151. package/dist/core/session-memory.js +111 -0
  152. package/dist/core/session-memory.js.map +1 -1
  153. package/dist/core/usage-intelligence-store.d.ts +126 -0
  154. package/dist/core/usage-intelligence-store.d.ts.map +1 -0
  155. package/dist/core/usage-intelligence-store.js +405 -0
  156. package/dist/core/usage-intelligence-store.js.map +1 -0
  157. package/dist/daemon/server.d.ts.map +1 -1
  158. package/dist/daemon/server.js +936 -17
  159. package/dist/daemon/server.js.map +1 -1
  160. package/dist/knowledge-graph-builder.d.ts +16 -0
  161. package/dist/knowledge-graph-builder.d.ts.map +1 -0
  162. package/dist/knowledge-graph-builder.js +186 -0
  163. package/dist/knowledge-graph-builder.js.map +1 -0
  164. package/dist/pc-scanner.d.ts +1 -0
  165. package/dist/pc-scanner.d.ts.map +1 -1
  166. package/dist/pc-scanner.js +58 -30
  167. package/dist/pc-scanner.js.map +1 -1
  168. package/dist/stack-detector.d.ts +34 -0
  169. package/dist/stack-detector.d.ts.map +1 -0
  170. package/dist/stack-detector.js +471 -0
  171. package/dist/stack-detector.js.map +1 -0
  172. package/dist/team-intel.d.ts +31 -0
  173. package/dist/team-intel.d.ts.map +1 -0
  174. package/dist/team-intel.js +310 -0
  175. package/dist/team-intel.js.map +1 -0
  176. package/package.json +1 -1
@@ -41,8 +41,10 @@ exports.formatScanReportCompact = formatScanReportCompact;
41
41
  exports.ensureCodePatternTable = ensureCodePatternTable;
42
42
  const fs = __importStar(require("fs"));
43
43
  const path = __importStar(require("path"));
44
+ const os = __importStar(require("os"));
44
45
  const ts = __importStar(require("typescript"));
45
46
  const database_1 = require("./core/database");
47
+ const stack_detector_1 = require("./stack-detector");
46
48
  // ─── Language Detection ──────────────────────────────────────────────────────
47
49
  const EXT_MAP = {
48
50
  '.ts': 'typescript', '.tsx': 'typescript', '.js': 'javascript',
@@ -394,9 +396,32 @@ function extractASTPatterns(content, filePath) {
394
396
  let zodImports = false;
395
397
  let nestDepth = 0;
396
398
  let maxNestDepth = 0;
399
+ let controlFlowDepth = 0;
400
+ let maxControlFlowDepth = 0;
401
+ let nPlusOne = 0;
402
+ let hardcodedSecrets = 0;
403
+ let missingNullCheck = 0;
404
+ let missingInputValidation = 0;
397
405
  const visit = (node, depth) => {
398
406
  nestDepth = Math.max(nestDepth, depth);
399
407
  maxNestDepth = Math.max(maxNestDepth, depth);
408
+ const isControlFlow = ts.isIfStatement(node) ||
409
+ ts.isForStatement(node) ||
410
+ ts.isForInStatement(node) ||
411
+ ts.isForOfStatement(node) ||
412
+ ts.isWhileStatement(node) ||
413
+ ts.isDoStatement(node) ||
414
+ ts.isSwitchStatement(node) ||
415
+ ts.isTryStatement(node) ||
416
+ ts.isConditionalExpression(node);
417
+ if (isControlFlow) {
418
+ controlFlowDepth = depth;
419
+ maxControlFlowDepth = Math.max(maxControlFlowDepth, depth);
420
+ }
421
+ if (isControlFlow) {
422
+ ts.forEachChild(node, (child) => visit(child, depth + 1));
423
+ return;
424
+ }
400
425
  // ── Async without try/catch ──────────────────────────────────────
401
426
  if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isMethodDeclaration(node)) {
402
427
  const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
@@ -414,7 +439,10 @@ function extractASTPatterns(content, filePath) {
414
439
  if (!hasTryCatch)
415
440
  asyncNoTryCatch++;
416
441
  // Return type check
417
- if (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) {
442
+ if (ts.isFunctionDeclaration(node) ||
443
+ ts.isMethodDeclaration(node) ||
444
+ ts.isFunctionExpression(node) ||
445
+ ts.isArrowFunction(node)) {
418
446
  const hasReturnType = node.type !== undefined;
419
447
  if (hasReturnType)
420
448
  functionsWithType++;
@@ -452,6 +480,128 @@ function extractASTPatterns(content, filePath) {
452
480
  if (expr.expression.getText().startsWith('z.'))
453
481
  zodImports = true;
454
482
  }
483
+ // N+1 query detection: await/query inside loop
484
+ if (ts.isCallExpression(node)) {
485
+ const callText = expr.getText();
486
+ if (callText.includes('.find') || callText.includes('.query') || callText.includes('.execute') ||
487
+ callText.includes('fetch') || callText.includes('prisma.') || callText.includes('db.')) {
488
+ let parent = node.parent;
489
+ while (parent) {
490
+ if (ts.isForStatement(parent) ||
491
+ ts.isForInStatement(parent) ||
492
+ ts.isForOfStatement(parent) ||
493
+ ts.isWhileStatement(parent) ||
494
+ ts.isDoStatement(parent) ||
495
+ (ts.isCallExpression(parent) &&
496
+ ts.isPropertyAccessExpression(parent.expression) &&
497
+ (parent.expression.name.text === 'map' || parent.expression.name.text === 'forEach'))) {
498
+ nPlusOne++;
499
+ break;
500
+ }
501
+ parent = parent.parent;
502
+ }
503
+ }
504
+ }
505
+ // Missing null check detection — any property access on awaited API call
506
+ if (ts.isPropertyAccessExpression(expr)) {
507
+ let base = expr.expression;
508
+ while (ts.isPropertyAccessExpression(base))
509
+ base = base.expression;
510
+ const baseText = base.getText();
511
+ if (baseText.includes('fetch') ||
512
+ baseText.includes('axios') ||
513
+ baseText.includes('request') ||
514
+ baseText.includes('http.get') ||
515
+ baseText.includes('prisma.') ||
516
+ baseText.includes('db.')) {
517
+ let hasAwait = false;
518
+ let hasNullCheck = false;
519
+ let ancestor = expr.parent;
520
+ while (ancestor && (!hasAwait || !hasNullCheck)) {
521
+ if (ts.isAwaitExpression(ancestor))
522
+ hasAwait = true;
523
+ if (ts.isExpressionStatement(ancestor) || ts.isVariableDeclaration(ancestor))
524
+ break;
525
+ if (ts.isIfStatement(ancestor)) {
526
+ const condText = ancestor.expression.getText();
527
+ if (condText.includes('!') ||
528
+ condText.includes('null') ||
529
+ condText.includes('undefined') ||
530
+ condText.includes('?.')) {
531
+ hasNullCheck = true;
532
+ }
533
+ }
534
+ if (ts.isBinaryExpression(ancestor) &&
535
+ (ancestor.operatorToken.kind === ts.SyntaxKind.EqualsEqualsToken ||
536
+ ancestor.operatorToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken ||
537
+ ancestor.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsToken ||
538
+ ancestor.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken)) {
539
+ const text = ancestor.getText();
540
+ if (text.includes('null') || text.includes('undefined')) {
541
+ hasNullCheck = true;
542
+ }
543
+ }
544
+ ancestor = ancestor.parent;
545
+ }
546
+ if (hasAwait && !hasNullCheck)
547
+ missingNullCheck++;
548
+ }
549
+ }
550
+ }
551
+ // ── Hardcoded secrets detection ─────────────────────────────────
552
+ if (ts.isVariableDeclaration(node)) {
553
+ const varNode = node;
554
+ const varName = ts.isIdentifier(varNode.name) ? varNode.name.text : '';
555
+ const lower = varName.toLowerCase();
556
+ if ((lower.includes('password') || lower.includes('secret') || lower.includes('api_key') ||
557
+ lower.includes('apikey') || (lower.includes('token') && lower.includes('auth'))) &&
558
+ varNode.initializer && ts.isStringLiteral(varNode.initializer)) {
559
+ const val = varNode.initializer.text;
560
+ if (val.length > 6 && !val.includes('${') && !val.startsWith('process.env')) {
561
+ hardcodedSecrets++;
562
+ }
563
+ }
564
+ }
565
+ // ── Missing input validation in API handlers ────────────────────
566
+ const isHandlerLike = ts.isFunctionDeclaration(node) ||
567
+ ts.isFunctionExpression(node) ||
568
+ ts.isArrowFunction(node);
569
+ if (isHandlerLike) {
570
+ let funcName = '';
571
+ let body;
572
+ if (ts.isFunctionDeclaration(node) && node.name) {
573
+ funcName = node.name.text;
574
+ body = node.body;
575
+ }
576
+ else if (ts.isFunctionExpression(node) ||
577
+ ts.isArrowFunction(node)) {
578
+ let p = node.parent;
579
+ while (p) {
580
+ if (ts.isVariableDeclaration(p) && ts.isIdentifier(p.name)) {
581
+ funcName = p.name.text;
582
+ break;
583
+ }
584
+ p = p.parent;
585
+ }
586
+ body = node.body;
587
+ }
588
+ if (funcName.toLowerCase().includes('handler') || funcName.toLowerCase().includes('route') || funcName.toLowerCase().includes('endpoint') || funcName.toLowerCase().includes('controller')) {
589
+ let hasValidation = false;
590
+ const checkBody = (n) => {
591
+ if (ts.isCallExpression(n)) {
592
+ const ct = n.expression.getText();
593
+ if (ct.includes('.parse') || ct.includes('.validate') || ct.includes('.safeParse') ||
594
+ ct.includes('check(') || ct.includes('body(') || ct.includes('query(')) {
595
+ hasValidation = true;
596
+ }
597
+ }
598
+ ts.forEachChild(n, checkBody);
599
+ };
600
+ if (body)
601
+ checkBody(body);
602
+ if (!hasValidation)
603
+ missingInputValidation++;
604
+ }
455
605
  }
456
606
  // ── any type usage ──────────────────────────────────────────────
457
607
  if (ts.isToken(node) && node.kind === ts.SyntaxKind.AnyKeyword)
@@ -505,12 +655,12 @@ function extractASTPatterns(content, filePath) {
505
655
  trustScore: 85,
506
656
  });
507
657
  }
508
- if (maxNestDepth > 4) {
658
+ if (maxControlFlowDepth > 4) {
509
659
  patterns.push({
510
660
  type: 'blind_spot_deep_nesting',
511
661
  category: 'blind_spots',
512
- value: `max_nesting:${maxNestDepth}`,
513
- count: maxNestDepth,
662
+ value: `max_nesting:${maxControlFlowDepth}`,
663
+ count: maxControlFlowDepth,
514
664
  trustScore: 80,
515
665
  });
516
666
  }
@@ -532,6 +682,42 @@ function extractASTPatterns(content, filePath) {
532
682
  trustScore: 90,
533
683
  });
534
684
  }
685
+ if (nPlusOne > 0) {
686
+ patterns.push({
687
+ type: 'blind_spot_n_plus_one',
688
+ category: 'blind_spots',
689
+ value: `n_plus_one_queries:${nPlusOne}`,
690
+ count: nPlusOne,
691
+ trustScore: 85,
692
+ });
693
+ }
694
+ if (hardcodedSecrets > 0) {
695
+ patterns.push({
696
+ type: 'blind_spot_hardcoded_secret',
697
+ category: 'blind_spots',
698
+ value: `hardcoded_secrets:${hardcodedSecrets}`,
699
+ count: hardcodedSecrets,
700
+ trustScore: 95,
701
+ });
702
+ }
703
+ if (missingNullCheck > 0) {
704
+ patterns.push({
705
+ type: 'blind_spot_null_check',
706
+ category: 'blind_spots',
707
+ value: `missing_null_checks:${missingNullCheck}`,
708
+ count: missingNullCheck,
709
+ trustScore: 80,
710
+ });
711
+ }
712
+ if (missingInputValidation > 0) {
713
+ patterns.push({
714
+ type: 'blind_spot_no_validation',
715
+ category: 'blind_spots',
716
+ value: `missing_input_validation:${missingInputValidation}`,
717
+ count: missingInputValidation,
718
+ trustScore: 85,
719
+ });
720
+ }
535
721
  }
536
722
  catch { /* AST parse may fail on broken files */ }
537
723
  return patterns;
@@ -602,8 +788,9 @@ function aggregatePatterns(analyses) {
602
788
  const key = `${pattern.type}:${pattern.value}`;
603
789
  const existing = patternMap.get(key);
604
790
  if (existing) {
605
- existing.count += pattern.count;
606
- existing.trustScore = Math.min(95, Math.round((existing.trustScore + pattern.trustScore) / 2));
791
+ const totalCount = existing.count + pattern.count;
792
+ existing.trustScore = Math.min(95, Math.round((existing.trustScore * existing.count + pattern.trustScore * pattern.count) / totalCount));
793
+ existing.count = totalCount;
607
794
  }
608
795
  else {
609
796
  patternMap.set(key, { ...pattern });
@@ -688,6 +875,23 @@ function buildDeveloperProfile(patterns) {
688
875
  strengths.push('Uses Zod validation for inputs');
689
876
  else
690
877
  improvements.push('Add input validation (Zod/Joi/Yup)');
878
+ // Blind spots v2
879
+ const nPlusOne = patterns.find(p => p.type === 'blind_spot_n_plus_one');
880
+ if (nPlusOne && nPlusOne.count > 0) {
881
+ improvements.push(`${nPlusOne.count} N+1 query patterns — DB calls inside loops`);
882
+ }
883
+ const secrets = patterns.find(p => p.type === 'blind_spot_hardcoded_secret');
884
+ if (secrets && secrets.count > 0) {
885
+ improvements.push(`${secrets.count} hardcoded secrets/API keys — move to env vars`);
886
+ }
887
+ const nullChecks = patterns.find(p => p.type === 'blind_spot_null_check');
888
+ if (nullChecks && nullChecks.count > 0) {
889
+ improvements.push(`${nullChecks.count} API responses used without null check`);
890
+ }
891
+ const noValidation = patterns.find(p => p.type === 'blind_spot_no_validation');
892
+ if (noValidation && noValidation.count > 0) {
893
+ improvements.push(`${noValidation.count} API handlers missing input validation`);
894
+ }
691
895
  if (asyncNoTry && asyncNoTry.count === 0 && patterns.find(p => p.type === 'error_trycatch')) {
692
896
  strengths.push('All async functions have try/catch error handling');
693
897
  }
@@ -713,54 +917,68 @@ function buildDeveloperProfile(patterns) {
713
917
  // ─── Database Storage ────────────────────────────────────────────────────────
714
918
  function ensureCodePatternTable() {
715
919
  const db = (0, database_1.getDb)();
716
- db.exec(`
717
- CREATE TABLE IF NOT EXISTS code_patterns (
718
- id INTEGER PRIMARY KEY AUTOINCREMENT,
719
- project TEXT NOT NULL,
720
- file_path TEXT NOT NULL,
721
- pattern_type TEXT NOT NULL,
722
- pattern_value TEXT NOT NULL,
723
- category TEXT NOT NULL,
724
- count INTEGER DEFAULT 1,
725
- trust_score INTEGER DEFAULT 50,
726
- language TEXT,
727
- created_at TEXT DEFAULT (datetime('now'))
728
- );
729
- CREATE INDEX IF NOT EXISTS idx_code_patterns_project ON code_patterns(project);
730
- CREATE INDEX IF NOT EXISTS idx_code_patterns_type ON code_patterns(pattern_type);
731
- CREATE INDEX IF NOT EXISTS idx_code_patterns_category ON code_patterns(category);
732
-
733
- CREATE TABLE IF NOT EXISTS developer_profiles (
734
- id INTEGER PRIMARY KEY AUTOINCREMENT,
735
- project TEXT NOT NULL UNIQUE,
736
- naming_style TEXT,
737
- import_style TEXT,
738
- export_style TEXT,
739
- error_handling TEXT,
740
- function_style TEXT,
741
- async_style TEXT,
742
- comment_style TEXT,
743
- indent_style TEXT,
744
- quote_style TEXT,
745
- semicolon_style TEXT,
746
- architecture_style TEXT,
747
- test_pattern TEXT,
748
- top_patterns TEXT,
749
- strengths TEXT,
750
- improvements TEXT,
751
- files_scanned INTEGER DEFAULT 0,
752
- total_lines INTEGER DEFAULT 0,
753
- languages TEXT,
754
- scanned_at TEXT DEFAULT (datetime('now'))
755
- );
756
- CREATE INDEX IF NOT EXISTS idx_dev_profile_project ON developer_profiles(project);
920
+ db.exec(`
921
+ CREATE TABLE IF NOT EXISTS code_patterns (
922
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
923
+ project TEXT NOT NULL,
924
+ file_path TEXT NOT NULL,
925
+ pattern_type TEXT NOT NULL,
926
+ pattern_value TEXT NOT NULL,
927
+ category TEXT NOT NULL,
928
+ count INTEGER DEFAULT 1,
929
+ trust_score INTEGER DEFAULT 50,
930
+ language TEXT,
931
+ created_at TEXT DEFAULT (datetime('now'))
932
+ );
933
+ CREATE INDEX IF NOT EXISTS idx_code_patterns_project ON code_patterns(project);
934
+ CREATE INDEX IF NOT EXISTS idx_code_patterns_type ON code_patterns(pattern_type);
935
+ CREATE INDEX IF NOT EXISTS idx_code_patterns_category ON code_patterns(category);
936
+
937
+ CREATE TABLE IF NOT EXISTS developer_profiles (
938
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
939
+ project TEXT NOT NULL UNIQUE,
940
+ naming_style TEXT,
941
+ import_style TEXT,
942
+ export_style TEXT,
943
+ error_handling TEXT,
944
+ function_style TEXT,
945
+ async_style TEXT,
946
+ comment_style TEXT,
947
+ indent_style TEXT,
948
+ quote_style TEXT,
949
+ semicolon_style TEXT,
950
+ architecture_style TEXT,
951
+ test_pattern TEXT,
952
+ top_patterns TEXT,
953
+ strengths TEXT,
954
+ improvements TEXT,
955
+ files_scanned INTEGER DEFAULT 0,
956
+ total_lines INTEGER DEFAULT 0,
957
+ languages TEXT,
958
+ scanned_at TEXT DEFAULT (datetime('now'))
959
+ );
960
+ CREATE INDEX IF NOT EXISTS idx_dev_profile_project ON developer_profiles(project);
757
961
  `);
758
962
  }
759
- function saveToDatabase(project, result) {
963
+ function saveToDatabase(project, result, projectPath) {
760
964
  const db = (0, database_1.getDb)();
761
965
  ensureCodePatternTable();
966
+ try {
967
+ const { ProjectRegistry } = require('./core/project-registry');
968
+ const registry = new ProjectRegistry();
969
+ const stack = Object.keys(result.languages || {}).slice(0, 3).join('+') || 'unknown';
970
+ registry.registerProject({
971
+ name: project,
972
+ path: projectPath || project,
973
+ stack,
974
+ fileCount: result.filesScanned,
975
+ });
976
+ }
977
+ catch (e) {
978
+ console.warn('[cell] Project registration failed (non-fatal):', e instanceof Error ? e.message : String(e));
979
+ }
762
980
  db.prepare('DELETE FROM code_patterns WHERE project = ?').run(project);
763
- const insertPattern = db.prepare('INSERT INTO code_patterns (project, file_path, pattern_type, pattern_value, category, count, confidence, language) VALUES (?, ?, ?, ?, ?, ?, ?, ?)');
981
+ const insertPattern = db.prepare('INSERT INTO code_patterns (project, file_path, pattern_type, pattern_value, category, count, trust_score, language) VALUES (?, ?, ?, ?, ?, ?, ?, ?)');
764
982
  const insertMany = db.transaction((patterns) => {
765
983
  for (const p of patterns) {
766
984
  insertPattern.run(project, 'scan', p.type, p.value, p.category, p.count, p.trustScore, '');
@@ -768,16 +986,23 @@ function saveToDatabase(project, result) {
768
986
  });
769
987
  insertMany(result.patterns);
770
988
  db.prepare('DELETE FROM developer_profiles WHERE project = ?').run(project);
771
- db.prepare(`INSERT INTO developer_profiles
772
- (project, naming_style, import_style, export_style, error_handling, function_style,
773
- async_style, comment_style, indent_style, quote_style, semicolon_style,
774
- architecture_style, test_pattern, top_patterns, strengths, improvements,
775
- files_scanned, total_lines, languages, scanned_at)
989
+ db.prepare(`INSERT INTO developer_profiles
990
+ (project, naming_style, import_style, export_style, error_handling, function_style,
991
+ async_style, comment_style, indent_style, quote_style, semicolon_style,
992
+ architecture_style, test_pattern, top_patterns, strengths, improvements,
993
+ files_scanned, total_lines, languages, scanned_at)
776
994
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))`)
777
995
  .run(project, result.profile.namingStyle, result.profile.importStyle, result.profile.exportStyle, result.profile.errorHandling, result.profile.functionStyle, result.profile.asyncStyle, result.profile.commentStyle, result.profile.indentStyle, result.profile.quoteStyle, result.profile.semicolonStyle, result.profile.architectureStyle, result.profile.testPattern, JSON.stringify(result.profile.topPatterns), JSON.stringify(result.profile.strengths), JSON.stringify(result.profile.improvements), result.filesScanned, result.totalLines, JSON.stringify(result.languages));
778
- db.prepare(`INSERT OR REPLACE INTO scans (project, scanned_at, file_count, line_count, stack, patterns, hubs, issues)
779
- VALUES (?, datetime('now'), ?, ?, ?, ?, ?, ?)`)
780
- .run(project, result.filesScanned, result.totalLines, JSON.stringify(result.languages), JSON.stringify(result.patterns.slice(0, 20)), JSON.stringify({}), JSON.stringify(result.profile.improvements));
996
+ try {
997
+ const stmt = db.prepare(`INSERT OR REPLACE INTO scans (project, scanned_at, file_count, line_count, stack, patterns, hubs, issues)
998
+ VALUES (?, datetime('now'), ?, ?, ?, ?, ?, ?)`);
999
+ if (stmt) {
1000
+ stmt.run(project, result.filesScanned, result.totalLines, JSON.stringify(result.languages), JSON.stringify(result.patterns.slice(0, 20)), JSON.stringify({}), JSON.stringify(result.profile.improvements));
1001
+ }
1002
+ }
1003
+ catch (e) {
1004
+ console.warn('[cell] scans table insert failed (schema mismatch, non-fatal):', e instanceof Error ? e.message : String(e));
1005
+ }
781
1006
  }
782
1007
  // ─── Public API ──────────────────────────────────────────────────────────────
783
1008
  function scanCodebase(dirPath, projectName) {
@@ -807,11 +1032,71 @@ function scanCodebase(dirPath, projectName) {
807
1032
  patterns,
808
1033
  profile,
809
1034
  };
810
- saveToDatabase(project, result);
1035
+ saveToDatabase(project, result, dirPath);
1036
+ // ─── PHASE 3: Build Knowledge Graphs (file deps + pattern entities) ───
1037
+ try {
1038
+ const { saveFileKnowledgeGraph, saveTechProficiency } = require('./core/knowledge-graph-store');
1039
+ const fileGraph = saveFileKnowledgeGraph(project, dirPath);
1040
+ // Tech proficiency: use languages + architecture style
1041
+ const archTech = profile.architectureStyle || 'unknown';
1042
+ saveTechProficiency(project, {
1043
+ technology: archTech,
1044
+ category: 'architecture',
1045
+ signalsCount: 1,
1046
+ proficiencyScore: 70,
1047
+ lastSeen: new Date().toISOString(),
1048
+ trend: 'stable',
1049
+ });
1050
+ for (const [lang, count] of Object.entries(languages)) {
1051
+ const score = Math.min(100, 40 + Math.log10(count + 1) * 20);
1052
+ saveTechProficiency(project, {
1053
+ technology: lang,
1054
+ category: 'language',
1055
+ signalsCount: count,
1056
+ proficiencyScore: Math.round(score),
1057
+ lastSeen: new Date().toISOString(),
1058
+ trend: 'stable',
1059
+ });
1060
+ }
1061
+ // Save pattern entities + relationships
1062
+ if (result.patterns && result.patterns.length > 0) {
1063
+ const { savePatternEntities, savePatternRelationships } = require('./core/knowledge-graph-store');
1064
+ const entities = result.patterns.slice(0, 200).map((p, i) => ({
1065
+ id: `${p.category || 'pattern'}-${i}`,
1066
+ type: p.category || 'pattern',
1067
+ label: p.value || '',
1068
+ weight: p.trustScore || 1,
1069
+ }));
1070
+ savePatternEntities(project, entities);
1071
+ // Co-occurrence: patterns in same category from same file are related
1072
+ const filePatterns = new Map();
1073
+ for (let i = 0; i < Math.min(200, result.patterns.length); i++) {
1074
+ const p = result.patterns[i];
1075
+ const f = p.file || 'global';
1076
+ const key = `${f}`;
1077
+ const arr = filePatterns.get(key) || [];
1078
+ arr.push(`${p.category || 'pattern'}-${i}`);
1079
+ filePatterns.set(key, arr);
1080
+ }
1081
+ const edges = [];
1082
+ for (const arr of filePatterns.values()) {
1083
+ for (let i = 0; i < arr.length; i++) {
1084
+ for (let j = i + 1; j < arr.length; j++) {
1085
+ edges.push({ from: arr[i], to: arr[j], type: 'co_occurs', weight: 1 });
1086
+ }
1087
+ }
1088
+ }
1089
+ if (edges.length > 0)
1090
+ savePatternRelationships(project, edges);
1091
+ }
1092
+ }
1093
+ catch (e) {
1094
+ console.warn('[cell] Knowledge graph build failed (non-fatal):', e instanceof Error ? e.message : String(e));
1095
+ }
1096
+ const hookResults = [];
811
1097
  // ─── CONNECT: Scanner → Behavioral Tracker ───────────────────────────────
812
1098
  try {
813
1099
  const { logError, logDecision, logContext } = require('./behavioral-tracker');
814
- // Log blind spots as errors
815
1100
  if (profile.improvements.length > 0) {
816
1101
  for (const imp of profile.improvements) {
817
1102
  logError({
@@ -823,7 +1108,6 @@ function scanCodebase(dirPath, projectName) {
823
1108
  });
824
1109
  }
825
1110
  }
826
- // Log architecture decision
827
1111
  logDecision({
828
1112
  project,
829
1113
  file: 'scan-auto',
@@ -831,33 +1115,106 @@ function scanCodebase(dirPath, projectName) {
831
1115
  approach: `Scan detected ${files.length} files, ${result.languages ? Object.keys(result.languages).join(', ') : 'unknown'}`,
832
1116
  worked: true,
833
1117
  });
834
- // Log current context
835
1118
  logContext({
836
1119
  project,
837
1120
  task: `Codebase scan completed — ${files.length} files, ${totalLines.toLocaleString()} lines`,
838
1121
  files: [dirPath],
839
1122
  });
1123
+ hookResults.push({ hook: 'behavioral-tracker', ok: true });
1124
+ }
1125
+ catch (e) {
1126
+ hookResults.push({ hook: 'behavioral-tracker', ok: false, error: String(e) });
1127
+ console.warn('[cell] Hook failed: behavioral-tracker:', e instanceof Error ? e.message : String(e));
840
1128
  }
841
- catch { /* behavioral tracker may not be initialized yet */ }
842
- // ─── CONNECT: Git Intelligence (behavior + journey + user analysis) ──────
1129
+ // ─── CONNECT: Git Intelligence ──────
843
1130
  try {
844
1131
  const { buildDeveloperIntelligence } = require('./developer-intelligence');
845
1132
  const gitIntel = buildDeveloperIntelligence(dirPath);
846
1133
  if (gitIntel) {
847
1134
  console.log(`[cell] Git intelligence: ${gitIntel.topBlindSpots.length} blind spots, ${gitIntel.topMistakes.length} repeat mistakes, score: ${gitIntel.behavioralScore}/100`);
848
1135
  }
1136
+ hookResults.push({ hook: 'developer-intelligence', ok: true });
849
1137
  }
850
- catch { /* git intelligence may not be available */ }
851
- // ─── CONNECT: Style Pull → Generate .cursorrules, CLAUDE.md, etc. ────
1138
+ catch (e) {
1139
+ hookResults.push({ hook: 'developer-intelligence', ok: false, error: String(e) });
1140
+ console.warn('[cell] Hook failed: developer-intelligence:', e instanceof Error ? e.message : String(e));
1141
+ }
1142
+ // ─── CONNECT: Style Pull ────
852
1143
  try {
853
1144
  const { writeStyleFiles, buildStyleSummary } = require('./style-pull');
854
1145
  const summary = buildStyleSummary(dirPath);
855
- const result = writeStyleFiles(dirPath, summary, { force: false });
856
- if (result.written.length > 0) {
857
- console.log(`[cell] Generated: ${result.written.join(', ')}`);
1146
+ const styleResult = writeStyleFiles(dirPath, summary, { force: false });
1147
+ if (styleResult.written.length > 0) {
1148
+ console.log(`[cell] Generated: ${styleResult.written.join(', ')}`);
1149
+ }
1150
+ hookResults.push({ hook: 'style-pull', ok: true });
1151
+ }
1152
+ catch (e) {
1153
+ hookResults.push({ hook: 'style-pull', ok: false, error: String(e) });
1154
+ console.warn('[cell] Hook failed: style-pull:', e instanceof Error ? e.message : String(e));
1155
+ }
1156
+ // ─── CONNECT: Stack Detector ──────────
1157
+ try {
1158
+ const dna = (0, stack_detector_1.detectProjectDNA)(dirPath);
1159
+ const projectName = dna.name || project;
1160
+ const db = (0, database_1.getDb)();
1161
+ db.prepare('INSERT OR REPLACE INTO code_patterns (project, file_path, pattern_type, pattern_value, category, count, trust_score, language) VALUES (?, ?, ?, ?, ?, ?, ?, ?)').run(projectName, 'stack-dna', 'project_dna', JSON.stringify(dna.stack), 'stack_detect', 1, dna.stack.trustScore, dna.stack.languages[0] || '');
1162
+ db.prepare('INSERT OR REPLACE INTO code_patterns (project, file_path, pattern_type, pattern_value, category, count, trust_score, language) VALUES (?, ?, ?, ?, ?, ?, ?, ?)').run(projectName, 'arch-dna', 'architecture', JSON.stringify(dna.architecture), 'architecture', 1, dna.architecture.confidence === 'high' ? 90 : 50, '');
1163
+ console.log('[cell] Stack: ' + dna.stack.frontend + ' + ' + dna.stack.backend + ', DB: ' + dna.stack.database.join(', ') + ', ORM: ' + dna.stack.orm + ', Test: ' + dna.stack.testing.join(', '));
1164
+ console.log('[cell] Arch: ' + dna.architecture.type + ' (' + dna.architecture.confidence + '), Entry: ' + dna.architecture.entryPoints.join(', '));
1165
+ hookResults.push({ hook: 'stack-detector', ok: true });
1166
+ }
1167
+ catch (e) {
1168
+ hookResults.push({ hook: 'stack-detector', ok: false, error: String(e) });
1169
+ console.warn('[cell] Hook failed: stack-detector:', e instanceof Error ? e.message : String(e));
1170
+ }
1171
+ // ─── CONNECT: Knowledge Graph ────────────────────────────────────
1172
+ try {
1173
+ const { buildKnowledgeGraph } = require('./knowledge-graph-builder');
1174
+ const graph = buildKnowledgeGraph(dirPath);
1175
+ console.log(`[cell] Graph: ${graph.nodes.length} nodes, ${graph.mostCoupled.length} most-coupled, Entry: ${graph.entryPoints.join(', ')}`);
1176
+ hookResults.push({ hook: 'knowledge-graph', ok: true });
1177
+ }
1178
+ catch (e) {
1179
+ hookResults.push({ hook: 'knowledge-graph', ok: false, error: String(e) });
1180
+ console.warn('[cell] Hook failed: knowledge-graph:', e instanceof Error ? e.message : String(e));
1181
+ }
1182
+ // ─── CONNECT: Blind Spot Engine ──────────────────────────────
1183
+ try {
1184
+ const { BlindSpotEngine } = require('./core/blind-spot-engine');
1185
+ const engine = new BlindSpotEngine(path.join(os.homedir(), '.fivo', 'cell'));
1186
+ let totalBlindSpots = 0;
1187
+ let totalHit = 0;
1188
+ let totalMissed = 0;
1189
+ let analyzed = 0;
1190
+ for (const analysis of analyses) {
1191
+ try {
1192
+ if (!analysis.filePath || typeof analysis.filePath !== 'string')
1193
+ continue;
1194
+ const content = fs.readFileSync(analysis.filePath, 'utf-8');
1195
+ const spots = engine.analyze(content, analysis.language);
1196
+ analyzed++;
1197
+ totalBlindSpots += spots.length;
1198
+ for (const s of spots) {
1199
+ totalHit += Math.round(s.hitRate);
1200
+ totalMissed += Math.round(s.missedRate);
1201
+ }
1202
+ }
1203
+ catch { }
858
1204
  }
1205
+ console.log(`[cell] Blind spots: ${totalBlindSpots} categories, ${totalMissed} missed checks, ${totalHit} hits across ${analyzed}/${analyses.length} files`);
1206
+ result.blindSpotStats = { totalBlindSpots, totalHit, totalMissed, files: analyzed };
1207
+ hookResults.push({ hook: 'blind-spot-engine', ok: true });
1208
+ }
1209
+ catch (e) {
1210
+ hookResults.push({ hook: 'blind-spot-engine', ok: false, error: String(e) });
1211
+ console.warn('[cell] Hook failed: blind-spot-engine:', e instanceof Error ? e.message : String(e));
1212
+ }
1213
+ const failed = hookResults.filter(h => !h.ok);
1214
+ if (failed.length > 0) {
1215
+ console.warn(`[cell] Scan completed with ${failed.length} hook failures: ${failed.map(f => f.hook).join(', ')}`);
859
1216
  }
860
- catch { /* style-pull may not be available */ }
1217
+ result.hookResults = hookResults;
861
1218
  return result;
862
1219
  }
863
1220
  function getDeveloperProfile(project) {