@massu/core 1.10.8 → 1.12.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.
Files changed (178) hide show
  1. package/CHANGELOG.md +1334 -0
  2. package/dist/adapter.js +6 -4
  3. package/dist/cli.js +581 -448
  4. package/dist/detect/adapters/.bundle-shasums.json +4 -7
  5. package/dist/detect/adapters/rails.js +6 -4
  6. package/dist/detect/adapters/spring.js +6 -4
  7. package/dist/detect/adapters/tree-sitter-loader.js +6 -4
  8. package/dist/hooks/auto-learning-pipeline.js +24 -1
  9. package/dist/hooks/classify-failure.js +32 -3
  10. package/dist/hooks/cost-tracker.js +24 -0
  11. package/dist/hooks/fix-detector.js +97 -1
  12. package/dist/hooks/incident-pipeline.js +30 -1
  13. package/dist/hooks/intent-suggester.js +6 -3
  14. package/dist/hooks/post-edit-context.js +24 -1
  15. package/dist/hooks/post-tool-use.js +67 -19
  16. package/dist/hooks/pre-compact.js +28 -3
  17. package/dist/hooks/pre-delete-check.js +53 -27
  18. package/dist/hooks/pre-tool-use-gate.js +962 -0
  19. package/dist/hooks/quality-event.js +24 -0
  20. package/dist/hooks/rule-enforcement-pipeline.js +24 -1
  21. package/dist/hooks/security-gate.js +29 -16
  22. package/dist/hooks/session-end.js +31 -6
  23. package/dist/hooks/session-start.js +8709 -8459
  24. package/dist/hooks/user-prompt.js +25 -1
  25. package/package.json +15 -18
  26. package/commands/README.md +0 -134
  27. package/src/adapter.ts +0 -121
  28. package/src/adr-generator.ts +0 -292
  29. package/src/analytics.ts +0 -373
  30. package/src/audit-trail.ts +0 -450
  31. package/src/backfill-sessions.ts +0 -182
  32. package/src/changelog-generator.ts +0 -178
  33. package/src/cli.ts +0 -246
  34. package/src/cloud-sync.ts +0 -234
  35. package/src/commands/adapters.ts +0 -824
  36. package/src/commands/changelog.ts +0 -165
  37. package/src/commands/config-check-drift.ts +0 -133
  38. package/src/commands/config-refresh.ts +0 -396
  39. package/src/commands/config-upgrade.ts +0 -127
  40. package/src/commands/doctor.ts +0 -485
  41. package/src/commands/hook-runner.ts +0 -145
  42. package/src/commands/init.ts +0 -1823
  43. package/src/commands/install-commands.ts +0 -692
  44. package/src/commands/install-hooks.ts +0 -27
  45. package/src/commands/permissions.ts +0 -150
  46. package/src/commands/refresh-log.ts +0 -37
  47. package/src/commands/show-template.ts +0 -65
  48. package/src/commands/template-engine.ts +0 -301
  49. package/src/commands/watch.ts +0 -430
  50. package/src/config.ts +0 -765
  51. package/src/cost-tracker.ts +0 -355
  52. package/src/db.ts +0 -370
  53. package/src/dependency-scorer.ts +0 -337
  54. package/src/detect/adapters/aspnet.ts +0 -4
  55. package/src/detect/adapters/discover.ts +0 -469
  56. package/src/detect/adapters/file-sampler.ts +0 -225
  57. package/src/detect/adapters/go-chi.ts +0 -4
  58. package/src/detect/adapters/index.ts +0 -51
  59. package/src/detect/adapters/nextjs-trpc.ts +0 -166
  60. package/src/detect/adapters/parse-guard.ts +0 -133
  61. package/src/detect/adapters/phoenix.ts +0 -4
  62. package/src/detect/adapters/python-django.ts +0 -208
  63. package/src/detect/adapters/python-fastapi.ts +0 -223
  64. package/src/detect/adapters/python-flask.ts +0 -235
  65. package/src/detect/adapters/query-helpers.ts +0 -170
  66. package/src/detect/adapters/rails.ts +0 -4
  67. package/src/detect/adapters/runner.ts +0 -284
  68. package/src/detect/adapters/spring.ts +0 -4
  69. package/src/detect/adapters/swift-swiftui.ts +0 -171
  70. package/src/detect/adapters/tree-sitter-loader.ts +0 -517
  71. package/src/detect/adapters/types.ts +0 -191
  72. package/src/detect/codebase-introspector.ts +0 -210
  73. package/src/detect/domain-inferrer.ts +0 -197
  74. package/src/detect/drift.ts +0 -199
  75. package/src/detect/framework-detector.ts +0 -307
  76. package/src/detect/index.ts +0 -209
  77. package/src/detect/manifest-registry.ts +0 -261
  78. package/src/detect/migrate.ts +0 -362
  79. package/src/detect/monorepo-detector.ts +0 -348
  80. package/src/detect/package-detector.ts +0 -828
  81. package/src/detect/passthrough.ts +0 -108
  82. package/src/detect/regex-fallback.ts +0 -449
  83. package/src/detect/source-dir-detector.ts +0 -297
  84. package/src/detect/vr-command-map.ts +0 -167
  85. package/src/docs-map.json +0 -100
  86. package/src/docs-tools.ts +0 -519
  87. package/src/domains.ts +0 -182
  88. package/src/hooks/auto-learning-pipeline.ts +0 -228
  89. package/src/hooks/classify-failure.ts +0 -259
  90. package/src/hooks/cost-tracker.ts +0 -66
  91. package/src/hooks/fix-detector.ts +0 -186
  92. package/src/hooks/incident-pipeline.ts +0 -190
  93. package/src/hooks/intent-suggester.ts +0 -131
  94. package/src/hooks/post-edit-context.ts +0 -91
  95. package/src/hooks/post-tool-use.ts +0 -413
  96. package/src/hooks/pre-compact.ts +0 -168
  97. package/src/hooks/pre-delete-check.ts +0 -243
  98. package/src/hooks/quality-event.ts +0 -127
  99. package/src/hooks/rule-enforcement-pipeline.ts +0 -159
  100. package/src/hooks/security-gate.ts +0 -153
  101. package/src/hooks/session-end.ts +0 -467
  102. package/src/hooks/session-start.ts +0 -436
  103. package/src/hooks/user-prompt.ts +0 -157
  104. package/src/import-resolver.ts +0 -226
  105. package/src/knowledge-db.ts +0 -176
  106. package/src/knowledge-indexer.ts +0 -708
  107. package/src/knowledge-tools.ts +0 -1414
  108. package/src/lib/fileLock.ts +0 -203
  109. package/src/lib/framework-supports.ts +0 -85
  110. package/src/lib/gitToplevel.ts +0 -22
  111. package/src/lib/hook-registry.ts +0 -43
  112. package/src/lib/installLock.ts +0 -66
  113. package/src/lib/memory-path.ts +0 -49
  114. package/src/lib/pidLiveness.ts +0 -67
  115. package/src/lib/settings-local.ts +0 -110
  116. package/src/lib/sql-table-names.ts +0 -71
  117. package/src/license.ts +0 -547
  118. package/src/lsp/auto-detect.ts +0 -98
  119. package/src/lsp/client.ts +0 -776
  120. package/src/lsp/enrich.ts +0 -127
  121. package/src/lsp/types.ts +0 -221
  122. package/src/memory-db.ts +0 -1614
  123. package/src/memory-file-ingest.ts +0 -128
  124. package/src/memory-tools.ts +0 -424
  125. package/src/middleware-tree.ts +0 -71
  126. package/src/observability-tools.ts +0 -343
  127. package/src/observation-extractor.ts +0 -418
  128. package/src/page-deps.ts +0 -285
  129. package/src/permissions.ts +0 -422
  130. package/src/prompt-analyzer.ts +0 -332
  131. package/src/python/coupling-detector.ts +0 -125
  132. package/src/python/domain-enforcer.ts +0 -84
  133. package/src/python/impact-analyzer.ts +0 -96
  134. package/src/python/import-parser.ts +0 -244
  135. package/src/python/import-resolver.ts +0 -136
  136. package/src/python/migration-indexer.ts +0 -116
  137. package/src/python/migration-parser.ts +0 -332
  138. package/src/python/model-indexer.ts +0 -71
  139. package/src/python/model-parser.ts +0 -279
  140. package/src/python/route-indexer.ts +0 -59
  141. package/src/python/route-parser.ts +0 -317
  142. package/src/python-tools.ts +0 -630
  143. package/src/regression-detector.ts +0 -319
  144. package/src/rules.ts +0 -57
  145. package/src/schema-mapper.ts +0 -232
  146. package/src/security/adapter-origin.ts +0 -130
  147. package/src/security/adapter-verifier.ts +0 -319
  148. package/src/security/atomic-write.ts +0 -164
  149. package/src/security/fetcher.ts +0 -200
  150. package/src/security/install-tracking.ts +0 -319
  151. package/src/security/license-pubkey.generated.ts +0 -30
  152. package/src/security/license-response-verifier.ts +0 -149
  153. package/src/security/local-fingerprint.ts +0 -225
  154. package/src/security/manifest-cache.ts +0 -333
  155. package/src/security/manifest-schema.ts +0 -129
  156. package/src/security/registry-pubkey.generated.ts +0 -35
  157. package/src/security/telemetry.ts +0 -320
  158. package/src/security-scorer.ts +0 -405
  159. package/src/security-utils.ts +0 -133
  160. package/src/sentinel-db.ts +0 -580
  161. package/src/sentinel-scanner.ts +0 -406
  162. package/src/sentinel-tools.ts +0 -513
  163. package/src/sentinel-types.ts +0 -140
  164. package/src/server-dispatch.ts +0 -225
  165. package/src/server.ts +0 -107
  166. package/src/session-archiver.ts +0 -111
  167. package/src/session-state-generator.ts +0 -174
  168. package/src/team-knowledge.ts +0 -407
  169. package/src/tool-db-needs.ts +0 -232
  170. package/src/tools.ts +0 -1206
  171. package/src/transcript-parser.ts +0 -458
  172. package/src/trpc-index.ts +0 -215
  173. package/src/validate-features-runner.ts +0 -107
  174. package/src/validation-engine.ts +0 -358
  175. package/src/watch/daemon.ts +0 -385
  176. package/src/watch/lockfile-detector.ts +0 -65
  177. package/src/watch/paths.ts +0 -279
  178. package/src/watch/state.ts +0 -178
package/dist/cli.js CHANGED
@@ -146,6 +146,8 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
146
146
  accessScopes: parsed.accessScopes,
147
147
  domains: parsed.domains,
148
148
  rules: parsed.rules,
149
+ // P-M-036: customer-authored CR-style governance rules.
150
+ governance_rules: parsed.governance_rules,
149
151
  analytics: parsed.analytics,
150
152
  governance: parsed.governance,
151
153
  security: parsed.security,
