@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.
- package/CHANGELOG.md +1334 -0
- package/dist/adapter.js +6 -4
- package/dist/cli.js +581 -448
- package/dist/detect/adapters/.bundle-shasums.json +4 -7
- package/dist/detect/adapters/rails.js +6 -4
- package/dist/detect/adapters/spring.js +6 -4
- package/dist/detect/adapters/tree-sitter-loader.js +6 -4
- package/dist/hooks/auto-learning-pipeline.js +24 -1
- package/dist/hooks/classify-failure.js +32 -3
- package/dist/hooks/cost-tracker.js +24 -0
- package/dist/hooks/fix-detector.js +97 -1
- package/dist/hooks/incident-pipeline.js +30 -1
- package/dist/hooks/intent-suggester.js +6 -3
- package/dist/hooks/post-edit-context.js +24 -1
- package/dist/hooks/post-tool-use.js +67 -19
- package/dist/hooks/pre-compact.js +28 -3
- package/dist/hooks/pre-delete-check.js +53 -27
- package/dist/hooks/pre-tool-use-gate.js +962 -0
- package/dist/hooks/quality-event.js +24 -0
- package/dist/hooks/rule-enforcement-pipeline.js +24 -1
- package/dist/hooks/security-gate.js +29 -16
- package/dist/hooks/session-end.js +31 -6
- package/dist/hooks/session-start.js +8709 -8459
- package/dist/hooks/user-prompt.js +25 -1
- package/package.json +15 -18
- package/commands/README.md +0 -134
- package/src/adapter.ts +0 -121
- package/src/adr-generator.ts +0 -292
- package/src/analytics.ts +0 -373
- package/src/audit-trail.ts +0 -450
- package/src/backfill-sessions.ts +0 -182
- package/src/changelog-generator.ts +0 -178
- package/src/cli.ts +0 -246
- package/src/cloud-sync.ts +0 -234
- package/src/commands/adapters.ts +0 -824
- package/src/commands/changelog.ts +0 -165
- package/src/commands/config-check-drift.ts +0 -133
- package/src/commands/config-refresh.ts +0 -396
- package/src/commands/config-upgrade.ts +0 -127
- package/src/commands/doctor.ts +0 -485
- package/src/commands/hook-runner.ts +0 -145
- package/src/commands/init.ts +0 -1823
- package/src/commands/install-commands.ts +0 -692
- package/src/commands/install-hooks.ts +0 -27
- package/src/commands/permissions.ts +0 -150
- package/src/commands/refresh-log.ts +0 -37
- package/src/commands/show-template.ts +0 -65
- package/src/commands/template-engine.ts +0 -301
- package/src/commands/watch.ts +0 -430
- package/src/config.ts +0 -765
- package/src/cost-tracker.ts +0 -355
- package/src/db.ts +0 -370
- package/src/dependency-scorer.ts +0 -337
- package/src/detect/adapters/aspnet.ts +0 -4
- package/src/detect/adapters/discover.ts +0 -469
- package/src/detect/adapters/file-sampler.ts +0 -225
- package/src/detect/adapters/go-chi.ts +0 -4
- package/src/detect/adapters/index.ts +0 -51
- package/src/detect/adapters/nextjs-trpc.ts +0 -166
- package/src/detect/adapters/parse-guard.ts +0 -133
- package/src/detect/adapters/phoenix.ts +0 -4
- package/src/detect/adapters/python-django.ts +0 -208
- package/src/detect/adapters/python-fastapi.ts +0 -223
- package/src/detect/adapters/python-flask.ts +0 -235
- package/src/detect/adapters/query-helpers.ts +0 -170
- package/src/detect/adapters/rails.ts +0 -4
- package/src/detect/adapters/runner.ts +0 -284
- package/src/detect/adapters/spring.ts +0 -4
- package/src/detect/adapters/swift-swiftui.ts +0 -171
- package/src/detect/adapters/tree-sitter-loader.ts +0 -517
- package/src/detect/adapters/types.ts +0 -191
- package/src/detect/codebase-introspector.ts +0 -210
- package/src/detect/domain-inferrer.ts +0 -197
- package/src/detect/drift.ts +0 -199
- package/src/detect/framework-detector.ts +0 -307
- package/src/detect/index.ts +0 -209
- package/src/detect/manifest-registry.ts +0 -261
- package/src/detect/migrate.ts +0 -362
- package/src/detect/monorepo-detector.ts +0 -348
- package/src/detect/package-detector.ts +0 -828
- package/src/detect/passthrough.ts +0 -108
- package/src/detect/regex-fallback.ts +0 -449
- package/src/detect/source-dir-detector.ts +0 -297
- package/src/detect/vr-command-map.ts +0 -167
- package/src/docs-map.json +0 -100
- package/src/docs-tools.ts +0 -519
- package/src/domains.ts +0 -182
- package/src/hooks/auto-learning-pipeline.ts +0 -228
- package/src/hooks/classify-failure.ts +0 -259
- package/src/hooks/cost-tracker.ts +0 -66
- package/src/hooks/fix-detector.ts +0 -186
- package/src/hooks/incident-pipeline.ts +0 -190
- package/src/hooks/intent-suggester.ts +0 -131
- package/src/hooks/post-edit-context.ts +0 -91
- package/src/hooks/post-tool-use.ts +0 -413
- package/src/hooks/pre-compact.ts +0 -168
- package/src/hooks/pre-delete-check.ts +0 -243
- package/src/hooks/quality-event.ts +0 -127
- package/src/hooks/rule-enforcement-pipeline.ts +0 -159
- package/src/hooks/security-gate.ts +0 -153
- package/src/hooks/session-end.ts +0 -467
- package/src/hooks/session-start.ts +0 -436
- package/src/hooks/user-prompt.ts +0 -157
- package/src/import-resolver.ts +0 -226
- package/src/knowledge-db.ts +0 -176
- package/src/knowledge-indexer.ts +0 -708
- package/src/knowledge-tools.ts +0 -1414
- package/src/lib/fileLock.ts +0 -203
- package/src/lib/framework-supports.ts +0 -85
- package/src/lib/gitToplevel.ts +0 -22
- package/src/lib/hook-registry.ts +0 -43
- package/src/lib/installLock.ts +0 -66
- package/src/lib/memory-path.ts +0 -49
- package/src/lib/pidLiveness.ts +0 -67
- package/src/lib/settings-local.ts +0 -110
- package/src/lib/sql-table-names.ts +0 -71
- package/src/license.ts +0 -547
- package/src/lsp/auto-detect.ts +0 -98
- package/src/lsp/client.ts +0 -776
- package/src/lsp/enrich.ts +0 -127
- package/src/lsp/types.ts +0 -221
- package/src/memory-db.ts +0 -1614
- package/src/memory-file-ingest.ts +0 -128
- package/src/memory-tools.ts +0 -424
- package/src/middleware-tree.ts +0 -71
- package/src/observability-tools.ts +0 -343
- package/src/observation-extractor.ts +0 -418
- package/src/page-deps.ts +0 -285
- package/src/permissions.ts +0 -422
- package/src/prompt-analyzer.ts +0 -332
- package/src/python/coupling-detector.ts +0 -125
- package/src/python/domain-enforcer.ts +0 -84
- package/src/python/impact-analyzer.ts +0 -96
- package/src/python/import-parser.ts +0 -244
- package/src/python/import-resolver.ts +0 -136
- package/src/python/migration-indexer.ts +0 -116
- package/src/python/migration-parser.ts +0 -332
- package/src/python/model-indexer.ts +0 -71
- package/src/python/model-parser.ts +0 -279
- package/src/python/route-indexer.ts +0 -59
- package/src/python/route-parser.ts +0 -317
- package/src/python-tools.ts +0 -630
- package/src/regression-detector.ts +0 -319
- package/src/rules.ts +0 -57
- package/src/schema-mapper.ts +0 -232
- package/src/security/adapter-origin.ts +0 -130
- package/src/security/adapter-verifier.ts +0 -319
- package/src/security/atomic-write.ts +0 -164
- package/src/security/fetcher.ts +0 -200
- package/src/security/install-tracking.ts +0 -319
- package/src/security/license-pubkey.generated.ts +0 -30
- package/src/security/license-response-verifier.ts +0 -149
- package/src/security/local-fingerprint.ts +0 -225
- package/src/security/manifest-cache.ts +0 -333
- package/src/security/manifest-schema.ts +0 -129
- package/src/security/registry-pubkey.generated.ts +0 -35
- package/src/security/telemetry.ts +0 -320
- package/src/security-scorer.ts +0 -405
- package/src/security-utils.ts +0 -133
- package/src/sentinel-db.ts +0 -580
- package/src/sentinel-scanner.ts +0 -406
- package/src/sentinel-tools.ts +0 -513
- package/src/sentinel-types.ts +0 -140
- package/src/server-dispatch.ts +0 -225
- package/src/server.ts +0 -107
- package/src/session-archiver.ts +0 -111
- package/src/session-state-generator.ts +0 -174
- package/src/team-knowledge.ts +0 -407
- package/src/tool-db-needs.ts +0 -232
- package/src/tools.ts +0 -1206
- package/src/transcript-parser.ts +0 -458
- package/src/trpc-index.ts +0 -215
- package/src/validate-features-runner.ts +0 -107
- package/src/validation-engine.ts +0 -358
- package/src/watch/daemon.ts +0 -385
- package/src/watch/lockfile-detector.ts +0 -65
- package/src/watch/paths.ts +0 -279
- 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
|
-
|
|
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
|
-
|
|
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:
|
|
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-
|
|
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:
|
|
13679
|
-
{ type: "command", command: hookCmd(version, "quality-event"), timeout:
|
|
13680
|
-
{ type: "command", command: hookCmd(version, "cost-tracker"), timeout:
|
|
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:
|
|
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:
|
|
13690
|
-
{ type: "command", command: hookCmd(version, "classify-failure"), timeout:
|
|
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:
|
|
13699
|
-
{ type: "command", command: hookCmd(version, "rule-enforcement-pipeline"), timeout:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
13723
|
-
{ type: "command", command: hookCmd(version, "intent-suggester"), timeout:
|
|
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
|
-
|
|
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
|
|
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}${
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
18031
|
-
|
|
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
|
|
19058
|
-
import { resolve as resolve25, dirname as
|
|
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(
|
|
19407
|
+
basePath = resolve25(dirname15(fromFile), specifier);
|
|
19117
19408
|
}
|
|
19118
|
-
if (
|
|
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 (
|
|
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 (
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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 (
|
|
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 (
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
|
19976
|
-
import { resolve as resolve29, join as join16, relative as relative6, dirname as
|
|
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 =
|
|
20271
|
+
let baseDir = dirname16(resolve29(projectRoot, fromFile));
|
|
19981
20272
|
for (let i = 1; i < level; i++) {
|
|
19982
|
-
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 (
|
|
20287
|
+
if (existsSync33(basePath + ".py")) {
|
|
19997
20288
|
return relative6(projectRoot, basePath + ".py");
|
|
19998
20289
|
}
|
|
19999
|
-
if (
|
|
20290
|
+
if (existsSync33(join16(basePath, "__init__.py"))) {
|
|
20000
20291
|
return relative6(projectRoot, join16(basePath, "__init__.py"));
|
|
20001
20292
|
}
|
|
20002
|
-
if (basePath.endsWith(".py") &&
|
|
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
|
|
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 (!
|
|
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 (!
|
|
21776
|
+
if (!existsSync34(absPath)) {
|
|
21486
21777
|
const altPath = ensureWithinRoot(resolve30(getResolvedPaths().srcDir, "../server/api/routers", basename6(routerPath)), root);
|
|
21487
|
-
if (!
|
|
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 (!
|
|
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 (
|
|
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 =
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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("/") && !
|
|
22508
|
+
if (page.page_route.startsWith("/") && !existsSync35(absPath)) {
|
|
22218
22509
|
const altPath = resolve31(PROJECT_ROOT, `src/app${page.page_route}/page.tsx`);
|
|
22219
|
-
if (!
|
|
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
|
|
22746
|
-
import { resolve as resolve32, join as join21, basename as basename7, dirname as
|
|
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 (
|
|
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 (!
|
|
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(
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
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 (
|
|
25984
|
+
if (existsSync40(paths.plansDir)) {
|
|
25684
25985
|
const planFiles = discoverMarkdownFiles(paths.plansDir);
|
|
25685
25986
|
files.push(...planFiles);
|
|
25686
25987
|
}
|
|
25687
|
-
if (
|
|
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 (!
|
|
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 (
|
|
26167
|
+
if (existsSync40(paths.plansDir)) {
|
|
25852
26168
|
files.push(...discoverMarkdownFiles(paths.plansDir));
|
|
25853
26169
|
}
|
|
25854
|
-
if (
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|