@@ -548,6 +550,22 @@ var init_config = __esm({
548
550
  accessScopes: z.array(z.string()).optional(),
549
551
  domains: z.array(DomainConfigSchema).default([]),
550
552
  rules: z.array(PatternRuleConfigSchema).default([]),
553
+ // P-M-036 (plan-stage-d-medium-sweep): customer-authored CR-style
554
+ // governance rules. DISTINCT from `rules:` above (path-scoped lint hints
555
+ // used by pattern-scanner). At config-refresh time these entries are
556
+ // loaded into the `knowledge_rules` SQLite table with
557
+ // `source = 'customer-config'` so `massu_knowledge_rule` and the
558
+ // governance docs surface customer-defined rules alongside framework CRs.
559
+ governance_rules: z.array(
560
+ z.object({
561
+ id: z.string().min(1, "governance_rules[].id is required"),
562
+ title: z.string().min(1, "governance_rules[].title is required"),
563
+ description: z.string().min(1, "governance_rules[].description is required"),
564
+ vr_type: z.string().default("VR-CUSTOM"),
565
+ reference_path: z.string().optional(),
566
+ severity: z.enum(["critical", "high", "medium", "low", "info"]).default("medium")
567
+ }).passthrough()
568
+ ).default([]),
551
569
  analytics: AnalyticsConfigSchema,
552
570
  governance: GovernanceConfigSchema,
553
571
  security: SecurityConfigSchema,
@@ -581,6 +599,7 @@ var init_config = __esm({
581
599
  // src/memory-db.ts
582
600
  var memory_db_exports = {};
583
601
  __export(memory_db_exports, {
602
+ TOOL_COST_EVENTS_RETENTION_DAYS: () => TOOL_COST_EVENTS_RETENTION_DAYS,
584
603
  addConversationTurn: () => addConversationTurn,
585
604
  addFailureClass: () => addFailureClass,
586
605
  addObservation: () => addObservation,
@@ -614,6 +633,7 @@ __export(memory_db_exports, {
614
633
  linkSessionToTask: () => linkSessionToTask,
615
634
  pruneOldConversationTurns: () => pruneOldConversationTurns,
616
635
  pruneOldObservations: () => pruneOldObservations,
636
+ pruneToolCostEvents: () => pruneToolCostEvents,
617
637
  removePendingSync: () => removePendingSync,
618
638
  sanitizeFts5Query: () => sanitizeFts5Query,
619
639
  scoreFailureClasses: () => scoreFailureClasses,
@@ -642,6 +662,12 @@ function getMemoryDb() {
642
662
  initMemorySchema(db);
643
663
  return db;
644
664
  }
665
+ function pruneToolCostEvents(db) {
666
+ const result = db.prepare(
667
+ `DELETE FROM tool_cost_events WHERE created_at < datetime('now', '-' || ? || ' days')`
668
+ ).run(TOOL_COST_EVENTS_RETENTION_DAYS);
669
+ return result.changes;
670
+ }
645
671
  function initMemorySchema(db) {
646
672
  db.exec(`
647
673
  -- Sessions table (linked to Claude Code session IDs)
@@ -1116,6 +1142,12 @@ function initMemorySchema(db) {
1116
1142
  features TEXT DEFAULT '[]'
1117
1143
  );
1118
1144
  `);
1145
+ const licenseCacheCols = db.prepare(`PRAGMA table_info(license_cache)`).all();
1146
+ if (!licenseCacheCols.some((c2) => c2.name === "signed_payload_json")) {
1147
+ db.exec(
1148
+ `ALTER TABLE license_cache ADD COLUMN signed_payload_json TEXT NOT NULL DEFAULT ''`
1149
+ );
1150
+ }
1119
1151
  db.exec(`
1120
1152
  CREATE TABLE IF NOT EXISTS failure_classes (
1121
1153
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -1141,7 +1173,7 @@ function enqueueSyncPayload(db, payload) {
1141
1173
  }
1142
1174
  function dequeuePendingSync(db, limit = 10) {
1143
1175
  const stale = db.prepare(
1144
- "SELECT id, retry_count, last_error FROM pending_sync WHERE retry_count >= 10"
1176
+ "SELECT id, retry_count, last_error FROM pending_sync WHERE retry_count >= 10 LIMIT 10000"
1145
1177
  ).all();
1146
1178
  if (stale.length > 0) {
1147
1179
  const ids = stale.map((s) => s.id);
@@ -1380,16 +1412,17 @@ function getSessionsByTask(db, taskId) {
1380
1412
  SELECT session_id, status, started_at, ended_at, plan_phase
1381
1413
  FROM sessions WHERE task_id = ?
1382
1414
  ORDER BY started_at_epoch DESC
1415
+ LIMIT 10000
1383
1416
  `).all(taskId);
1384
1417
  }
1385
1418
  function getCrossTaskProgress(db, taskId) {
1386
1419
  const sessions = db.prepare(`
1387
- SELECT session_id FROM sessions WHERE task_id = ?
1420
+ SELECT session_id FROM sessions WHERE task_id = ? LIMIT 10000
1388
1421
  `).all(taskId);
1389
1422
  const merged = {};
1390
1423
  for (const session of sessions) {
1391
1424
  const summaries = db.prepare(`
1392
- SELECT plan_progress FROM session_summaries WHERE session_id = ?
1425
+ SELECT plan_progress FROM session_summaries WHERE session_id = ? LIMIT 10000
1393
1426
  `).all(session.session_id);
1394
1427
  for (const summary of summaries) {
1395
1428
  try {
@@ -1688,10 +1721,12 @@ function scoreFailureClasses(db, matchText, filePath, promptContext, weights) {
1688
1721
  }
1689
1722
  return bestMatch;
1690
1723
  }
1724
+ var TOOL_COST_EVENTS_RETENTION_DAYS;
1691
1725
  var init_memory_db = __esm({
1692
1726
  "src/memory-db.ts"() {
1693
1727
  "use strict";
1694
1728
  init_config();
1729
+ TOOL_COST_EVENTS_RETENTION_DAYS = 90;
1695
1730
  }
1696
1731
  });
1697
1732
 
@@ -2658,6 +2693,40 @@ var init_install_commands = __esm({
2658
2693
  }
2659
2694
  });
2660
2695
 
2696
+ // src/lib/hook-timeouts.ts
2697
+ var HOOK_TIMEOUTS;
2698
+ var init_hook_timeouts = __esm({
2699
+ "src/lib/hook-timeouts.ts"() {
2700
+ "use strict";
2701
+ HOOK_TIMEOUTS = {
2702
+ // Session lifecycle
2703
+ "session-start": 10,
2704
+ "session-end": 15,
2705
+ "pre-compact": 10,
2706
+ // PreToolUse hooks
2707
+ // P-E-019: `pre-tool-use-gate` is the consolidated PreToolUse hook
2708
+ // (combines security-gate + pre-delete-check into one spawn). The
2709
+ // individual entries below are kept for back-compat with operator
2710
+ // settings.json files that still reference them directly.
2711
+ "pre-tool-use-gate": 5,
2712
+ "security-gate": 5,
2713
+ "pre-delete-check": 5,
2714
+ // PostToolUse hooks
2715
+ "post-tool-use": 10,
2716
+ "quality-event": 5,
2717
+ "cost-tracker": 5,
2718
+ "post-edit-context": 5,
2719
+ // Failure-handling hooks
2720
+ "fix-detector": 5,
2721
+ "classify-failure": 5,
2722
+ "incident-pipeline": 5,
2723
+ "rule-enforcement-pipeline": 5,
2724
+ // Auto-learning
2725
+ "auto-learning-pipeline": 10
2726
+ };
2727
+ }
2728
+ });
2729
+
2661
2730
  // src/detect/manifest-registry.ts
2662
2731
  var manifest_registry_exports = {};
2663
2732
  __export(manifest_registry_exports, {
@@ -10567,8 +10636,9 @@ function evictBeyondRetainCount(retain = getCacheRetainCount()) {
10567
10636
  continue;
10568
10637
  }
10569
10638
  if (stat.isSymbolicLink() || !stat.isFile()) {
10570
- console.error(
10571
- `[tree-sitter-loader] cache eviction skipped non-regular file: ${path} (possible symlink attack \u2014 see Phase 3.5 finding F-008).`
10639
+ process.stderr.write(
10640
+ `[tree-sitter-loader] cache eviction skipped non-regular file: ${path} (possible symlink attack \u2014 see Phase 3.5 finding F-008).
10641
+ `
10572
10642
  );
10573
10643
  continue;
10574
10644
  }
@@ -10681,8 +10751,9 @@ async function loadGrammar(language, options = {}) {
10681
10751
  }
10682
10752
  evictBeyondRetainCount();
10683
10753
  } catch (e2) {
10684
- console.error(
10685
- `[tree-sitter-loader] cache write failed for ${language}: ${e2 instanceof Error ? e2.message : String(e2)} \u2014 loading directly from memory.`
10754
+ process.stderr.write(
10755
+ `[tree-sitter-loader] cache write failed for ${language}: ${e2 instanceof Error ? e2.message : String(e2)} \u2014 loading directly from memory.
10756
+ `
10686
10757
  );
10687
10758
  }
10688
10759
  const lang = await Language.load(body);
@@ -11509,14 +11580,6 @@ var init_python_flask = __esm({
11509
11580
  }
11510
11581
  });
11511
11582
 
11512
- // src/detect/adapters/go-chi.ts
11513
- import { goChiAdapter } from "@massu/adapter-go-chi";
11514
- var init_go_chi = __esm({
11515
- "src/detect/adapters/go-chi.ts"() {
11516
- "use strict";
11517
- }
11518
- });
11519
-
11520
11583
  // src/detect/adapters/rails.ts
11521
11584
  import { railsAdapter } from "@massu/adapter-rails";
11522
11585
  var init_rails = __esm({
@@ -11525,22 +11588,6 @@ var init_rails = __esm({
11525
11588
  }
11526
11589
  });
11527
11590
 
11528
- // src/detect/adapters/phoenix.ts
11529
- import { phoenixAdapter } from "@massu/adapter-phoenix";
11530
- var init_phoenix = __esm({
11531
- "src/detect/adapters/phoenix.ts"() {
11532
- "use strict";
11533
- }
11534
- });
11535
-
11536
- // src/detect/adapters/aspnet.ts
11537
- import { aspnetAdapter } from "@massu/adapter-aspnet";
11538
- var init_aspnet = __esm({
11539
- "src/detect/adapters/aspnet.ts"() {
11540
- "use strict";
11541
- }
11542
- });
11543
-
11544
11591
  // src/detect/adapters/spring.ts
11545
11592
  import { springAdapter } from "@massu/adapter-spring";
11546
11593
  var init_spring = __esm({
@@ -11773,10 +11820,7 @@ var init_codebase_introspector = __esm({
11773
11820
  init_nextjs_trpc();
11774
11821
  init_swift_swiftui();
11775
11822
  init_python_flask();
11776
- init_go_chi();
11777
11823
  init_rails();
11778
- init_phoenix();
11779
- init_aspnet();
11780
11824
  init_spring();
11781
11825
  FIRST_PARTY_ADAPTERS = [
11782
11826
  pythonFastApiAdapter,
@@ -11784,10 +11828,7 @@ var init_codebase_introspector = __esm({
11784
11828
  pythonFlaskAdapter,
11785
11829
  nextjsTrpcAdapter,
11786
11830
  swiftSwiftUiAdapter,
11787
- goChiAdapter,
11788
11831
  railsAdapter,
11789
- phoenixAdapter,
11790
- aspnetAdapter,
11791
11832
  springAdapter
11792
11833
  ];
11793
11834
  }
@@ -13648,46 +13689,50 @@ function resolveHooksDir() {
13648
13689
  function hookCmd(version, hookName) {
13649
13690
  return `npx -y @massu/core@${version} hook-runner ${hookName}`;
13650
13691
  }
13692
+ function getHookTimeout(name) {
13693
+ return HOOK_TIMEOUTS[name] ?? 5;
13694
+ }
13651
13695
  function buildHooksConfig(_hooksDir) {
13652
13696
  const version = getInstallerVersion();
13653
13697
  return {
13654
13698
  SessionStart: [
13655
13699
  {
13656
13700
  hooks: [
13657
- { type: "command", command: hookCmd(version, "session-start"), timeout: 10 }
13701
+ { type: "command", command: hookCmd(version, "session-start"), timeout: getHookTimeout("session-start") }
13658
13702
  ]
13659
13703
  }
13660
13704
  ],
13661
13705
  PreToolUse: [
13706
+ // P-E-019 (plan-stage-e-low-info-sweep): consolidated PreToolUse
13707
+ // gate. Single hook spawn covers BOTH security-gate (Bash
13708
+ // dangerous-pattern + Write/Edit protected-path + Python
13709
+ // dangerous-code) AND pre-delete-check (sentinel feature impact +
13710
+ // Python import graph). Cuts ~200ms of cold-start spawn overhead
13711
+ // per tool call. Matcher includes both Bash + Write so the single
13712
+ // hook fires once for every operation either check cares about.
13662
13713
  {
13663
- matcher: "Bash",
13664
- hooks: [
13665
- { type: "command", command: hookCmd(version, "security-gate"), timeout: 5 }
13666
- ]
13667
- },
13668
- {
13669
- matcher: "Bash|Write",
13714
+ matcher: "Bash|Write|Edit",
13670
13715
  hooks: [
13671
- { type: "command", command: hookCmd(version, "pre-delete-check"), timeout: 5 }
13716
+ { type: "command", command: hookCmd(version, "pre-tool-use-gate"), timeout: getHookTimeout("pre-tool-use-gate") }
13672
13717
  ]
13673
13718
  }
13674
13719
  ],
13675
13720
  PostToolUse: [
13676
13721
  {
13677
13722
  hooks: [
13678
- { type: "command", command: hookCmd(version, "post-tool-use"), timeout: 10 },
13679
- { type: "command", command: hookCmd(version, "quality-event"), timeout: 5 },
13680
- { type: "command", command: hookCmd(version, "cost-tracker"), timeout: 5 }
13723
+ { type: "command", command: hookCmd(version, "post-tool-use"), timeout: getHookTimeout("post-tool-use") },
13724
+ { type: "command", command: hookCmd(version, "quality-event"), timeout: getHookTimeout("quality-event") },
13725
+ { type: "command", command: hookCmd(version, "cost-tracker"), timeout: getHookTimeout("cost-tracker") }
13681
13726
  ]
13682
13727
  },
13683
13728
  {
13684
13729
  matcher: "Edit|Write",
13685
13730
  hooks: [
13686
- { type: "command", command: hookCmd(version, "post-edit-context"), timeout: 5 },
13731
+ { type: "command", command: hookCmd(version, "post-edit-context"), timeout: getHookTimeout("post-edit-context") },
13687
13732
  // Auto-learning pipeline — classifies failures and detects fixes on
13688
13733
  // file changes. See Phase 5-6 of the autodetect plan.
13689
- { type: "command", command: hookCmd(version, "fix-detector"), timeout: 5 },
13690
- { type: "command", command: hookCmd(version, "classify-failure"), timeout: 5 }
13734
+ { type: "command", command: hookCmd(version, "fix-detector"), timeout: getHookTimeout("fix-detector") },
13735
+ { type: "command", command: hookCmd(version, "classify-failure"), timeout: getHookTimeout("classify-failure") }
13691
13736
  ]
13692
13737
  },
13693
13738
  {
@@ -13695,32 +13740,32 @@ function buildHooksConfig(_hooksDir) {
13695
13740
  hooks: [
13696
13741
  // Incident + rule enforcement pipelines fire on Write-only (incidents
13697
13742
  // are authored as .md files; rules are enforced after new-file drops).
13698
- { type: "command", command: hookCmd(version, "incident-pipeline"), timeout: 5 },
13699
- { type: "command", command: hookCmd(version, "rule-enforcement-pipeline"), timeout: 5 }
13743
+ { type: "command", command: hookCmd(version, "incident-pipeline"), timeout: getHookTimeout("incident-pipeline") },
13744
+ { type: "command", command: hookCmd(version, "rule-enforcement-pipeline"), timeout: getHookTimeout("rule-enforcement-pipeline") }
13700
13745
  ]
13701
13746
  }
13702
13747
  ],
13703
13748
  Stop: [
13704
13749
  {
13705
13750
  hooks: [
13706
- { type: "command", command: hookCmd(version, "session-end"), timeout: 15 },
13751
+ { type: "command", command: hookCmd(version, "session-end"), timeout: getHookTimeout("session-end") },
13707
13752
  // Session-end auto-learning aggregation (failure-class roll-up).
13708
- { type: "command", command: hookCmd(version, "auto-learning-pipeline"), timeout: 10 }
13753
+ { type: "command", command: hookCmd(version, "auto-learning-pipeline"), timeout: getHookTimeout("auto-learning-pipeline") }
13709
13754
  ]
13710
13755
  }
13711
13756
  ],
13712
13757
  PreCompact: [
13713
13758
  {
13714
13759
  hooks: [
13715
- { type: "command", command: hookCmd(version, "pre-compact"), timeout: 10 }
13760
+ { type: "command", command: hookCmd(version, "pre-compact"), timeout: getHookTimeout("pre-compact") }
13716
13761
  ]
13717
13762
  }
13718
13763
  ],
13719
13764
  UserPromptSubmit: [
13720
13765
  {
13721
13766
  hooks: [
13722
- { type: "command", command: hookCmd(version, "user-prompt"), timeout: 5 },
13723
- { type: "command", command: hookCmd(version, "intent-suggester"), timeout: 5 }
13767
+ { type: "command", command: hookCmd(version, "user-prompt"), timeout: getHookTimeout("user-prompt") },
13768
+ { type: "command", command: hookCmd(version, "intent-suggester"), timeout: getHookTimeout("intent-suggester") }
13724
13769
  ]
13725
13770
  }
13726
13771
  ]
@@ -13905,7 +13950,15 @@ function summarizeDetection2(detection) {
13905
13950
  const dirs = detection.sourceDirs[lang]?.source_dirs ?? [];
13906
13951
  const dirSuffix = dirs.length > 0 ? ` in ${dirs.join(",")}` : "";
13907
13952
  const fwName = fw?.framework ?? "no-framework";
13908
- parts.push(`${capitalize(lang)}/${fwName}${dirSuffix}`);
13953
+ const slotParts = [];
13954
+ const router = fw?.router;
13955
+ const orm = fw?.orm;
13956
+ const ui = fw?.ui;
13957
+ if (router && router !== "none") slotParts.push(`router=${router}`);
13958
+ if (orm && orm !== "none") slotParts.push(`orm=${orm}`);
13959
+ if (ui && ui !== "none") slotParts.push(`ui=${ui}`);
13960
+ const slotSuffix = slotParts.length > 0 ? ` (${slotParts.join(", ")})` : "";
13961
+ parts.push(`${capitalize(lang)}/${fwName}${slotSuffix}${dirSuffix}`);
13909
13962
  }
13910
13963
  const mono = detection.monorepo.type;
13911
13964
  const monoSuffix = mono && mono !== "single" ? ` [${mono} monorepo]` : "";
@@ -14178,6 +14231,7 @@ var init_init = __esm({
14178
14231
  init_install_commands();
14179
14232
  init_settings_local();
14180
14233
  init_memory_path();
14234
+ init_hook_timeouts();
14181
14235
  init_detect();
14182
14236
  init_drift();
14183
14237
  __filename2 = fileURLToPath2(import.meta.url);
@@ -14216,7 +14270,7 @@ var init_license_pubkey_generated = __esm({
14216
14270
  });
14217
14271
 
14218
14272
  // src/security/license-response-verifier.ts
14219
- import { createPublicKey } from "crypto";
14273
+ import { createPublicKey, verify as cryptoVerify } from "crypto";
14220
14274
  function verifyLicenseResponse(payload) {
14221
14275
  if (!KNOWN_LICENSE_PUBKEY_FINGERPRINTS.has(LICENSE_PUBKEY_FINGERPRINT_HEX)) {
14222
14276
  return {
@@ -14263,8 +14317,7 @@ function verifyLicenseResponse(payload) {
14263
14317
  ]);
14264
14318
  const der = Buffer.concat([spkiPrefix, Buffer.from(LICENSE_PUBKEY_ED25519)]);
14265
14319
  const pubkey = createPublicKey({ key: der, format: "der", type: "spki" });
14266
- const { verify } = __require("crypto");
14267
- const ok = verify(
14320
+ const ok = cryptoVerify(
14268
14321
  null,
14269
14322
  Buffer.from(canonical, "utf-8"),
14270
14323
  pubkey,
@@ -14311,13 +14364,31 @@ function isToolAllowed(toolName, userTier) {
14311
14364
  return tierLevel(userTier) >= tierLevel(requiredTier);
14312
14365
  }
14313
14366
  function annotateToolDefinitions(defs) {
14367
+ for (const def of defs) {
14368
+ const expectedTier = getToolTier(def.name);
14369
+ const incomingTier = def.annotations?.tier;
14370
+ if (incomingTier !== void 0 && incomingTier !== expectedTier) {
14371
+ throw new Error(
14372
+ `TOOL_TIER_MAP bijection violation (P-M-033): tool "${def.name}" has annotations.tier="${incomingTier}" but TOOL_TIER_MAP says "${expectedTier}". TOOL_TIER_MAP is the SoT \u2014 fix the source instead.`
14373
+ );
14374
+ }
14375
+ const expectedPrefix = TIER_LABELS[expectedTier];
14376
+ for (const otherPrefix of Object.values(TIER_LABELS)) {
14377
+ if (otherPrefix && otherPrefix !== expectedPrefix && def.description.startsWith(otherPrefix)) {
14378
+ throw new Error(
14379
+ `TOOL_TIER_MAP bijection violation (P-M-033): tool "${def.name}" description starts with "${otherPrefix}" but TOOL_TIER_MAP tier is "${expectedTier}" (expected prefix "${expectedPrefix}").`
14380
+ );
14381
+ }
14382
+ }
14383
+ }
14314
14384
  return defs.map((def) => {
14315
14385
  const tier = getToolTier(def.name);
14316
14386
  const label = TIER_LABELS[tier];
14387
+ const stripped = label && def.description.startsWith(label) ? def.description.slice(label.length) : def.description;
14317
14388
  return {
14318
14389
  ...def,
14319
14390
  annotations: { ...def.annotations ?? {}, tier },
14320
- description: label ? `${label}${def.description}` : def.description
14391
+ description: label ? `${label}${stripped}` : stripped
14321
14392
  };
14322
14393
  });
14323
14394
  }
@@ -14329,17 +14400,14 @@ async function validateLicense(apiKey) {
14329
14400
  const memDb = getMemoryDb();
14330
14401
  try {
14331
14402
  const cached = memDb.prepare(
14332
- "SELECT tier, valid_until, last_validated, features FROM license_cache WHERE api_key_hash = ?"
14403
+ "SELECT tier, valid_until, last_validated, features, signed_payload_json FROM license_cache WHERE api_key_hash = ?"
14333
14404
  ).get(keyHash);
14334
- if (cached) {
14405
+ const trusted = cached ? readTrustedCache(cached) : null;
14406
+ if (cached && trusted) {
14335
14407
  const lastValidated = new Date(cached.last_validated);
14336
14408
  const hourAgo = new Date(Date.now() - 60 * 60 * 1e3);
14337
14409
  if (lastValidated > hourAgo) {
14338
- return {
14339
- tier: cached.tier,
14340
- validUntil: cached.valid_until,
14341
- features: JSON.parse(cached.features || "[]")
14342
- };
14410
+ return trusted;
14343
14411
  }
14344
14412
  }
14345
14413
  const config = getConfig();
@@ -14368,7 +14436,13 @@ async function validateLicense(apiKey) {
14368
14436
  const tier = data.plan ? PLAN_TO_TIER_MAP[data.plan] ?? "free" : data.tier ?? "free";
14369
14437
  const validUntil = data.validUntil ?? "";
14370
14438
  const features = data.features ?? [];
14371
- updateLicenseCache(apiKey, tier, validUntil, features);
14439
+ updateLicenseCache(
14440
+ apiKey,
14441
+ tier,
14442
+ validUntil,
14443
+ features,
14444
+ sigResult.kind === "valid" ? data : null
14445
+ );
14372
14446
  return { tier, validUntil, features };
14373
14447
  }
14374
14448
  return { tier: "free", validUntil: "", features: [] };
@@ -14376,15 +14450,11 @@ async function validateLicense(apiKey) {
14376
14450
  } catch {
14377
14451
  }
14378
14452
  }
14379
- if (cached) {
14453
+ if (cached && trusted) {
14380
14454
  const lastValidated = new Date(cached.last_validated);
14381
14455
  const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3);
14382
14456
  if (lastValidated > sevenDaysAgo) {
14383
- return {
14384
- tier: cached.tier,
14385
- validUntil: cached.valid_until,
14386
- features: JSON.parse(cached.features || "[]")
14387
- };
14457
+ return trusted;
14388
14458
  }
14389
14459
  }
14390
14460
  return { tier: "free", validUntil: "", features: [] };
@@ -14392,18 +14462,49 @@ async function validateLicense(apiKey) {
14392
14462
  memDb.close();
14393
14463
  }
14394
14464
  }
14395
- function updateLicenseCache(apiKey, tier, validUntil, features = []) {
14465
+ function updateLicenseCache(apiKey, tier, validUntil, features = [], signedPayload = null) {
14396
14466
  const keyHash = createHash5("sha256").update(apiKey).digest("hex");
14397
14467
  const memDb = getMemoryDb();
14398
14468
  try {
14399
14469
  memDb.prepare(`
14400
- INSERT OR REPLACE INTO license_cache (api_key_hash, tier, valid_until, last_validated, features)
14401
- VALUES (?, ?, ?, datetime('now'), ?)
14402
- `).run(keyHash, tier, validUntil, JSON.stringify(features));
14470
+ INSERT OR REPLACE INTO license_cache (api_key_hash, tier, valid_until, last_validated, features, signed_payload_json)
14471
+ VALUES (?, ?, ?, datetime('now'), ?, ?)
14472
+ `).run(
14473
+ keyHash,
14474
+ tier,
14475
+ validUntil,
14476
+ JSON.stringify(features),
14477
+ signedPayload ? JSON.stringify(signedPayload) : ""
14478
+ );
14403
14479
  } finally {
14404
14480
  memDb.close();
14405
14481
  }
14406
14482
  }
14483
+ function readTrustedCache(cached) {
14484
+ if (!cached.signed_payload_json) {
14485
+ if (isLicenseSignatureRequired()) return null;
14486
+ warnLicenseSigOnce("cache_unsigned_transition");
14487
+ return {
14488
+ tier: cached.tier,
14489
+ validUntil: cached.valid_until,
14490
+ features: JSON.parse(cached.features || "[]")
14491
+ };
14492
+ }
14493
+ let parsed;
14494
+ try {
14495
+ parsed = JSON.parse(cached.signed_payload_json);
14496
+ } catch {
14497
+ return null;
14498
+ }
14499
+ const result = verifyLicenseResponse(parsed);
14500
+ if (result.kind !== "valid") return null;
14501
+ const verifiedPlan = typeof parsed.plan === "string" ? parsed.plan : null;
14502
+ const verifiedTierField = typeof parsed.tier === "string" ? parsed.tier : null;
14503
+ const tier = verifiedPlan ? PLAN_TO_TIER_MAP[verifiedPlan] ?? "free" : verifiedTierField ?? "free";
14504
+ const validUntil = typeof parsed.validUntil === "string" ? parsed.validUntil : "";
14505
+ const features = Array.isArray(parsed.features) ? parsed.features : [];
14506
+ return { tier, validUntil, features };
14507
+ }
14407
14508
  async function getCurrentTier() {
14408
14509
  if (cachedTier && Date.now() - cachedTierTimestamp < IN_MEMORY_CACHE_TTL_MS) {
14409
14510
  return cachedTier.tier;
@@ -14511,6 +14612,12 @@ var init_license = __esm({
14511
14612
  coupling_check: "free",
14512
14613
  memory_search: "free",
14513
14614
  memory_ingest: "free",
14615
+ // P-M-042 (plan-stage-d-medium-sweep): memory_backfill serves the
14616
+ // customer's MEMORY.md ingestion workflow and was registered in
14617
+ // memory-tools.ts but missing from TOOL_TIER_MAP. The bijection
14618
+ // drift-guard (P-M-033) would have caught it on next add; we land it
14619
+ // explicitly as Free since it's a customer-owned-data ingestion path.
14620
+ memory_backfill: "free",
14514
14621
  regression_risk: "free",
14515
14622
  feature_health: "free",
14516
14623
  license_status: "free",
@@ -14586,7 +14693,7 @@ var init_license = __esm({
14586
14693
  cloud_enterprise: "enterprise"
14587
14694
  };
14588
14695
  TIER_LABELS = {
14589
- free: "",
14696
+ free: "[FREE] ",
14590
14697
  pro: "[PRO] ",
14591
14698
  team: "[TEAM] ",
14592
14699
  enterprise: "[ENTERPRISE] "
@@ -14615,7 +14722,12 @@ var init_hook_registry = __esm({
14615
14722
  "post-edit-context",
14616
14723
  "post-tool-use",
14617
14724
  "pre-compact",
14725
+ // P-E-019 (1.12.0): security-gate + pre-delete-check are kept registered
14726
+ // for back-compat (operator settings.json may still reference them
14727
+ // individually), but new installs emit only `pre-tool-use-gate` —
14728
+ // the consolidated single-spawn PreToolUse hook.
14618
14729
  "pre-delete-check",
14730
+ "pre-tool-use-gate",
14619
14731
  "quality-event",
14620
14732
  "rule-enforcement-pipeline",
14621
14733
  "security-gate",
@@ -18027,10 +18139,11 @@ var init_adapters = __esm({
18027
18139
  "src/detect/adapters/index.ts"() {
18028
18140
  "use strict";
18029
18141
  CORE_BUNDLED_IDS = /* @__PURE__ */ new Set([
18030
- "aspnet",
18031
- "go-chi",
18142
+ // P-M-032 (plan-stage-d-medium-sweep): aspnet, go-chi, phoenix REMOVED
18143
+ // — zero downstream consumers (each had a single 1-line re-export shim).
18144
+ // Rails (5 consumers) + Spring (1 consumer) deferred to Stage E as the
18145
+ // JVM/Ruby ecosystem-watch pair per operator decision.
18032
18146
  "nextjs-trpc",
18033
- "phoenix",
18034
18147
  "python-django",
18035
18148
  "python-fastapi",
18036
18149
  "python-flask",
@@ -18977,6 +19090,184 @@ var init_db = __esm({
18977
19090
  }
18978
19091
  });
18979
19092
 
19093
+ // src/knowledge-db.ts
19094
+ import Database3 from "better-sqlite3";
19095
+ import { dirname as dirname14 } from "path";
19096
+ import { existsSync as existsSync28, mkdirSync as mkdirSync11 } from "fs";
19097
+ function getKnowledgeDb() {
19098
+ const dbPath = getResolvedPaths().knowledgeDbPath;
19099
+ const dir = dirname14(dbPath);
19100
+ if (!existsSync28(dir)) {
19101
+ mkdirSync11(dir, { recursive: true });
19102
+ }
19103
+ const db = new Database3(dbPath);
19104
+ db.pragma("journal_mode = WAL");
19105
+ db.pragma("foreign_keys = ON");
19106
+ initKnowledgeSchema(db);
19107
+ return db;
19108
+ }
19109
+ function initKnowledgeSchema(db) {
19110
+ db.exec(`
19111
+ -- Core document chunks (parsed from .claude/**/*.md)
19112
+ CREATE TABLE IF NOT EXISTS knowledge_documents (
19113
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
19114
+ file_path TEXT NOT NULL,
19115
+ category TEXT NOT NULL,
19116
+ title TEXT NOT NULL,
19117
+ description TEXT,
19118
+ content_hash TEXT NOT NULL,
19119
+ indexed_at TEXT NOT NULL,
19120
+ indexed_at_epoch INTEGER NOT NULL
19121
+ );
19122
+
19123
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_kd_filepath ON knowledge_documents(file_path);
19124
+ CREATE INDEX IF NOT EXISTS idx_kd_category ON knowledge_documents(category);
19125
+
19126
+ -- Structured chunks within documents
19127
+ CREATE TABLE IF NOT EXISTS knowledge_chunks (
19128
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
19129
+ document_id INTEGER NOT NULL REFERENCES knowledge_documents(id) ON DELETE CASCADE,
19130
+ chunk_type TEXT NOT NULL CHECK(chunk_type IN (
19131
+ 'section', 'table_row', 'code_block', 'rule', 'incident', 'pattern', 'command', 'mismatch'
19132
+ )),
19133
+ heading TEXT,
19134
+ content TEXT NOT NULL,
19135
+ line_start INTEGER,
19136
+ line_end INTEGER,
19137
+ metadata TEXT DEFAULT '{}'
19138
+ );
19139
+
19140
+ CREATE INDEX IF NOT EXISTS idx_kc_doc ON knowledge_chunks(document_id);
19141
+ CREATE INDEX IF NOT EXISTS idx_kc_type ON knowledge_chunks(chunk_type);
19142
+ CREATE INDEX IF NOT EXISTS idx_kc_heading ON knowledge_chunks(heading);
19143
+
19144
+ -- Canonical Rules index
19145
+ CREATE TABLE IF NOT EXISTS knowledge_rules (
19146
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
19147
+ rule_id TEXT UNIQUE NOT NULL,
19148
+ rule_text TEXT NOT NULL,
19149
+ vr_type TEXT,
19150
+ reference_path TEXT,
19151
+ severity TEXT DEFAULT 'HIGH',
19152
+ prevention_summary TEXT
19153
+ );
19154
+
19155
+ CREATE INDEX IF NOT EXISTS idx_kr_id ON knowledge_rules(rule_id);
19156
+ `);
19157
+ const rulesCols = db.prepare(`PRAGMA table_info(knowledge_rules)`).all();
19158
+ if (!rulesCols.some((c2) => c2.name === "source")) {
19159
+ db.exec(
19160
+ `ALTER TABLE knowledge_rules ADD COLUMN source TEXT NOT NULL DEFAULT 'framework'`
19161
+ );
19162
+ }
19163
+ db.exec(`
19164
+
19165
+ -- Verification Requirements index
19166
+ CREATE TABLE IF NOT EXISTS knowledge_verifications (
19167
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
19168
+ vr_type TEXT UNIQUE NOT NULL,
19169
+ command TEXT NOT NULL,
19170
+ expected TEXT NOT NULL,
19171
+ use_when TEXT NOT NULL,
19172
+ catches TEXT,
19173
+ category TEXT
19174
+ );
19175
+
19176
+ CREATE INDEX IF NOT EXISTS idx_kv_type ON knowledge_verifications(vr_type);
19177
+ CREATE INDEX IF NOT EXISTS idx_kv_category ON knowledge_verifications(category);
19178
+
19179
+ -- Incident index
19180
+ CREATE TABLE IF NOT EXISTS knowledge_incidents (
19181
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
19182
+ incident_num INTEGER UNIQUE NOT NULL,
19183
+ date TEXT NOT NULL,
19184
+ type TEXT NOT NULL,
19185
+ gap_found TEXT NOT NULL,
19186
+ prevention TEXT NOT NULL,
19187
+ cr_added TEXT,
19188
+ root_cause TEXT,
19189
+ user_quote TEXT
19190
+ );
19191
+
19192
+ CREATE INDEX IF NOT EXISTS idx_ki_num ON knowledge_incidents(incident_num);
19193
+ CREATE INDEX IF NOT EXISTS idx_ki_type ON knowledge_incidents(type);
19194
+ CREATE INDEX IF NOT EXISTS idx_ki_cr ON knowledge_incidents(cr_added);
19195
+
19196
+ -- Schema mismatch quick lookup
19197
+ -- P-H013 (plan-stage-c-high-batch): the source column no longer has a
19198
+ -- SQL DEFAULT. Previously the value was a JS template-literal
19199
+ -- interpolation baked into the customer SQLite at schema creation
19200
+ -- time, so later config changes were ignored. Default is now applied
19201
+ -- at INSERT time via getConfig().conventions.knowledgeSourceFiles[0]
19202
+ -- in knowledge-indexer.ts -- a runtime config lookup that reflects
19203
+ -- the customers current config.
19204
+ CREATE TABLE IF NOT EXISTS knowledge_schema_mismatches (
19205
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
19206
+ table_name TEXT NOT NULL,
19207
+ wrong_column TEXT NOT NULL,
19208
+ correct_column TEXT NOT NULL,
19209
+ source TEXT NOT NULL
19210
+ );
19211
+
19212
+ CREATE INDEX IF NOT EXISTS idx_ksm_table ON knowledge_schema_mismatches(table_name);
19213
+
19214
+ -- Cross-reference graph
19215
+ CREATE TABLE IF NOT EXISTS knowledge_edges (
19216
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
19217
+ source_type TEXT NOT NULL,
19218
+ source_id TEXT NOT NULL,
19219
+ target_type TEXT NOT NULL,
19220
+ target_id TEXT NOT NULL,
19221
+ edge_type TEXT NOT NULL
19222
+ );
19223
+
19224
+ CREATE INDEX IF NOT EXISTS idx_ke_source ON knowledge_edges(source_type, source_id);
19225
+ CREATE INDEX IF NOT EXISTS idx_ke_target ON knowledge_edges(target_type, target_id);
19226
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_ke_unique ON knowledge_edges(source_type, source_id, target_type, target_id, edge_type);
19227
+
19228
+ -- Staleness tracking
19229
+ CREATE TABLE IF NOT EXISTS knowledge_meta (
19230
+ key TEXT PRIMARY KEY,
19231
+ value TEXT NOT NULL
19232
+ );
19233
+ `);
19234
+ try {
19235
+ db.exec(`
19236
+ CREATE VIRTUAL TABLE IF NOT EXISTS knowledge_fts USING fts5(
19237
+ heading, content, chunk_type, file_path
19238
+ );
19239
+
19240
+ CREATE TRIGGER IF NOT EXISTS kc_fts_insert AFTER INSERT ON knowledge_chunks BEGIN
19241
+ INSERT INTO knowledge_fts(rowid, heading, content, chunk_type, file_path)
19242
+ SELECT new.id, new.heading, new.content, new.chunk_type, kd.file_path
19243
+ FROM knowledge_documents kd WHERE kd.id = new.document_id;
19244
+ END;
19245
+
19246
+ CREATE TRIGGER IF NOT EXISTS kc_fts_delete AFTER DELETE ON knowledge_chunks BEGIN
19247
+ INSERT INTO knowledge_fts(knowledge_fts, rowid, heading, content, chunk_type, file_path)
19248
+ SELECT 'delete', old.id, old.heading, old.content, old.chunk_type, kd.file_path
19249
+ FROM knowledge_documents kd WHERE kd.id = old.document_id;
19250
+ END;
19251
+
19252
+ CREATE TRIGGER IF NOT EXISTS kc_fts_update AFTER UPDATE ON knowledge_chunks BEGIN
19253
+ INSERT INTO knowledge_fts(knowledge_fts, rowid, heading, content, chunk_type, file_path)
19254
+ SELECT 'delete', old.id, old.heading, old.content, old.chunk_type, kd.file_path
19255
+ FROM knowledge_documents kd WHERE kd.id = old.document_id;
19256
+ INSERT INTO knowledge_fts(rowid, heading, content, chunk_type, file_path)
19257
+ SELECT new.id, new.heading, new.content, new.chunk_type, kd.file_path
19258
+ FROM knowledge_documents kd WHERE kd.id = new.document_id;
19259
+ END;
19260
+ `);
19261
+ } catch {
19262
+ }
19263
+ }
19264
+ var init_knowledge_db = __esm({
19265
+ "src/knowledge-db.ts"() {
19266
+ "use strict";
19267
+ init_config();
19268
+ }
19269
+ });
19270
+
18980
19271
  // src/security-utils.ts
18981
19272
  import { resolve as resolve24, normalize } from "path";
18982
19273
  function ensureWithinRoot(filePath, projectRoot) {
@@ -19054,8 +19345,8 @@ var init_rules = __esm({
19054
19345
  });
19055
19346
 
19056
19347
  // src/import-resolver.ts
19057
- import { readFileSync as readFileSync25, existsSync as existsSync28, statSync as statSync12 } from "fs";
19058
- import { resolve as resolve25, dirname as dirname14, join as join13 } from "path";
19348
+ import { readFileSync as readFileSync25, existsSync as existsSync29, statSync as statSync12 } from "fs";
19349
+ import { resolve as resolve25, dirname as dirname15, join as join13 } from "path";
19059
19350
  function parseImports(source) {
19060
19351
  const imports = [];
19061
19352
  const lines = source.split("\n");
@@ -19113,21 +19404,21 @@ function resolveImportPath(specifier, fromFile) {
19113
19404
  const paths = getResolvedPaths();
19114
19405
  basePath = resolve25(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
19115
19406
  } else {
19116
- basePath = resolve25(dirname14(fromFile), specifier);
19407
+ basePath = resolve25(dirname15(fromFile), specifier);
19117
19408
  }
19118
- if (existsSync28(basePath) && !isDirectory(basePath)) {
19409
+ if (existsSync29(basePath) && !isDirectory(basePath)) {
19119
19410
  return toRelative(basePath);
19120
19411
  }
19121
19412
  const resolvedPaths = getResolvedPaths();
19122
19413
  for (const ext of resolvedPaths.extensions) {
19123
19414
  const withExt = basePath + ext;
19124
- if (existsSync28(withExt)) {
19415
+ if (existsSync29(withExt)) {
19125
19416
  return toRelative(withExt);
19126
19417
  }
19127
19418
  }
19128
19419
  for (const indexFile of resolvedPaths.indexFiles) {
19129
19420
  const indexPath = join13(basePath, indexFile);
19130
- if (existsSync28(indexPath)) {
19421
+ if (existsSync29(indexPath)) {
19131
19422
  return toRelative(indexPath);
19132
19423
  }
19133
19424
  }
@@ -19164,7 +19455,7 @@ function buildImportIndex(dataDb, codegraphDb) {
19164
19455
  let batch = [];
19165
19456
  for (const file of files) {
19166
19457
  const absPath = ensureWithinRoot(resolve25(projectRoot, file.path), projectRoot);
19167
- if (!existsSync28(absPath)) continue;
19458
+ if (!existsSync29(absPath)) continue;
19168
19459
  let source;
19169
19460
  try {
19170
19461
  source = readFileSync25(absPath, "utf-8");
@@ -19204,12 +19495,12 @@ var init_import_resolver = __esm({
19204
19495
  });
19205
19496
 
19206
19497
  // src/trpc-index.ts
19207
- import { readFileSync as readFileSync26, existsSync as existsSync29, readdirSync as readdirSync16 } from "fs";
19498
+ import { readFileSync as readFileSync26, existsSync as existsSync30, readdirSync as readdirSync16 } from "fs";
19208
19499
  import { resolve as resolve26, join as join14 } from "path";
19209
19500
  function parseRootRouter() {
19210
19501
  const paths = getResolvedPaths();
19211
19502
  const rootPath = paths.rootRouterPath;
19212
- if (!existsSync29(rootPath)) {
19503
+ if (!existsSync30(rootPath)) {
19213
19504
  throw new Error(`Root router not found at ${rootPath}`);
19214
19505
  }
19215
19506
  const source = readFileSync26(rootPath, "utf-8");
@@ -19224,12 +19515,12 @@ function parseRootRouter() {
19224
19515
  for (const ext of [".ts", ".tsx", ""]) {
19225
19516
  const candidate = fullPath + ext;
19226
19517
  const routersRelPath = getConfig().paths.routers ?? "src/server/api/routers";
19227
- if (existsSync29(candidate)) {
19518
+ if (existsSync30(candidate)) {
19228
19519
  filePath = routersRelPath + "/" + filePath + ext;
19229
19520
  break;
19230
19521
  }
19231
19522
  const indexCandidate = join14(fullPath, "index.ts");
19232
- if (existsSync29(indexCandidate)) {
19523
+ if (existsSync30(indexCandidate)) {
19233
19524
  filePath = routersRelPath + "/" + filePath + "/index.ts";
19234
19525
  break;
19235
19526
  }
@@ -19249,7 +19540,7 @@ function parseRootRouter() {
19249
19540
  }
19250
19541
  function extractProcedures(routerFilePath) {
19251
19542
  const absPath = resolve26(getProjectRoot(), routerFilePath);
19252
- if (!existsSync29(absPath)) return [];
19543
+ if (!existsSync30(absPath)) return [];
19253
19544
  const source = readFileSync26(absPath, "utf-8");
19254
19545
  const procedures = [];
19255
19546
  const seen = /* @__PURE__ */ new Set();
@@ -19279,7 +19570,7 @@ function findUICallSites(routerKey, procedureName) {
19279
19570
  ];
19280
19571
  const searchPattern = `api.${routerKey}.${procedureName}`;
19281
19572
  for (const dir of searchDirs) {
19282
- if (!existsSync29(dir)) continue;
19573
+ if (!existsSync30(dir)) continue;
19283
19574
  searchDirectory(dir, searchPattern, callSites);
19284
19575
  }
19285
19576
  return callSites;
@@ -19357,7 +19648,7 @@ var init_trpc_index = __esm({
19357
19648
  });
19358
19649
 
19359
19650
  // src/page-deps.ts
19360
- import { readFileSync as readFileSync27, existsSync as existsSync30 } from "fs";
19651
+ import { readFileSync as readFileSync27, existsSync as existsSync31 } from "fs";
19361
19652
  import { resolve as resolve27 } from "path";
19362
19653
  function deriveRoute(pageFile) {
19363
19654
  let route = pageFile.replace(/^src\/app/, "").replace(/\/page\.tsx?$/, "").replace(/\/page\.jsx?$/, "");
@@ -19377,7 +19668,7 @@ function traceImports(startFile, dataDb, visited, components, hooks, maxDepth =
19377
19668
  if (maxDepth <= 0 || visited.has(startFile)) return;
19378
19669
  visited.add(startFile);
19379
19670
  const imports = dataDb.prepare(
19380
- `SELECT target_file, imported_names FROM ${t("imports")} WHERE source_file = ?`
19671
+ `SELECT target_file, imported_names FROM ${t("imports")} WHERE source_file = ? LIMIT 10000`
19381
19672
  ).all(startFile);
19382
19673
  for (const imp of imports) {
19383
19674
  const target = imp.target_file;
@@ -19397,7 +19688,7 @@ function findRouterCalls(files) {
19397
19688
  const projectRoot = getProjectRoot();
19398
19689
  for (const file of files) {
19399
19690
  const absPath = ensureWithinRoot(resolve27(projectRoot, file), projectRoot);
19400
- if (!existsSync30(absPath)) continue;
19691
+ if (!existsSync31(absPath)) continue;
19401
19692
  try {
19402
19693
  const source = readFileSync27(absPath, "utf-8");
19403
19694
  const apiCallRegex = /api\.(\w+)\.\w+/g;
@@ -19414,11 +19705,11 @@ function findTablesFromRouters(routerNames, dataDb) {
19414
19705
  const tables = /* @__PURE__ */ new Set();
19415
19706
  for (const routerName of routerNames) {
19416
19707
  const procs = dataDb.prepare(
19417
- `SELECT DISTINCT router_file FROM ${t("trpc_procedures")} WHERE router_name = ?`
19708
+ `SELECT DISTINCT router_file FROM ${t("trpc_procedures")} WHERE router_name = ? LIMIT 10000`
19418
19709
  ).all(routerName);
19419
19710
  for (const proc of procs) {
19420
19711
  const absPath = ensureWithinRoot(resolve27(getProjectRoot(), proc.router_file), getProjectRoot());
19421
- if (!existsSync30(absPath)) continue;
19712
+ if (!existsSync31(absPath)) continue;
19422
19713
  try {
19423
19714
  const source = readFileSync27(absPath, "utf-8");
19424
19715
  const dbPattern = getConfig().dbAccessPattern ?? "ctx.db.{table}";
@@ -19437,7 +19728,7 @@ function findTablesFromRouters(routerNames, dataDb) {
19437
19728
  function buildPageDeps(dataDb, codegraphDb) {
19438
19729
  dataDb.exec(`DELETE FROM ${t("page_deps")}`);
19439
19730
  const pages = codegraphDb.prepare(
19440
- "SELECT path FROM files WHERE path LIKE 'src/app/%/page.tsx' OR path = 'src/app/page.tsx'"
19731
+ "SELECT path FROM files WHERE path LIKE 'src/app/%/page.tsx' OR path = 'src/app/page.tsx' LIMIT 10000"
19441
19732
  ).all();
19442
19733
  const insertStmt = dataDb.prepare(
19443
19734
  `INSERT INTO ${t("page_deps")} (page_file, route, portal, components, hooks, routers, tables_touched) VALUES (?, ?, ?, ?, ?, ?, ?)`
@@ -19486,7 +19777,7 @@ function findAffectedPages(dataDb, file) {
19486
19777
  const directPage = getPageChain(dataDb, file);
19487
19778
  if (directPage) return [directPage];
19488
19779
  const importers = dataDb.prepare(
19489
- `SELECT source_file FROM ${t("imports")} WHERE target_file = ?`
19780
+ `SELECT source_file FROM ${t("imports")} WHERE target_file = ? LIMIT 5000`
19490
19781
  ).all(file);
19491
19782
  const affectedFiles = /* @__PURE__ */ new Set([file, ...importers.map((i) => i.source_file)]);
19492
19783
  let frontier = [...importers.map((i) => i.source_file)];
@@ -19497,7 +19788,7 @@ function findAffectedPages(dataDb, file) {
19497
19788
  const next = [];
19498
19789
  for (const f2 of frontier) {
19499
19790
  const upstreamImporters = dataDb.prepare(
19500
- `SELECT source_file FROM ${t("imports")} WHERE target_file = ?`
19791
+ `SELECT source_file FROM ${t("imports")} WHERE target_file = ? LIMIT 5000`
19501
19792
  ).all(f2);
19502
19793
  for (const imp of upstreamImporters) {
19503
19794
  if (!visited.has(imp.source_file)) {
@@ -19548,7 +19839,7 @@ function buildMiddlewareTree(dataDb) {
19548
19839
  while (queue.length > 0) {
19549
19840
  const current = queue.shift();
19550
19841
  const imports = dataDb.prepare(
19551
- `SELECT target_file FROM ${t("imports")} WHERE source_file = ?`
19842
+ `SELECT target_file FROM ${t("imports")} WHERE source_file = ? LIMIT 10000`
19552
19843
  ).all(current);
19553
19844
  for (const imp of imports) {
19554
19845
  if (!visited.has(imp.target_file) && imp.target_file.startsWith("src/")) {
@@ -19636,7 +19927,7 @@ function findCrossDomainImports(dataDb) {
19636
19927
  const srcPrefix = config.paths.source;
19637
19928
  const srcPattern = srcPrefix + "/%";
19638
19929
  const imports = dataDb.prepare(
19639
- `SELECT source_file, target_file FROM ${t("imports")} WHERE source_file LIKE ? AND target_file LIKE ?`
19930
+ `SELECT source_file, target_file FROM ${t("imports")} WHERE source_file LIKE ? AND target_file LIKE ? LIMIT 10000`
19640
19931
  ).all(srcPattern, srcPattern);
19641
19932
  const crossings = [];
19642
19933
  for (const imp of imports) {
@@ -19692,11 +19983,11 @@ var init_domains = __esm({
19692
19983
  });
19693
19984
 
19694
19985
  // src/schema-mapper.ts
19695
- import { readFileSync as readFileSync28, existsSync as existsSync31, readdirSync as readdirSync17 } from "fs";
19986
+ import { readFileSync as readFileSync28, existsSync as existsSync32, readdirSync as readdirSync17 } from "fs";
19696
19987
  import { join as join15 } from "path";
19697
19988
  function parsePrismaSchema() {
19698
19989
  const schemaPath = getResolvedPaths().prismaSchemaPath;
19699
- if (!existsSync31(schemaPath)) {
19990
+ if (!existsSync32(schemaPath)) {
19700
19991
  throw new Error(`Prisma schema not found at ${schemaPath}`);
19701
19992
  }
19702
19993
  const source = readFileSync28(schemaPath, "utf-8");
@@ -19757,7 +20048,7 @@ function toSnakeCase(str) {
19757
20048
  function findColumnUsageInRouters(tableName) {
19758
20049
  const usage = /* @__PURE__ */ new Map();
19759
20050
  const routersDir = getResolvedPaths().routersDir;
19760
- if (!existsSync31(routersDir)) return usage;
20051
+ if (!existsSync32(routersDir)) return usage;
19761
20052
  scanDirectory(routersDir, tableName, usage);
19762
20053
  return usage;
19763
20054
  }
@@ -19819,7 +20110,7 @@ function detectMismatches(models) {
19819
20110
  }
19820
20111
  function findFilesUsingColumn(dir, column, tableName) {
19821
20112
  const result = [];
19822
- if (!existsSync31(dir)) return result;
20113
+ if (!existsSync32(dir)) return result;
19823
20114
  const entries = readdirSync17(dir, { withFileTypes: true });
19824
20115
  for (const entry of entries) {
19825
20116
  const fullPath = join15(dir, entry.name);
@@ -19972,14 +20263,14 @@ var init_import_parser = __esm({
19972
20263
  });
19973
20264
 
19974
20265
  // src/python/import-resolver.ts
19975
- import { readFileSync as readFileSync29, existsSync as existsSync32, readdirSync as readdirSync18 } from "fs";
19976
- import { resolve as resolve29, join as join16, relative as relative6, dirname as dirname15 } from "path";
20266
+ import { readFileSync as readFileSync29, existsSync as existsSync33, readdirSync as readdirSync18 } from "fs";
20267
+ import { resolve as resolve29, join as join16, relative as relative6, dirname as dirname16 } from "path";
19977
20268
  function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
19978
20269
  const projectRoot = getProjectRoot();
19979
20270
  if (level > 0) {
19980
- let baseDir = dirname15(resolve29(projectRoot, fromFile));
20271
+ let baseDir = dirname16(resolve29(projectRoot, fromFile));
19981
20272
  for (let i = 1; i < level; i++) {
19982
- baseDir = dirname15(baseDir);
20273
+ baseDir = dirname16(baseDir);
19983
20274
  }
19984
20275
  const modulePart = module.replace(/^\.+/, "");
19985
20276
  if (modulePart) {
@@ -19993,13 +20284,13 @@ function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
19993
20284
  return tryResolvePythonPath(candidate, projectRoot);
19994
20285
  }
19995
20286
  function tryResolvePythonPath(basePath, projectRoot) {
19996
- if (existsSync32(basePath + ".py")) {
20287
+ if (existsSync33(basePath + ".py")) {
19997
20288
  return relative6(projectRoot, basePath + ".py");
19998
20289
  }
19999
- if (existsSync32(join16(basePath, "__init__.py"))) {
20290
+ if (existsSync33(join16(basePath, "__init__.py"))) {
20000
20291
  return relative6(projectRoot, join16(basePath, "__init__.py"));
20001
20292
  }
20002
- if (basePath.endsWith(".py") && existsSync32(basePath)) {
20293
+ if (basePath.endsWith(".py") && existsSync33(basePath)) {
20003
20294
  return relative6(projectRoot, basePath);
20004
20295
  }
20005
20296
  return null;
@@ -21201,7 +21492,7 @@ function handleDetail(args2, db) {
21201
21492
  if (!ids || ids.length === 0) return text("Error: ids array is required");
21202
21493
  const placeholders = ids.map(() => "?").join(",");
21203
21494
  const observations = db.prepare(
21204
- `SELECT * FROM observations WHERE id IN (${placeholders}) ORDER BY created_at_epoch ASC`
21495
+ `SELECT * FROM observations WHERE id IN (${placeholders}) ORDER BY created_at_epoch ASC LIMIT 10000`
21205
21496
  ).all(...ids);
21206
21497
  if (observations.length === 0) {
21207
21498
  return text("No observations found for the given IDs.");
@@ -21352,7 +21643,7 @@ var init_memory_tools = __esm({
21352
21643
  });
21353
21644
 
21354
21645
  // src/docs-tools.ts
21355
- import { readFileSync as readFileSync34, existsSync as existsSync33 } from "fs";
21646
+ import { readFileSync as readFileSync34, existsSync as existsSync34 } from "fs";
21356
21647
  import { resolve as resolve30, basename as basename6 } from "path";
21357
21648
  function p3(baseName) {
21358
21649
  return `${getConfig().toolPrefix}_${baseName}`;
@@ -21408,7 +21699,7 @@ function handleDocsToolCall(name, args2) {
21408
21699
  }
21409
21700
  function loadDocsMap() {
21410
21701
  const mapPath = getResolvedPaths().docsMapPath;
21411
- if (!existsSync33(mapPath)) {
21702
+ if (!existsSync34(mapPath)) {
21412
21703
  throw new Error(`docs-map.json not found at ${mapPath}`);
21413
21704
  }
21414
21705
  return JSON.parse(readFileSync34(mapPath, "utf-8"));
@@ -21482,9 +21773,9 @@ function extractFrontmatter(content) {
21482
21773
  function extractProcedureNames(routerPath) {
21483
21774
  const root = getProjectRoot();
21484
21775
  const absPath = ensureWithinRoot(resolve30(getResolvedPaths().srcDir, "..", routerPath), root);
21485
- if (!existsSync33(absPath)) {
21776
+ if (!existsSync34(absPath)) {
21486
21777
  const altPath = ensureWithinRoot(resolve30(getResolvedPaths().srcDir, "../server/api/routers", basename6(routerPath)), root);
21487
- if (!existsSync33(altPath)) return [];
21778
+ if (!existsSync34(altPath)) return [];
21488
21779
  return extractProcedureNamesFromContent(readFileSync34(altPath, "utf-8"));
21489
21780
  }
21490
21781
  return extractProcedureNamesFromContent(readFileSync34(absPath, "utf-8"));
@@ -21528,7 +21819,7 @@ function handleDocsAudit(args2) {
21528
21819
  const mapping = docsMap.mappings.find((m3) => m3.id === mappingId);
21529
21820
  if (!mapping) continue;
21530
21821
  const helpPagePath = ensureWithinRoot(resolve30(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
21531
- if (!existsSync33(helpPagePath)) {
21822
+ if (!existsSync34(helpPagePath)) {
21532
21823
  results.push({
21533
21824
  helpPage: mapping.helpPage,
21534
21825
  mappingId,
@@ -21581,7 +21872,7 @@ function handleDocsAudit(args2) {
21581
21872
  for (const [guideName, parentId] of Object.entries(docsMap.userGuideInheritance.examples)) {
21582
21873
  if (parentId === mappingId) {
21583
21874
  const guidePath = ensureWithinRoot(resolve30(getResolvedPaths().helpSitePath, `pages/user-guides/${guideName}/index.mdx`), getProjectRoot());
21584
- if (existsSync33(guidePath)) {
21875
+ if (existsSync34(guidePath)) {
21585
21876
  const guideContent = readFileSync34(guidePath, "utf-8");
21586
21877
  const guideFrontmatter = extractFrontmatter(guideContent);
21587
21878
  if (!guideFrontmatter?.lastVerified || status === "STALE") {
@@ -21616,7 +21907,7 @@ function handleDocsCoverage(args2) {
21616
21907
  const mappings = filterDomain ? docsMap.mappings.filter((m3) => m3.id === filterDomain) : docsMap.mappings;
21617
21908
  for (const mapping of mappings) {
21618
21909
  const helpPagePath = ensureWithinRoot(resolve30(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
21619
- const exists = existsSync33(helpPagePath);
21910
+ const exists = existsSync34(helpPagePath);
21620
21911
  let hasContent = false;
21621
21912
  let lineCount = 0;
21622
21913
  let lastVerified = null;
@@ -21963,7 +22254,7 @@ var init_observability_tools = __esm({
21963
22254
  });
21964
22255
 
21965
22256
  // src/sentinel-db.ts
21966
- import { existsSync as existsSync34 } from "fs";
22257
+ import { existsSync as existsSync35 } from "fs";
21967
22258
  import { resolve as resolve31 } from "path";
21968
22259
  function parsePortalScope(raw) {
21969
22260
  if (!raw) return [];
@@ -22109,7 +22400,7 @@ function getFeatureImpact(db, filePaths) {
22109
22400
  const affectedFeatureIds = /* @__PURE__ */ new Set();
22110
22401
  for (const filePath of filePaths) {
22111
22402
  const links = db.prepare(
22112
- `SELECT feature_id FROM ${t("sentinel_components")} WHERE component_file = ?`
22403
+ `SELECT feature_id FROM ${t("sentinel_components")} WHERE component_file = ? LIMIT 10000`
22113
22404
  ).all(filePath);
22114
22405
  for (const link of links) {
22115
22406
  affectedFeatureIds.add(link.feature_id);
@@ -22122,7 +22413,7 @@ function getFeatureImpact(db, filePaths) {
22122
22413
  const feature = getFeatureById(db, featureId);
22123
22414
  if (!feature || feature.status !== "active") continue;
22124
22415
  const allComponents = db.prepare(
22125
- `SELECT component_file, is_primary FROM ${t("sentinel_components")} WHERE feature_id = ?`
22416
+ `SELECT component_file, is_primary FROM ${t("sentinel_components")} WHERE feature_id = ? LIMIT 10000`
22126
22417
  ).all(featureId);
22127
22418
  const affected = allComponents.filter((c2) => fileSet.has(c2.component_file));
22128
22419
  const remaining = allComponents.filter((c2) => !fileSet.has(c2.component_file));
@@ -22201,22 +22492,22 @@ function validateFeatures(db, domainFilter) {
22201
22492
  const missingPages = [];
22202
22493
  for (const comp of components) {
22203
22494
  const absPath = resolve31(PROJECT_ROOT, comp.component_file);
22204
- if (!existsSync34(absPath)) {
22495
+ if (!existsSync35(absPath)) {
22205
22496
  missingComponents.push(comp.component_file);
22206
22497
  }
22207
22498
  }
22208
22499
  for (const proc of procedures) {
22209
22500
  const routerPath = resolve31(PROJECT_ROOT, `src/server/api/routers/${proc.router_name}.ts`);
22210
- if (!existsSync34(routerPath)) {
22501
+ if (!existsSync35(routerPath)) {
22211
22502
  missingProcedures.push({ router: proc.router_name, procedure: proc.procedure_name });
22212
22503
  }
22213
22504
  }
22214
22505
  for (const page of pages) {
22215
22506
  const routeToPath = page.page_route.replace(/^\/(portal-[^/]+\/)?/, "src/app/").replace(/\/$/, "") + "/page.tsx";
22216
22507
  const absPath = resolve31(PROJECT_ROOT, routeToPath);
22217
- if (page.page_route.startsWith("/") && !existsSync34(absPath)) {
22508
+ if (page.page_route.startsWith("/") && !existsSync35(absPath)) {
22218
22509
  const altPath = resolve31(PROJECT_ROOT, `src/app${page.page_route}/page.tsx`);
22219
- if (!existsSync34(altPath)) {
22510
+ if (!existsSync35(altPath)) {
22220
22511
  missingPages.push(page.page_route);
22221
22512
  }
22222
22513
  }
@@ -22742,8 +23033,8 @@ var init_sentinel_tools = __esm({
22742
23033
  });
22743
23034
 
22744
23035
  // src/sentinel-scanner.ts
22745
- import { readFileSync as readFileSync35, existsSync as existsSync35, readdirSync as readdirSync23, statSync as statSync13 } from "fs";
22746
- import { resolve as resolve32, join as join21, basename as basename7, dirname as dirname16, relative as relative11 } from "path";
23036
+ import { readFileSync as readFileSync35, existsSync as existsSync36, readdirSync as readdirSync23, statSync as statSync13 } from "fs";
23037
+ import { resolve as resolve32, join as join21, basename as basename7, dirname as dirname17, relative as relative11 } from "path";
22747
23038
  function inferDomain(filePath) {
22748
23039
  const domains = getConfig().domains;
22749
23040
  const path = filePath.toLowerCase();
@@ -22796,6 +23087,7 @@ function scanTrpcProcedures(dataDb) {
22796
23087
  SELECT router_name, procedure_name, procedure_type, router_file
22797
23088
  FROM ${t("trpc_procedures")}
22798
23089
  ORDER BY router_name, procedure_name
23090
+ LIMIT 100000
22799
23091
  `).all();
22800
23092
  for (const proc of procedures) {
22801
23093
  const subdomain = inferSubdomain(proc.router_name, proc.procedure_name);
@@ -22838,6 +23130,7 @@ function scanPageRoutes(dataDb) {
22838
23130
  SELECT page_file, route, portal, components, hooks, routers
22839
23131
  FROM ${t("page_deps")}
22840
23132
  ORDER BY route
23133
+ LIMIT 100000
22841
23134
  `).all();
22842
23135
  for (const page of pages) {
22843
23136
  const domain = inferDomain(page.route);
@@ -22873,7 +23166,7 @@ function scanComponentExports(dataDb) {
22873
23166
  const componentsBase = config.paths.components ?? config.paths.source + "/components";
22874
23167
  const componentDirs = [];
22875
23168
  const basePath = resolve32(projectRoot, componentsBase);
22876
- if (existsSync35(basePath)) {
23169
+ if (existsSync36(basePath)) {
22877
23170
  try {
22878
23171
  const entries = readdirSync23(basePath, { withFileTypes: true });
22879
23172
  for (const entry of entries) {
@@ -22886,7 +23179,7 @@ function scanComponentExports(dataDb) {
22886
23179
  }
22887
23180
  for (const dir of componentDirs) {
22888
23181
  const absDir = resolve32(projectRoot, dir);
22889
- if (!existsSync35(absDir)) continue;
23182
+ if (!existsSync36(absDir)) continue;
22890
23183
  const files = walkDir2(absDir).filter((f2) => f2.endsWith(".tsx") || f2.endsWith(".ts"));
22891
23184
  for (const file of files) {
22892
23185
  const relPath = relative11(projectRoot, file);
@@ -22916,7 +23209,7 @@ function scanComponentExports(dataDb) {
22916
23209
  if (hasHandlers && exportMatch) {
22917
23210
  const componentName = exportMatch[1];
22918
23211
  const domain = inferDomain(relPath);
22919
- const subdomain = basename7(dirname16(relPath));
23212
+ const subdomain = basename7(dirname17(relPath));
22920
23213
  const featureKey = `component.${subdomain}.${componentName.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "")}`;
22921
23214
  if (!annotations.some((a2) => a2.featureKey === featureKey)) {
22922
23215
  features.push({
@@ -23025,7 +23318,7 @@ function calculateQualityScore(db, sessionId) {
23025
23318
  const weights = getWeights();
23026
23319
  const categories = getCategories();
23027
23320
  const observations = db.prepare(
23028
- "SELECT type, detail FROM observations WHERE session_id = ?"
23321
+ "SELECT type, detail FROM observations WHERE session_id = ? LIMIT 10000"
23029
23322
  ).all(sessionId);
23030
23323
  let score = 50;
23031
23324
  const breakdown = Object.fromEntries(
@@ -23067,6 +23360,7 @@ function backfillQualityScores(db) {
23067
23360
  FROM sessions s
23068
23361
  LEFT JOIN session_quality_scores q ON s.session_id = q.session_id
23069
23362
  WHERE q.session_id IS NULL
23363
+ LIMIT 100000
23070
23364
  `).all();
23071
23365
  let backfilled = 0;
23072
23366
  for (const session of sessions) {
@@ -23165,6 +23459,7 @@ function handleQualityTrend(args2, db, retried = false) {
23165
23459
  FROM session_quality_scores
23166
23460
  WHERE created_at >= datetime('now', ?)
23167
23461
  ORDER BY created_at ASC
23462
+ LIMIT 100000
23168
23463
  `).all(`-${days} days`);
23169
23464
  if (scores.length === 0) {
23170
23465
  if (!retried) {
@@ -23200,6 +23495,7 @@ function handleQualityReport(args2, db, retried = false) {
23200
23495
  SELECT score, security_score, architecture_score, coupling_score, test_score, rule_compliance_score
23201
23496
  FROM session_quality_scores
23202
23497
  WHERE created_at >= datetime('now', ?)
23498
+ LIMIT 10000
23203
23499
  `).all(`-${days} days`);
23204
23500
  if (scores.length === 0) {
23205
23501
  if (!retried) {
@@ -23686,7 +23982,7 @@ function queryAuditLog(db, options) {
23686
23982
  }
23687
23983
  function getFileChain(db, filePath) {
23688
23984
  return db.prepare(
23689
- "SELECT * FROM audit_log WHERE file_path = ? ORDER BY timestamp ASC"
23985
+ "SELECT * FROM audit_log WHERE file_path = ? ORDER BY timestamp ASC LIMIT 10000"
23690
23986
  ).all(filePath);
23691
23987
  }
23692
23988
  function getAuditToolDefinitions() {
@@ -23930,7 +24226,7 @@ var init_audit_trail = __esm({
23930
24226
  });
23931
24227
 
23932
24228
  // src/validation-engine.ts
23933
- import { existsSync as existsSync36, readFileSync as readFileSync36 } from "fs";
24229
+ import { existsSync as existsSync37, readFileSync as readFileSync36 } from "fs";
23934
24230
  function p10(baseName) {
23935
24231
  return `${getConfig().toolPrefix}_${baseName}`;
23936
24232
  }
@@ -23961,7 +24257,7 @@ function validateFile(filePath, projectRoot) {
23961
24257
  });
23962
24258
  return checks;
23963
24259
  }
23964
- if (!existsSync36(absPath)) {
24260
+ if (!existsSync37(absPath)) {
23965
24261
  checks.push({
23966
24262
  name: "file_exists",
23967
24263
  severity: "error",
@@ -24166,6 +24462,7 @@ function handleValidationReport(args2, db) {
24166
24462
  FROM validation_results
24167
24463
  WHERE created_at >= datetime('now', ?)
24168
24464
  ORDER BY created_at DESC
24465
+ LIMIT 100000
24169
24466
  `).all(`-${days} days`);
24170
24467
  if (results.length === 0) {
24171
24468
  return text9(`No validation results found in the last ${days} days. Run ${p10("validation_check")} { file: "src/path/to/file.ts" } on specific files to generate validation data, or try a longer time range with { days: 90 }.`);
@@ -24415,7 +24712,7 @@ var init_adr_generator = __esm({
24415
24712
  });
24416
24713
 
24417
24714
  // src/security-scorer.ts
24418
- import { existsSync as existsSync37, readFileSync as readFileSync37 } from "fs";
24715
+ import { existsSync as existsSync38, readFileSync as readFileSync37 } from "fs";
24419
24716
  function p12(baseName) {
24420
24717
  return `${getConfig().toolPrefix}_${baseName}`;
24421
24718
  }
@@ -24439,7 +24736,7 @@ function scoreFileSecurity(filePath, projectRoot) {
24439
24736
  }]
24440
24737
  };
24441
24738
  }
24442
- if (!existsSync37(absPath)) {
24739
+ if (!existsSync38(absPath)) {
24443
24740
  return { riskScore: 0, findings: [] };
24444
24741
  }
24445
24742
  let source;
@@ -24576,6 +24873,7 @@ function handleSecurityScore(args2, db) {
24576
24873
  SELECT file_path, risk_score, findings FROM security_scores
24577
24874
  WHERE session_id = ?
24578
24875
  ORDER BY risk_score DESC
24876
+ LIMIT 10000
24579
24877
  `).all(sessionId);
24580
24878
  if (scores.length === 0) {
24581
24879
  return text11(`No security scores for session ${sessionId.slice(0, 8)}... Security scores are generated when files are scanned. Try: ${p12("security_score")} { file_path: "src/server/api/routers/example.ts" } to scan a file, or ${p12("security_heatmap")} {} to see all scanned files.`);
@@ -24733,7 +25031,7 @@ var init_security_scorer = __esm({
24733
25031
  });
24734
25032
 
24735
25033
  // src/dependency-scorer.ts
24736
- import { existsSync as existsSync38, readFileSync as readFileSync38 } from "fs";
25034
+ import { existsSync as existsSync39, readFileSync as readFileSync38 } from "fs";
24737
25035
  import { resolve as resolve33 } from "path";
24738
25036
  function p13(baseName) {
24739
25037
  return `${getConfig().toolPrefix}_${baseName}`;
@@ -24767,7 +25065,7 @@ function calculateDepRisk(factors) {
24767
25065
  }
24768
25066
  function getInstalledPackages(projectRoot) {
24769
25067
  const pkgPath = resolve33(projectRoot, "package.json");
24770
- if (!existsSync38(pkgPath)) return /* @__PURE__ */ new Map();
25068
+ if (!existsSync39(pkgPath)) return /* @__PURE__ */ new Map();
24771
25069
  try {
24772
25070
  const pkg = JSON.parse(readFileSync38(pkgPath, "utf-8"));
24773
25071
  const packages = /* @__PURE__ */ new Map();
@@ -24993,6 +25291,7 @@ function detectConflicts(db, daysBack = 7) {
24993
25291
  AND so1.created_at >= datetime('now', ?)
24994
25292
  AND so2.created_at >= datetime('now', ?)
24995
25293
  GROUP BY so1.file_path, so1.developer_id, so2.developer_id
25294
+ LIMIT 10000
24996
25295
  `).all(`-${daysBack} days`, `-${daysBack} days`);
24997
25296
  return conflicts.map((c2) => ({
24998
25297
  filePath: c2.file_path,
@@ -25142,6 +25441,7 @@ function handleTeamExpertise(args2, db) {
25142
25441
  FROM developer_expertise
25143
25442
  WHERE module = ?
25144
25443
  ORDER BY expertise_score DESC
25444
+ LIMIT 10000
25145
25445
  `).all(targetModule);
25146
25446
  if (experts.length === 0) {
25147
25447
  return text13(`No expertise data for module "${targetModule}". Expertise builds as developers work on files in this module across sessions. Try: ${p14("team_expertise")} {} to see all modules with tracked expertise.`);
@@ -25319,6 +25619,7 @@ function handleRegressionCheck(_args, db) {
25319
25619
  FROM feature_health
25320
25620
  WHERE modifications_since_test > 0
25321
25621
  ORDER BY modifications_since_test DESC
25622
+ LIMIT 10000
25322
25623
  `).all();
25323
25624
  if (recentlyModified.length === 0) {
25324
25625
  return text14(`No features have been modified since their last test run. Low regression risk. Use ${p15("feature_health")} {} to see the full feature health dashboard.`);
@@ -25372,7 +25673,7 @@ var init_regression_detector = __esm({
25372
25673
 
25373
25674
  // src/knowledge-indexer.ts
25374
25675
  import { createHash as createHash9 } from "crypto";
25375
- import { readFileSync as readFileSync39, readdirSync as readdirSync24, statSync as statSync14, existsSync as existsSync39 } from "fs";
25676
+ import { readFileSync as readFileSync39, readdirSync as readdirSync24, statSync as statSync14, existsSync as existsSync40 } from "fs";
25376
25677
  import { resolve as resolve34, relative as relative12, basename as basename8, extname as extname2 } from "path";
25377
25678
  function getKnowledgePaths() {
25378
25679
  const resolved = getResolvedPaths();
@@ -25680,11 +25981,11 @@ function indexAllKnowledge(db) {
25680
25981
  files.push(...memFiles);
25681
25982
  } catch {
25682
25983
  }
25683
- if (existsSync39(paths.plansDir)) {
25984
+ if (existsSync40(paths.plansDir)) {
25684
25985
  const planFiles = discoverMarkdownFiles(paths.plansDir);
25685
25986
  files.push(...planFiles);
25686
25987
  }
25687
- if (existsSync39(paths.docsDir)) {
25988
+ if (existsSync40(paths.docsDir)) {
25688
25989
  const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
25689
25990
  const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f2) => !f2.includes("/plans/") && !excludePatterns.some((p19) => f2.includes(p19)));
25690
25991
  files.push(...docsFiles);
@@ -25727,7 +26028,7 @@ function indexAllKnowledge(db) {
25727
26028
  } catch {
25728
26029
  }
25729
26030
  for (const filePath of files) {
25730
- if (!existsSync39(filePath)) continue;
26031
+ if (!existsSync40(filePath)) continue;
25731
26032
  const content = readFileSync39(filePath, "utf-8");
25732
26033
  const hash = hashContent2(content);
25733
26034
  const relPath = filePath.startsWith(paths.claudeDir) ? relative12(paths.claudeDir, filePath) : filePath.startsWith(paths.plansDir) ? "plans/" + relative12(paths.plansDir, filePath) : filePath.startsWith(paths.docsDir) ? "docs/" + relative12(paths.docsDir, filePath) : filePath.startsWith(paths.memoryDir) ? `memory/${relative12(paths.memoryDir, filePath)}` : basename8(filePath);
@@ -25826,6 +26127,21 @@ Rule: ${c2.rule}`,
25826
26127
  }
25827
26128
  }
25828
26129
  }
26130
+ const customerRules = getConfig().governance_rules ?? [];
26131
+ const insertCustomerRule = db.prepare(
26132
+ `INSERT OR REPLACE INTO knowledge_rules (rule_id, rule_text, vr_type, reference_path, severity, source)
26133
+ VALUES (?, ?, ?, ?, ?, 'customer-config')`
26134
+ );
26135
+ for (const rule of customerRules) {
26136
+ const namespacedId = rule.id.startsWith("customer:") ? rule.id : `customer:${rule.id}`;
26137
+ insertCustomerRule.run(
26138
+ namespacedId,
26139
+ `${rule.title}: ${rule.description}`,
26140
+ rule.vr_type ?? "VR-CUSTOM",
26141
+ rule.reference_path ?? null,
26142
+ rule.severity?.toUpperCase() ?? "MEDIUM"
26143
+ );
26144
+ }
25829
26145
  stats.edgesCreated = buildCrossReferences(db);
25830
26146
  const finalNow = /* @__PURE__ */ new Date();
25831
26147
  const finalIso = finalNow.toISOString();
@@ -25848,10 +26164,10 @@ function isKnowledgeStale(db) {
25848
26164
  files.push(...discoverMarkdownFiles(paths.memoryDir));
25849
26165
  } catch {
25850
26166
  }
25851
- if (existsSync39(paths.plansDir)) {
26167
+ if (existsSync40(paths.plansDir)) {
25852
26168
  files.push(...discoverMarkdownFiles(paths.plansDir));
25853
26169
  }
25854
- if (existsSync39(paths.docsDir)) {
26170
+ if (existsSync40(paths.docsDir)) {
25855
26171
  const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
25856
26172
  const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f2) => !f2.includes("/plans/") && !excludePatterns.some((p19) => f2.includes(p19)));
25857
26173
  files.push(...docsFiles);
@@ -26211,7 +26527,7 @@ function handleRule(db, args2) {
26211
26527
  }
26212
26528
  if (withIncidents) {
26213
26529
  const edges = db.prepare(
26214
- "SELECT source_id FROM knowledge_edges WHERE target_type = 'cr' AND target_id = ? AND source_type = 'incident'"
26530
+ "SELECT source_id FROM knowledge_edges WHERE target_type = 'cr' AND target_id = ? AND source_type = 'incident' LIMIT 5000"
26215
26531
  ).all(ruleId);
26216
26532
  if (edges.length > 0) {
26217
26533
  lines.push("### Related Incidents");
@@ -26228,7 +26544,7 @@ function handleRule(db, args2) {
26228
26544
  }
26229
26545
  } else if (keyword) {
26230
26546
  const rules = db.prepare(
26231
- "SELECT * FROM knowledge_rules WHERE rule_text LIKE ? OR rule_id LIKE ? ORDER BY rule_id"
26547
+ "SELECT * FROM knowledge_rules WHERE rule_text LIKE ? OR rule_id LIKE ? ORDER BY rule_id LIMIT 1000"
26232
26548
  ).all(`%${keyword}%`, `%${keyword}%`);
26233
26549
  lines.push(`## Rules matching "${keyword}" (${rules.length} found)`);
26234
26550
  lines.push("");
@@ -26268,7 +26584,7 @@ function handleIncident(db, args2) {
26268
26584
  if (incident.user_quote) lines.push(`| **User Quote** | ${incident.user_quote.replace(/\|/g, "\\|")} |`);
26269
26585
  lines.push("");
26270
26586
  const edges = db.prepare(
26271
- "SELECT target_id FROM knowledge_edges WHERE source_type = 'incident' AND source_id = ? AND target_type = 'cr'"
26587
+ "SELECT target_id FROM knowledge_edges WHERE source_type = 'incident' AND source_id = ? AND target_type = 'cr' LIMIT 5000"
26272
26588
  ).all(String(incidentNum));
26273
26589
  if (edges.length > 0) {
26274
26590
  lines.push("### Linked Rules");
@@ -26313,7 +26629,7 @@ function handleSchemaCheck(db, args2) {
26313
26629
  const lines = [];
26314
26630
  if (table) {
26315
26631
  const mismatches = db.prepare(
26316
- "SELECT * FROM knowledge_schema_mismatches WHERE table_name = ?"
26632
+ "SELECT * FROM knowledge_schema_mismatches WHERE table_name = ? LIMIT 1000"
26317
26633
  ).all(table);
26318
26634
  if (mismatches.length > 0) {
26319
26635
  lines.push(`## Schema Mismatches for \`${table}\``);
@@ -26331,7 +26647,7 @@ function handleSchemaCheck(db, args2) {
26331
26647
  }
26332
26648
  if (column) {
26333
26649
  const mismatches = db.prepare(
26334
- "SELECT * FROM knowledge_schema_mismatches WHERE wrong_column = ?"
26650
+ "SELECT * FROM knowledge_schema_mismatches WHERE wrong_column = ? LIMIT 1000"
26335
26651
  ).all(column);
26336
26652
  if (mismatches.length > 0) {
26337
26653
  if (lines.length > 0) lines.push("");
@@ -26370,7 +26686,7 @@ function handlePattern(db, args2) {
26370
26686
  `patterns-quickref`
26371
26687
  ];
26372
26688
  const docs = db.prepare(
26373
- "SELECT id, file_path, title FROM knowledge_documents WHERE category = 'patterns' OR category = 'reference' OR category = 'root'"
26689
+ "SELECT id, file_path, title FROM knowledge_documents WHERE category = 'patterns' OR category = 'reference' OR category = 'root' LIMIT 1000"
26374
26690
  ).all();
26375
26691
  const relevantDocs = docs.filter(
26376
26692
  (d2) => domainPatterns.some((pat) => d2.file_path.toLowerCase().includes(pat.toLowerCase()))
@@ -26415,7 +26731,7 @@ function handlePattern(db, args2) {
26415
26731
  if (!topic && relevantDocs.length > 0) {
26416
26732
  for (const doc of relevantDocs.slice(0, 2)) {
26417
26733
  const chunks = db.prepare(
26418
- "SELECT heading, content, chunk_type FROM knowledge_chunks WHERE document_id = ? AND chunk_type = 'section' ORDER BY line_start"
26734
+ "SELECT heading, content, chunk_type FROM knowledge_chunks WHERE document_id = ? AND chunk_type = 'section' ORDER BY line_start LIMIT 5000"
26419
26735
  ).all(doc.id);
26420
26736
  lines.push(`### From: ${claudeDir}/${doc.file_path}`);
26421
26737
  lines.push("");
@@ -26471,7 +26787,7 @@ function handleVerification(db, args2) {
26471
26787
  if (vr.category) lines.push(`| **Category** | ${vr.category} |`);
26472
26788
  } else if (situation) {
26473
26789
  const results = db.prepare(
26474
- "SELECT * FROM knowledge_verifications WHERE use_when LIKE ? OR vr_type LIKE ? OR catches LIKE ? ORDER BY vr_type"
26790
+ "SELECT * FROM knowledge_verifications WHERE use_when LIKE ? OR vr_type LIKE ? OR catches LIKE ? ORDER BY vr_type LIMIT 1000"
26475
26791
  ).all(`%${situation}%`, `%${situation}%`, `%${situation}%`);
26476
26792
  lines.push(`## Verifications for "${situation}" (${results.length} found)`);
26477
26793
  lines.push("");
@@ -26509,10 +26825,10 @@ function handleGraph(db, args2) {
26509
26825
  lines.push("");
26510
26826
  for (const entity of currentLevel) {
26511
26827
  const outgoing = db.prepare(
26512
- "SELECT target_type, target_id, edge_type FROM knowledge_edges WHERE source_type = ? AND source_id = ?"
26828
+ "SELECT target_type, target_id, edge_type FROM knowledge_edges WHERE source_type = ? AND source_id = ? LIMIT 5000"
26513
26829
  ).all(entity.type, entity.id);
26514
26830
  const incoming = db.prepare(
26515
- "SELECT source_type, source_id, edge_type FROM knowledge_edges WHERE target_type = ? AND target_id = ?"
26831
+ "SELECT source_type, source_id, edge_type FROM knowledge_edges WHERE target_type = ? AND target_id = ? LIMIT 5000"
26516
26832
  ).all(entity.type, entity.id);
26517
26833
  for (const edge of outgoing) {
26518
26834
  const key = `${edge.target_type}:${edge.target_id}`;
@@ -26587,6 +26903,7 @@ function handleCommand(db, args2) {
26587
26903
  JOIN knowledge_documents kd ON kd.id = kc.document_id
26588
26904
  WHERE kc.chunk_type = 'command' AND (kc.content LIKE ? OR kc.heading LIKE ?)
26589
26905
  ORDER BY kc.heading
26906
+ LIMIT 1000
26590
26907
  `).all(`%${keyword}%`, `%${keyword}%`);
26591
26908
  lines.push(`## Commands matching "${keyword}" (${results.length} found)`);
26592
26909
  lines.push("");
@@ -26601,6 +26918,7 @@ function handleCommand(db, args2) {
26601
26918
  JOIN knowledge_documents kd ON kd.id = kc.document_id
26602
26919
  WHERE kc.chunk_type = 'command'
26603
26920
  ORDER BY kc.heading
26921
+ LIMIT 1000
26604
26922
  `).all();
26605
26923
  lines.push(`## All Commands (${commands.length} total)`);
26606
26924
  lines.push("");
@@ -26717,7 +27035,7 @@ function handlePlan(db, args2) {
26717
27035
  if (doc.description) lines.push(`**Description**: ${doc.description}`);
26718
27036
  lines.push("");
26719
27037
  const items = db.prepare(
26720
- "SELECT heading, content FROM knowledge_chunks WHERE document_id = ? AND metadata LIKE '%plan_item_id%' ORDER BY heading"
27038
+ "SELECT heading, content FROM knowledge_chunks WHERE document_id = ? AND metadata LIKE '%plan_item_id%' ORDER BY heading LIMIT 5000"
26721
27039
  ).all(doc.id);
26722
27040
  if (items.length > 0) {
26723
27041
  lines.push(`### Plan Items (${items.length})`);
@@ -26743,7 +27061,7 @@ function handlePlan(db, args2) {
26743
27061
  }
26744
27062
  } else if (status) {
26745
27063
  const docs = db.prepare(
26746
- "SELECT kd.file_path, kd.title, kc.content FROM knowledge_chunks kc JOIN knowledge_documents kd ON kd.id = kc.document_id WHERE kd.category = 'plan' AND kc.heading = 'IMPLEMENTATION STATUS' AND kc.content LIKE ? ORDER BY kd.file_path DESC"
27064
+ "SELECT kd.file_path, kd.title, kc.content FROM knowledge_chunks kc JOIN knowledge_documents kd ON kd.id = kc.document_id WHERE kd.category = 'plan' AND kc.heading = 'IMPLEMENTATION STATUS' AND kc.content LIKE ? ORDER BY kd.file_path DESC LIMIT 1000"
26747
27065
  ).all(`%${status}%`);
26748
27066
  lines.push(`## Plans with status "${status}" (${docs.length} found)`);
26749
27067
  lines.push("");
@@ -26829,7 +27147,7 @@ function handleGaps(db, args2) {
26829
27147
  }
26830
27148
  } else if (checkType === "patterns") {
26831
27149
  const patternDocs = db.prepare(
26832
- "SELECT file_path, title FROM knowledge_documents WHERE category = 'patterns'"
27150
+ "SELECT file_path, title FROM knowledge_documents WHERE category = 'patterns' LIMIT 10000"
26833
27151
  ).all();
26834
27152
  const documentedDomains = new Set(patternDocs.map((d2) => d2.title.replace("-patterns", "").replace(" Patterns", "").toLowerCase()));
26835
27153
  lines.push("### Pattern Coverage");
@@ -26839,7 +27157,7 @@ function handleGaps(db, args2) {
26839
27157
  try {
26840
27158
  dataDb = getDataDb();
26841
27159
  const domains = dataDb.prepare(
26842
- `SELECT DISTINCT domain FROM ${t("sentinel")} WHERE status = 'active' ORDER BY domain`
27160
+ `SELECT DISTINCT domain FROM ${t("sentinel")} WHERE status = 'active' ORDER BY domain LIMIT 10000`
26843
27161
  ).all();
26844
27162
  for (const d2 of domains) {
26845
27163
  const hasPattern = documentedDomains.has(d2.domain.toLowerCase());
@@ -26968,176 +27286,6 @@ var init_knowledge_tools = __esm({
26968
27286
  }
26969
27287
  });
26970
27288
 
26971
- // src/knowledge-db.ts
26972
- import Database3 from "better-sqlite3";
26973
- import { dirname as dirname17 } from "path";
26974
- import { existsSync as existsSync41, mkdirSync as mkdirSync11 } from "fs";
26975
- function getKnowledgeDb() {
26976
- const dbPath = getResolvedPaths().knowledgeDbPath;
26977
- const dir = dirname17(dbPath);
26978
- if (!existsSync41(dir)) {
26979
- mkdirSync11(dir, { recursive: true });
26980
- }
26981
- const db = new Database3(dbPath);
26982
- db.pragma("journal_mode = WAL");
26983
- db.pragma("foreign_keys = ON");
26984
- initKnowledgeSchema(db);
26985
- return db;
26986
- }
26987
- function initKnowledgeSchema(db) {
26988
- db.exec(`
26989
- -- Core document chunks (parsed from .claude/**/*.md)
26990
- CREATE TABLE IF NOT EXISTS knowledge_documents (
26991
- id INTEGER PRIMARY KEY AUTOINCREMENT,
26992
- file_path TEXT NOT NULL,
26993
- category TEXT NOT NULL,
26994
- title TEXT NOT NULL,
26995
- description TEXT,
26996
- content_hash TEXT NOT NULL,
26997
- indexed_at TEXT NOT NULL,
26998
- indexed_at_epoch INTEGER NOT NULL
26999
- );
27000
-
27001
- CREATE UNIQUE INDEX IF NOT EXISTS idx_kd_filepath ON knowledge_documents(file_path);
27002
- CREATE INDEX IF NOT EXISTS idx_kd_category ON knowledge_documents(category);
27003
-
27004
- -- Structured chunks within documents
27005
- CREATE TABLE IF NOT EXISTS knowledge_chunks (
27006
- id INTEGER PRIMARY KEY AUTOINCREMENT,
27007
- document_id INTEGER NOT NULL REFERENCES knowledge_documents(id) ON DELETE CASCADE,
27008
- chunk_type TEXT NOT NULL CHECK(chunk_type IN (
27009
- 'section', 'table_row', 'code_block', 'rule', 'incident', 'pattern', 'command', 'mismatch'
27010
- )),
27011
- heading TEXT,
27012
- content TEXT NOT NULL,
27013
- line_start INTEGER,
27014
- line_end INTEGER,
27015
- metadata TEXT DEFAULT '{}'
27016
- );
27017
-
27018
- CREATE INDEX IF NOT EXISTS idx_kc_doc ON knowledge_chunks(document_id);
27019
- CREATE INDEX IF NOT EXISTS idx_kc_type ON knowledge_chunks(chunk_type);
27020
- CREATE INDEX IF NOT EXISTS idx_kc_heading ON knowledge_chunks(heading);
27021
-
27022
- -- Canonical Rules index
27023
- CREATE TABLE IF NOT EXISTS knowledge_rules (
27024
- id INTEGER PRIMARY KEY AUTOINCREMENT,
27025
- rule_id TEXT UNIQUE NOT NULL,
27026
- rule_text TEXT NOT NULL,
27027
- vr_type TEXT,
27028
- reference_path TEXT,
27029
- severity TEXT DEFAULT 'HIGH',
27030
- prevention_summary TEXT
27031
- );
27032
-
27033
- CREATE INDEX IF NOT EXISTS idx_kr_id ON knowledge_rules(rule_id);
27034
-
27035
- -- Verification Requirements index
27036
- CREATE TABLE IF NOT EXISTS knowledge_verifications (
27037
- id INTEGER PRIMARY KEY AUTOINCREMENT,
27038
- vr_type TEXT UNIQUE NOT NULL,
27039
- command TEXT NOT NULL,
27040
- expected TEXT NOT NULL,
27041
- use_when TEXT NOT NULL,
27042
- catches TEXT,
27043
- category TEXT
27044
- );
27045
-
27046
- CREATE INDEX IF NOT EXISTS idx_kv_type ON knowledge_verifications(vr_type);
27047
- CREATE INDEX IF NOT EXISTS idx_kv_category ON knowledge_verifications(category);
27048
-
27049
- -- Incident index
27050
- CREATE TABLE IF NOT EXISTS knowledge_incidents (
27051
- id INTEGER PRIMARY KEY AUTOINCREMENT,
27052
- incident_num INTEGER UNIQUE NOT NULL,
27053
- date TEXT NOT NULL,
27054
- type TEXT NOT NULL,
27055
- gap_found TEXT NOT NULL,
27056
- prevention TEXT NOT NULL,
27057
- cr_added TEXT,
27058
- root_cause TEXT,
27059
- user_quote TEXT
27060
- );
27061
-
27062
- CREATE INDEX IF NOT EXISTS idx_ki_num ON knowledge_incidents(incident_num);
27063
- CREATE INDEX IF NOT EXISTS idx_ki_type ON knowledge_incidents(type);
27064
- CREATE INDEX IF NOT EXISTS idx_ki_cr ON knowledge_incidents(cr_added);
27065
-
27066
- -- Schema mismatch quick lookup
27067
- -- P-H013 (plan-stage-c-high-batch): the source column no longer has a
27068
- -- SQL DEFAULT. Previously the value was a JS template-literal
27069
- -- interpolation baked into the customer SQLite at schema creation
27070
- -- time, so later config changes were ignored. Default is now applied
27071
- -- at INSERT time via getConfig().conventions.knowledgeSourceFiles[0]
27072
- -- in knowledge-indexer.ts -- a runtime config lookup that reflects
27073
- -- the customers current config.
27074
- CREATE TABLE IF NOT EXISTS knowledge_schema_mismatches (
27075
- id INTEGER PRIMARY KEY AUTOINCREMENT,
27076
- table_name TEXT NOT NULL,
27077
- wrong_column TEXT NOT NULL,
27078
- correct_column TEXT NOT NULL,
27079
- source TEXT NOT NULL
27080
- );
27081
-
27082
- CREATE INDEX IF NOT EXISTS idx_ksm_table ON knowledge_schema_mismatches(table_name);
27083
-
27084
- -- Cross-reference graph
27085
- CREATE TABLE IF NOT EXISTS knowledge_edges (
27086
- id INTEGER PRIMARY KEY AUTOINCREMENT,
27087
- source_type TEXT NOT NULL,
27088
- source_id TEXT NOT NULL,
27089
- target_type TEXT NOT NULL,
27090
- target_id TEXT NOT NULL,
27091
- edge_type TEXT NOT NULL
27092
- );
27093
-
27094
- CREATE INDEX IF NOT EXISTS idx_ke_source ON knowledge_edges(source_type, source_id);
27095
- CREATE INDEX IF NOT EXISTS idx_ke_target ON knowledge_edges(target_type, target_id);
27096
- CREATE UNIQUE INDEX IF NOT EXISTS idx_ke_unique ON knowledge_edges(source_type, source_id, target_type, target_id, edge_type);
27097
-
27098
- -- Staleness tracking
27099
- CREATE TABLE IF NOT EXISTS knowledge_meta (
27100
- key TEXT PRIMARY KEY,
27101
- value TEXT NOT NULL
27102
- );
27103
- `);
27104
- try {
27105
- db.exec(`
27106
- CREATE VIRTUAL TABLE IF NOT EXISTS knowledge_fts USING fts5(
27107
- heading, content, chunk_type, file_path
27108
- );
27109
-
27110
- CREATE TRIGGER IF NOT EXISTS kc_fts_insert AFTER INSERT ON knowledge_chunks BEGIN
27111
- INSERT INTO knowledge_fts(rowid, heading, content, chunk_type, file_path)
27112
- SELECT new.id, new.heading, new.content, new.chunk_type, kd.file_path
27113
- FROM knowledge_documents kd WHERE kd.id = new.document_id;
27114
- END;
27115
-
27116
- CREATE TRIGGER IF NOT EXISTS kc_fts_delete AFTER DELETE ON knowledge_chunks BEGIN
27117
- INSERT INTO knowledge_fts(knowledge_fts, rowid, heading, content, chunk_type, file_path)
27118
- SELECT 'delete', old.id, old.heading, old.content, old.chunk_type, kd.file_path
27119
- FROM knowledge_documents kd WHERE kd.id = old.document_id;
27120
- END;
27121
-
27122
- CREATE TRIGGER IF NOT EXISTS kc_fts_update AFTER UPDATE ON knowledge_chunks BEGIN
27123
- INSERT INTO knowledge_fts(knowledge_fts, rowid, heading, content, chunk_type, file_path)
27124
- SELECT 'delete', old.id, old.heading, old.content, old.chunk_type, kd.file_path
27125
- FROM knowledge_documents kd WHERE kd.id = old.document_id;
27126
- INSERT INTO knowledge_fts(rowid, heading, content, chunk_type, file_path)
27127
- SELECT new.id, new.heading, new.content, new.chunk_type, kd.file_path
27128
- FROM knowledge_documents kd WHERE kd.id = new.document_id;
27129
- END;
27130
- `);
27131
- } catch {
27132
- }
27133
- }
27134
- var init_knowledge_db = __esm({
27135
- "src/knowledge-db.ts"() {
27136
- "use strict";
27137
- init_config();
27138
- }
27139
- });
27140
-
27141
27289
  // src/python/domain-enforcer.ts
27142
27290
  function classifyPythonFileDomain(file) {
27143
27291
  const config = getConfig();
@@ -27334,7 +27482,7 @@ function handlePyImports(args2, dataDb) {
27334
27482
  if (depth > maxDepth || visited.has(f2)) return;
27335
27483
  visited.add(f2);
27336
27484
  const imports = dataDb.prepare(
27337
- `SELECT target_file, imported_names FROM ${t("py_imports")} WHERE source_file = ?`
27485
+ `SELECT target_file, imported_names FROM ${t("py_imports")} WHERE source_file = ? LIMIT 10000`
27338
27486
  ).all(f2);
27339
27487
  for (const imp of imports) {
27340
27488
  const indent = " ".repeat(depth);
@@ -27349,7 +27497,7 @@ function handlePyImports(args2, dataDb) {
27349
27497
  traverse2(file, 0);
27350
27498
  } else {
27351
27499
  const imports = dataDb.prepare(
27352
- `SELECT target_file, import_type, imported_names, line FROM ${t("py_imports")} WHERE source_file = ? ORDER BY line`
27500
+ `SELECT target_file, import_type, imported_names, line FROM ${t("py_imports")} WHERE source_file = ? ORDER BY line LIMIT 10000`
27353
27501
  ).all(file);
27354
27502
  lines.push(`## Imports from ${file} (${imports.length} edges)`);
27355
27503
  for (const imp of imports) {
@@ -27359,7 +27507,7 @@ function handlePyImports(args2, dataDb) {
27359
27507
  }
27360
27508
  } else if (importedBy) {
27361
27509
  const importers = dataDb.prepare(
27362
- `SELECT source_file, imported_names, line FROM ${t("py_imports")} WHERE target_file = ? ORDER BY source_file`
27510
+ `SELECT source_file, imported_names, line FROM ${t("py_imports")} WHERE target_file = ? ORDER BY source_file LIMIT 10000`
27363
27511
  ).all(importedBy);
27364
27512
  lines.push(`## Files importing ${importedBy} (${importers.length} edges)`);
27365
27513
  for (const imp of importers) {
@@ -27606,14 +27754,14 @@ function handlePyImpact(args2, dataDb) {
27606
27754
  const file = args2.file;
27607
27755
  const lines = [`## Impact Analysis: ${file}`, ""];
27608
27756
  const importedBy = dataDb.prepare(
27609
- `SELECT source_file FROM ${t("py_imports")} WHERE target_file = ?`
27757
+ `SELECT source_file FROM ${t("py_imports")} WHERE target_file = ? LIMIT 10000`
27610
27758
  ).all(file);
27611
27759
  lines.push(`### Imported By (${importedBy.length} files)`);
27612
27760
  for (const imp of importedBy.slice(0, 20)) {
27613
27761
  lines.push(`- ${imp.source_file}`);
27614
27762
  }
27615
27763
  const routes = dataDb.prepare(
27616
- `SELECT method, path, function_name FROM ${t("py_routes")} WHERE file = ?`
27764
+ `SELECT method, path, function_name FROM ${t("py_routes")} WHERE file = ? LIMIT 10000`
27617
27765
  ).all(file);
27618
27766
  if (routes.length > 0) {
27619
27767
  lines.push(`
@@ -27623,7 +27771,7 @@ function handlePyImpact(args2, dataDb) {
27623
27771
  }
27624
27772
  }
27625
27773
  const models = dataDb.prepare(
27626
- `SELECT class_name, table_name FROM ${t("py_models")} WHERE file = ?`
27774
+ `SELECT class_name, table_name FROM ${t("py_models")} WHERE file = ? LIMIT 10000`
27627
27775
  ).all(file);
27628
27776
  if (models.length > 0) {
27629
27777
  lines.push(`
@@ -27636,7 +27784,7 @@ function handlePyImpact(args2, dataDb) {
27636
27784
  if (routeIds.length > 0) {
27637
27785
  const placeholders = routeIds.map(() => "?").join(",");
27638
27786
  const callers = dataDb.prepare(
27639
- `SELECT DISTINCT frontend_file FROM ${t("py_route_callers")} WHERE route_id IN (${placeholders})`
27787
+ `SELECT DISTINCT frontend_file FROM ${t("py_route_callers")} WHERE route_id IN (${placeholders}) LIMIT 100000`
27640
27788
  ).all(...routeIds.map((r2) => r2.id));
27641
27789
  if (callers.length > 0) {
27642
27790
  lines.push(`
@@ -27677,14 +27825,14 @@ function handlePyContext(args2, dataDb) {
27677
27825
  lines.push(`- ${imp.source_file}`);
27678
27826
  }
27679
27827
  }
27680
- const routes = dataDb.prepare(`SELECT method, path, function_name, line FROM ${t("py_routes")} WHERE file = ?`).all(file);
27828
+ const routes = dataDb.prepare(`SELECT method, path, function_name, line FROM ${t("py_routes")} WHERE file = ? LIMIT 10000`).all(file);
27681
27829
  if (routes.length > 0) {
27682
27830
  lines.push("\n### Routes");
27683
27831
  for (const r2 of routes) {
27684
27832
  lines.push(`- ${r2.method} ${r2.path} \u2192 ${r2.function_name} (L${r2.line})`);
27685
27833
  }
27686
27834
  }
27687
- const models = dataDb.prepare(`SELECT class_name, table_name, line FROM ${t("py_models")} WHERE file = ?`).all(file);
27835
+ const models = dataDb.prepare(`SELECT class_name, table_name, line FROM ${t("py_models")} WHERE file = ? LIMIT 10000`).all(file);
27688
27836
  if (models.length > 0) {
27689
27837
  lines.push("\n### Models");
27690
27838
  for (const m3 of models) {
@@ -27946,29 +28094,37 @@ function assertCodegraphDb(codegraphDb, toolName) {
27946
28094
  }
27947
28095
  return codegraphDb;
27948
28096
  }
27949
- async function handleToolCall(name, args2, dataDb, codegraphDb) {
28097
+ async function handleToolCall(name, args2, dataDb, codegraphDb, memoryDbCache, knowledgeDbCache) {
27950
28098
  const userTier = await getCurrentTier();
27951
28099
  const requiredTier = getToolTier(name);
27952
28100
  if (!isToolAllowed(name, userTier)) {
27953
28101
  return text17(`This tool requires ${requiredTier} tier. Current tier: ${userTier}. Upgrade at https://massu.ai/pricing`);
27954
28102
  }
27955
28103
  const pfx = prefix2();
28104
+ async function withMemDb(fn) {
28105
+ if (memoryDbCache) return fn(memoryDbCache);
28106
+ const db = getMemoryDb();
28107
+ try {
28108
+ return await fn(db);
28109
+ } finally {
28110
+ db.close();
28111
+ }
28112
+ }
28113
+ async function withKnowledgeDb(fn) {
28114
+ if (knowledgeDbCache) return fn(knowledgeDbCache);
28115
+ const db = getKnowledgeDb();
28116
+ try {
28117
+ return await fn(db);
28118
+ } finally {
28119
+ db.close();
28120
+ }
28121
+ }
27956
28122
  try {
27957
28123
  if (name.startsWith(pfx + "_memory_")) {
27958
- const memDb = getMemoryDb();
27959
- try {
27960
- return handleMemoryToolCall(name, args2, memDb);
27961
- } finally {
27962
- memDb.close();
27963
- }
28124
+ return withMemDb((memDb) => handleMemoryToolCall(name, args2, memDb));
27964
28125
  }
27965
28126
  if (isObservabilityTool(name)) {
27966
- const memDb = getMemoryDb();
27967
- try {
27968
- return handleObservabilityToolCall(name, args2, memDb);
27969
- } finally {
27970
- memDb.close();
27971
- }
28127
+ return withMemDb((memDb) => handleObservabilityToolCall(name, args2, memDb));
27972
28128
  }
27973
28129
  if (name.startsWith(pfx + "_docs_")) {
27974
28130
  return handleDocsToolCall(name, args2);
@@ -27977,92 +28133,37 @@ async function handleToolCall(name, args2, dataDb, codegraphDb) {
27977
28133
  return handleSentinelToolCall(name, args2, assertDataDb(dataDb, name));
27978
28134
  }
27979
28135
  if (isAnalyticsTool(name)) {
27980
- const memDb = getMemoryDb();
27981
- try {
27982
- return handleAnalyticsToolCall(name, args2, memDb);
27983
- } finally {
27984
- memDb.close();
27985
- }
28136
+ return withMemDb((memDb) => handleAnalyticsToolCall(name, args2, memDb));
27986
28137
  }
27987
28138
  if (isCostTool(name)) {
27988
- const memDb = getMemoryDb();
27989
- try {
27990
- return handleCostToolCall(name, args2, memDb);
27991
- } finally {
27992
- memDb.close();
27993
- }
28139
+ return withMemDb((memDb) => handleCostToolCall(name, args2, memDb));
27994
28140
  }
27995
28141
  if (isPromptTool(name)) {
27996
- const memDb = getMemoryDb();
27997
- try {
27998
- return handlePromptToolCall(name, args2, memDb);
27999
- } finally {
28000
- memDb.close();
28001
- }
28142
+ return withMemDb((memDb) => handlePromptToolCall(name, args2, memDb));
28002
28143
  }
28003
28144
  if (isAuditTool(name)) {
28004
- const memDb = getMemoryDb();
28005
- try {
28006
- return handleAuditToolCall(name, args2, memDb);
28007
- } finally {
28008
- memDb.close();
28009
- }
28145
+ return withMemDb((memDb) => handleAuditToolCall(name, args2, memDb));
28010
28146
  }
28011
28147
  if (isValidationTool(name)) {
28012
- const memDb = getMemoryDb();
28013
- try {
28014
- return handleValidationToolCall(name, args2, memDb);
28015
- } finally {
28016
- memDb.close();
28017
- }
28148
+ return withMemDb((memDb) => handleValidationToolCall(name, args2, memDb));
28018
28149
  }
28019
28150
  if (isAdrTool(name)) {
28020
- const memDb = getMemoryDb();
28021
- try {
28022
- return handleAdrToolCall(name, args2, memDb);
28023
- } finally {
28024
- memDb.close();
28025
- }
28151
+ return withMemDb((memDb) => handleAdrToolCall(name, args2, memDb));
28026
28152
  }
28027
28153
  if (isSecurityTool(name)) {
28028
- const memDb = getMemoryDb();
28029
- try {
28030
- return handleSecurityToolCall(name, args2, memDb);
28031
- } finally {
28032
- memDb.close();
28033
- }
28154
+ return withMemDb((memDb) => handleSecurityToolCall(name, args2, memDb));
28034
28155
  }
28035
28156
  if (isDependencyTool(name)) {
28036
- const memDb = getMemoryDb();
28037
- try {
28038
- return handleDependencyToolCall(name, args2, memDb);
28039
- } finally {
28040
- memDb.close();
28041
- }
28157
+ return withMemDb((memDb) => handleDependencyToolCall(name, args2, memDb));
28042
28158
  }
28043
28159
  if (isTeamTool(name) && isCloudFeatureAvailable()) {
28044
- const memDb = getMemoryDb();
28045
- try {
28046
- return handleTeamToolCall(name, args2, memDb);
28047
- } finally {
28048
- memDb.close();
28049
- }
28160
+ return withMemDb((memDb) => handleTeamToolCall(name, args2, memDb));
28050
28161
  }
28051
28162
  if (isRegressionTool(name)) {
28052
- const memDb = getMemoryDb();
28053
- try {
28054
- return handleRegressionToolCall(name, args2, memDb);
28055
- } finally {
28056
- memDb.close();
28057
- }
28163
+ return withMemDb((memDb) => handleRegressionToolCall(name, args2, memDb));
28058
28164
  }
28059
28165
  if (isKnowledgeTool(name)) {
28060
- const knowledgeDb = getKnowledgeDb();
28061
- try {
28062
- return handleKnowledgeToolCall(name, args2, knowledgeDb);
28063
- } finally {
28064
- knowledgeDb.close();
28065
- }
28166
+ return withKnowledgeDb((kdb) => handleKnowledgeToolCall(name, args2, kdb));
28066
28167
  }
28067
28168
  if (isPythonTool(name)) {
28068
28169
  const pyDataDb = assertDataDb(dataDb, name);
@@ -28070,12 +28171,7 @@ async function handleToolCall(name, args2, dataDb, codegraphDb) {
28070
28171
  return handlePythonToolCall(name, args2, pyDataDb);
28071
28172
  }
28072
28173
  if (isLicenseTool(name)) {
28073
- const memDb = getMemoryDb();
28074
- try {
28075
- return await handleLicenseToolCall(name, args2, memDb);
28076
- } finally {
28077
- memDb.close();
28078
- }
28174
+ return withMemDb((memDb) => handleLicenseToolCall(name, args2, memDb));
28079
28175
  }
28080
28176
  const baseName = stripPrefix3(name);
28081
28177
  switch (baseName) {
@@ -28143,7 +28239,7 @@ Feature scan failed: ${error instanceof Error ? error.message : String(error)}`)
28143
28239
  function handleContext(file, dataDb, codegraphDb) {
28144
28240
  const lines = [];
28145
28241
  const nodes = codegraphDb.prepare(
28146
- "SELECT name, kind, start_line, end_line FROM nodes WHERE file_path = ? ORDER BY start_line"
28242
+ "SELECT name, kind, start_line, end_line FROM nodes WHERE file_path = ? ORDER BY start_line LIMIT 10000"
28147
28243
  ).all(file);
28148
28244
  if (nodes.length > 0) {
28149
28245
  lines.push("## CodeGraph Nodes");
@@ -28260,7 +28356,7 @@ function handleContext(file, dataDb, codegraphDb) {
28260
28356
  if (relevantCRs.length > 0) {
28261
28357
  const placeholders = relevantCRs.map(() => "?").join(",");
28262
28358
  const crRules = knowledgeDb.prepare(
28263
- `SELECT rule_id, rule_text, vr_type FROM knowledge_rules WHERE rule_id IN (${placeholders})`
28359
+ `SELECT rule_id, rule_text, vr_type FROM knowledge_rules WHERE rule_id IN (${placeholders}) LIMIT 10000`
28264
28360
  ).all(...relevantCRs);
28265
28361
  if (crRules.length > 0) {
28266
28362
  lines.push("## Relevant Canonical Rules");
@@ -28357,7 +28453,7 @@ function handleTrpcMap(args2, dataDb) {
28357
28453
  const lines = [];
28358
28454
  if (args2.uncoupled) {
28359
28455
  const uncoupled = dataDb.prepare(
28360
- `SELECT router_name, procedure_name, procedure_type, router_file FROM ${t("trpc_procedures")} WHERE has_ui_caller = 0 ORDER BY router_name, procedure_name`
28456
+ `SELECT router_name, procedure_name, procedure_type, router_file FROM ${t("trpc_procedures")} WHERE has_ui_caller = 0 ORDER BY router_name, procedure_name LIMIT 10000`
28361
28457
  ).all();
28362
28458
  lines.push(`## Uncoupled Procedures (${uncoupled.length} total)`);
28363
28459
  lines.push("These procedures have ZERO UI callers.");
@@ -28372,7 +28468,7 @@ function handleTrpcMap(args2, dataDb) {
28372
28468
  }
28373
28469
  } else if (args2.router) {
28374
28470
  const procs = dataDb.prepare(
28375
- `SELECT id, procedure_name, procedure_type, has_ui_caller FROM ${t("trpc_procedures")} WHERE router_name = ? ORDER BY procedure_name`
28471
+ `SELECT id, procedure_name, procedure_type, has_ui_caller FROM ${t("trpc_procedures")} WHERE router_name = ? ORDER BY procedure_name LIMIT 10000`
28376
28472
  ).all(args2.router);
28377
28473
  lines.push(`## Router: ${args2.router} (${procs.length} procedures)`);
28378
28474
  lines.push("");
@@ -28380,7 +28476,7 @@ function handleTrpcMap(args2, dataDb) {
28380
28476
  const status = proc.has_ui_caller ? "" : " [NO UI CALLERS]";
28381
28477
  lines.push(`### ${args2.router}.${proc.procedure_name} (${proc.procedure_type})${status}`);
28382
28478
  const callSites = dataDb.prepare(
28383
- `SELECT file, line, call_pattern FROM ${t("trpc_call_sites")} WHERE procedure_id = ?`
28479
+ `SELECT file, line, call_pattern FROM ${t("trpc_call_sites")} WHERE procedure_id = ? LIMIT 10000`
28384
28480
  ).all(proc.id);
28385
28481
  if (callSites.length > 0) {
28386
28482
  lines.push("UI Call Sites:");
@@ -28394,7 +28490,7 @@ function handleTrpcMap(args2, dataDb) {
28394
28490
  }
28395
28491
  } else if (args2.procedure) {
28396
28492
  const procs = dataDb.prepare(
28397
- `SELECT id, router_name, router_file, procedure_type, has_ui_caller FROM ${t("trpc_procedures")} WHERE procedure_name = ? ORDER BY router_name`
28493
+ `SELECT id, router_name, router_file, procedure_type, has_ui_caller FROM ${t("trpc_procedures")} WHERE procedure_name = ? ORDER BY router_name LIMIT 10000`
28398
28494
  ).all(args2.procedure);
28399
28495
  lines.push(`## Procedure "${args2.procedure}" found in ${procs.length} routers`);
28400
28496
  lines.push("");
@@ -28402,7 +28498,7 @@ function handleTrpcMap(args2, dataDb) {
28402
28498
  lines.push(`### ${proc.router_name}.${args2.procedure} (${proc.procedure_type})`);
28403
28499
  lines.push(`File: ${proc.router_file}`);
28404
28500
  const callSites = dataDb.prepare(
28405
- `SELECT file, line, call_pattern FROM ${t("trpc_call_sites")} WHERE procedure_id = ?`
28501
+ `SELECT file, line, call_pattern FROM ${t("trpc_call_sites")} WHERE procedure_id = ? LIMIT 10000`
28406
28502
  ).all(proc.id);
28407
28503
  if (callSites.length > 0) {
28408
28504
  lines.push("UI Call Sites:");
@@ -28447,11 +28543,11 @@ function handleCouplingCheck(args2, dataDb, codegraphDb) {
28447
28543
  let uncoupledProcs;
28448
28544
  if (stagedFiles) {
28449
28545
  uncoupledProcs = dataDb.prepare(
28450
- `SELECT router_name, procedure_name, procedure_type, router_file FROM ${t("trpc_procedures")} WHERE has_ui_caller = 0 AND router_file IN (${stagedFiles.map(() => "?").join(",")})`
28546
+ `SELECT router_name, procedure_name, procedure_type, router_file FROM ${t("trpc_procedures")} WHERE has_ui_caller = 0 AND router_file IN (${stagedFiles.map(() => "?").join(",")}) LIMIT 10000`
28451
28547
  ).all(...stagedFiles);
28452
28548
  } else {
28453
28549
  uncoupledProcs = dataDb.prepare(
28454
- `SELECT router_name, procedure_name, procedure_type, router_file FROM ${t("trpc_procedures")} WHERE has_ui_caller = 0`
28550
+ `SELECT router_name, procedure_name, procedure_type, router_file FROM ${t("trpc_procedures")} WHERE has_ui_caller = 0 LIMIT 10000`
28455
28551
  ).all();
28456
28552
  }
28457
28553
  lines.push("## Coupling Check Results");
@@ -28467,12 +28563,12 @@ function handleCouplingCheck(args2, dataDb, codegraphDb) {
28467
28563
  lines.push("");
28468
28564
  }
28469
28565
  const allPages = codegraphDb.prepare(
28470
- "SELECT path FROM files WHERE path LIKE 'src/app/%/page.tsx' OR path = 'src/app/page.tsx'"
28566
+ "SELECT path FROM files WHERE path LIKE 'src/app/%/page.tsx' OR path = 'src/app/page.tsx' LIMIT 10000"
28471
28567
  ).all();
28472
28568
  const pageImports = /* @__PURE__ */ new Set();
28473
28569
  for (const page of allPages) {
28474
28570
  const imports = dataDb.prepare(
28475
- `SELECT target_file FROM ${t("imports")} WHERE source_file = ?`
28571
+ `SELECT target_file FROM ${t("imports")} WHERE source_file = ? LIMIT 10000`
28476
28572
  ).all(page.path);
28477
28573
  for (const imp of imports) {
28478
28574
  pageImports.add(imp.target_file);
@@ -28482,7 +28578,7 @@ function handleCouplingCheck(args2, dataDb, codegraphDb) {
28482
28578
  if (stagedFiles) {
28483
28579
  const placeholders = stagedFiles.map(() => "?").join(",");
28484
28580
  componentFiles = codegraphDb.prepare(
28485
- `SELECT path FROM files WHERE path LIKE 'src/components/%' AND path IN (${placeholders})`
28581
+ `SELECT path FROM files WHERE path LIKE 'src/components/%' AND path IN (${placeholders}) LIMIT 10000`
28486
28582
  ).all(...stagedFiles);
28487
28583
  } else {
28488
28584
  componentFiles = [];
@@ -28544,7 +28640,7 @@ function handleImpact2(file, dataDb, codegraphDb) {
28544
28640
  const fileDomain = classifyFile(file);
28545
28641
  lines.push(`### Domain: ${fileDomain}`);
28546
28642
  const imports = dataDb.prepare(
28547
- `SELECT target_file FROM ${t("imports")} WHERE source_file = ?`
28643
+ `SELECT target_file FROM ${t("imports")} WHERE source_file = ? LIMIT 10000`
28548
28644
  ).all(file);
28549
28645
  const crossings = [];
28550
28646
  for (const imp of imports) {
@@ -28897,10 +28993,14 @@ var init_tool_db_needs = __esm({
28897
28993
  function createDispatcher(options) {
28898
28994
  let codegraphDbCache = null;
28899
28995
  let dataDbCache = null;
28996
+ let memoryDbCache = null;
28997
+ let knowledgeDbCache = null;
28900
28998
  function resolveDbsForTool(toolName) {
28901
28999
  const needs = getToolDbNeeds(toolName, getConfig().toolPrefix);
28902
29000
  let dataDbResolved;
28903
29001
  let codegraphDbResolved;
29002
+ let memoryDbResolved;
29003
+ let knowledgeDbResolved;
28904
29004
  if (needs.includes("data")) {
28905
29005
  if (!dataDbCache) dataDbCache = getDataDb();
28906
29006
  dataDbResolved = dataDbCache;
@@ -28909,7 +29009,21 @@ function createDispatcher(options) {
28909
29009
  if (!codegraphDbCache) codegraphDbCache = getCodeGraphDb();
28910
29010
  codegraphDbResolved = codegraphDbCache;
28911
29011
  }
28912
- return { needs, dataDb: dataDbResolved, codegraphDb: codegraphDbResolved };
29012
+ if (needs.includes("memory")) {
29013
+ if (!memoryDbCache) memoryDbCache = getMemoryDb();
29014
+ memoryDbResolved = memoryDbCache;
29015
+ }
29016
+ if (needs.includes("knowledge")) {
29017
+ if (!knowledgeDbCache) knowledgeDbCache = getKnowledgeDb();
29018
+ knowledgeDbResolved = knowledgeDbCache;
29019
+ }
29020
+ return {
29021
+ needs,
29022
+ dataDb: dataDbResolved,
29023
+ codegraphDb: codegraphDbResolved,
29024
+ memoryDb: memoryDbResolved,
29025
+ knowledgeDb: knowledgeDbResolved
29026
+ };
28913
29027
  }
28914
29028
  async function handleRequest(request) {
28915
29029
  const { method, params, id } = request;
@@ -28939,8 +29053,8 @@ function createDispatcher(options) {
28939
29053
  const toolName = params?.name ?? "";
28940
29054
  const toolArgs = params?.arguments ?? {};
28941
29055
  try {
28942
- const { dataDb, codegraphDb } = resolveDbsForTool(toolName);
28943
- const result = await handleToolCall(toolName, toolArgs, dataDb, codegraphDb);
29056
+ const { dataDb, codegraphDb, memoryDb, knowledgeDb } = resolveDbsForTool(toolName);
29057
+ const result = await handleToolCall(toolName, toolArgs, dataDb, codegraphDb, memoryDb, knowledgeDb);
28944
29058
  return { jsonrpc: "2.0", id: id ?? null, result };
28945
29059
  } catch (err) {
28946
29060
  if (err instanceof CodegraphDbNotInitializedError) {
@@ -29032,6 +29146,14 @@ function createDispatcher(options) {
29032
29146
  dataDbCache.close();
29033
29147
  dataDbCache = null;
29034
29148
  }
29149
+ if (memoryDbCache) {
29150
+ memoryDbCache.close();
29151
+ memoryDbCache = null;
29152
+ }
29153
+ if (knowledgeDbCache) {
29154
+ knowledgeDbCache.close();
29155
+ knowledgeDbCache = null;
29156
+ }
29035
29157
  }
29036
29158
  return { handleRequest, processLine, closeCachedDbs };
29037
29159
  }
@@ -29039,6 +29161,8 @@ var init_server_dispatch = __esm({
29039
29161
  "src/server-dispatch.ts"() {
29040
29162
  "use strict";
29041
29163
  init_db();
29164
+ init_memory_db();
29165
+ init_knowledge_db();
29042
29166
  init_config();
29043
29167
  init_tools();
29044
29168
  init_tool_db_needs();
@@ -29646,6 +29770,15 @@ async function main() {
29646
29770
  break;
29647
29771
  }
29648
29772
  default: {
29773
+ if (subcommand && !subcommand.startsWith("-")) {
29774
+ process.stderr.write(
29775
+ `Error: Unknown subcommand '${subcommand}'.
29776
+ Run \`npx @massu/core --help\` for the list of available subcommands.
29777
+ To start the MCP server, run without a subcommand (e.g. \`npx @massu/core\`).
29778
+ `
29779
+ );
29780
+ process.exit(2);
29781
+ }
29649
29782
  await Promise.resolve().then(() => (init_server(), server_exports));
29650
29783
  }
29651
29784
  }