@massu/core 1.10.8 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1303 -0
- package/dist/adapter.js +6 -4
- package/dist/cli.js +517 -397
- 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 +66 -18
- package/dist/hooks/pre-compact.js +27 -2
- package/dist/hooks/pre-delete-check.js +48 -22
- 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 +24 -0
- package/dist/hooks/session-start.js +8710 -8460
- package/dist/hooks/user-prompt.js +24 -0
- 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,
|
|
@@ -1688,10 +1720,12 @@ function scoreFailureClasses(db, matchText, filePath, promptContext, weights) {
|
|
|
1688
1720
|
}
|
|
1689
1721
|
return bestMatch;
|
|
1690
1722
|
}
|
|
1723
|
+
var TOOL_COST_EVENTS_RETENTION_DAYS;
|
|
1691
1724
|
var init_memory_db = __esm({
|
|
1692
1725
|
"src/memory-db.ts"() {
|
|
1693
1726
|
"use strict";
|
|
1694
1727
|
init_config();
|
|
1728
|
+
TOOL_COST_EVENTS_RETENTION_DAYS = 90;
|
|
1695
1729
|
}
|
|
1696
1730
|
});
|
|
1697
1731
|
|
|
@@ -2658,6 +2692,40 @@ var init_install_commands = __esm({
|
|
|
2658
2692
|
}
|
|
2659
2693
|
});
|
|
2660
2694
|
|
|
2695
|
+
// src/lib/hook-timeouts.ts
|
|
2696
|
+
var HOOK_TIMEOUTS;
|
|
2697
|
+
var init_hook_timeouts = __esm({
|
|
2698
|
+
"src/lib/hook-timeouts.ts"() {
|
|
2699
|
+
"use strict";
|
|
2700
|
+
HOOK_TIMEOUTS = {
|
|
2701
|
+
// Session lifecycle
|
|
2702
|
+
"session-start": 10,
|
|
2703
|
+
"session-end": 15,
|
|
2704
|
+
"pre-compact": 10,
|
|
2705
|
+
// PreToolUse hooks
|
|
2706
|
+
// P-E-019: `pre-tool-use-gate` is the consolidated PreToolUse hook
|
|
2707
|
+
// (combines security-gate + pre-delete-check into one spawn). The
|
|
2708
|
+
// individual entries below are kept for back-compat with operator
|
|
2709
|
+
// settings.json files that still reference them directly.
|
|
2710
|
+
"pre-tool-use-gate": 5,
|
|
2711
|
+
"security-gate": 5,
|
|
2712
|
+
"pre-delete-check": 5,
|
|
2713
|
+
// PostToolUse hooks
|
|
2714
|
+
"post-tool-use": 10,
|
|
2715
|
+
"quality-event": 5,
|
|
2716
|
+
"cost-tracker": 5,
|
|
2717
|
+
"post-edit-context": 5,
|
|
2718
|
+
// Failure-handling hooks
|
|
2719
|
+
"fix-detector": 5,
|
|
2720
|
+
"classify-failure": 5,
|
|
2721
|
+
"incident-pipeline": 5,
|
|
2722
|
+
"rule-enforcement-pipeline": 5,
|
|
2723
|
+
// Auto-learning
|
|
2724
|
+
"auto-learning-pipeline": 10
|
|
2725
|
+
};
|
|
2726
|
+
}
|
|
2727
|
+
});
|
|
2728
|
+
|
|
2661
2729
|
// src/detect/manifest-registry.ts
|
|
2662
2730
|
var manifest_registry_exports = {};
|
|
2663
2731
|
__export(manifest_registry_exports, {
|
|
@@ -10567,8 +10635,9 @@ function evictBeyondRetainCount(retain = getCacheRetainCount()) {
|
|
|
10567
10635
|
continue;
|
|
10568
10636
|
}
|
|
10569
10637
|
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)
|
|
10638
|
+
process.stderr.write(
|
|
10639
|
+
`[tree-sitter-loader] cache eviction skipped non-regular file: ${path} (possible symlink attack \u2014 see Phase 3.5 finding F-008).
|
|
10640
|
+
`
|
|
10572
10641
|
);
|
|
10573
10642
|
continue;
|
|
10574
10643
|
}
|
|
@@ -10681,8 +10750,9 @@ async function loadGrammar(language, options = {}) {
|
|
|
10681
10750
|
}
|
|
10682
10751
|
evictBeyondRetainCount();
|
|
10683
10752
|
} catch (e2) {
|
|
10684
|
-
|
|
10685
|
-
`[tree-sitter-loader] cache write failed for ${language}: ${e2 instanceof Error ? e2.message : String(e2)} \u2014 loading directly from memory
|
|
10753
|
+
process.stderr.write(
|
|
10754
|
+
`[tree-sitter-loader] cache write failed for ${language}: ${e2 instanceof Error ? e2.message : String(e2)} \u2014 loading directly from memory.
|
|
10755
|
+
`
|
|
10686
10756
|
);
|
|
10687
10757
|
}
|
|
10688
10758
|
const lang = await Language.load(body);
|
|
@@ -11509,14 +11579,6 @@ var init_python_flask = __esm({
|
|
|
11509
11579
|
}
|
|
11510
11580
|
});
|
|
11511
11581
|
|
|
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
11582
|
// src/detect/adapters/rails.ts
|
|
11521
11583
|
import { railsAdapter } from "@massu/adapter-rails";
|
|
11522
11584
|
var init_rails = __esm({
|
|
@@ -11525,22 +11587,6 @@ var init_rails = __esm({
|
|
|
11525
11587
|
}
|
|
11526
11588
|
});
|
|
11527
11589
|
|
|
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
11590
|
// src/detect/adapters/spring.ts
|
|
11545
11591
|
import { springAdapter } from "@massu/adapter-spring";
|
|
11546
11592
|
var init_spring = __esm({
|
|
@@ -11773,10 +11819,7 @@ var init_codebase_introspector = __esm({
|
|
|
11773
11819
|
init_nextjs_trpc();
|
|
11774
11820
|
init_swift_swiftui();
|
|
11775
11821
|
init_python_flask();
|
|
11776
|
-
init_go_chi();
|
|
11777
11822
|
init_rails();
|
|
11778
|
-
init_phoenix();
|
|
11779
|
-
init_aspnet();
|
|
11780
11823
|
init_spring();
|
|
11781
11824
|
FIRST_PARTY_ADAPTERS = [
|
|
11782
11825
|
pythonFastApiAdapter,
|
|
@@ -11784,10 +11827,7 @@ var init_codebase_introspector = __esm({
|
|
|
11784
11827
|
pythonFlaskAdapter,
|
|
11785
11828
|
nextjsTrpcAdapter,
|
|
11786
11829
|
swiftSwiftUiAdapter,
|
|
11787
|
-
goChiAdapter,
|
|
11788
11830
|
railsAdapter,
|
|
11789
|
-
phoenixAdapter,
|
|
11790
|
-
aspnetAdapter,
|
|
11791
11831
|
springAdapter
|
|
11792
11832
|
];
|
|
11793
11833
|
}
|
|
@@ -13648,46 +13688,50 @@ function resolveHooksDir() {
|
|
|
13648
13688
|
function hookCmd(version, hookName) {
|
|
13649
13689
|
return `npx -y @massu/core@${version} hook-runner ${hookName}`;
|
|
13650
13690
|
}
|
|
13691
|
+
function getHookTimeout(name) {
|
|
13692
|
+
return HOOK_TIMEOUTS[name] ?? 5;
|
|
13693
|
+
}
|
|
13651
13694
|
function buildHooksConfig(_hooksDir) {
|
|
13652
13695
|
const version = getInstallerVersion();
|
|
13653
13696
|
return {
|
|
13654
13697
|
SessionStart: [
|
|
13655
13698
|
{
|
|
13656
13699
|
hooks: [
|
|
13657
|
-
{ type: "command", command: hookCmd(version, "session-start"), timeout:
|
|
13700
|
+
{ type: "command", command: hookCmd(version, "session-start"), timeout: getHookTimeout("session-start") }
|
|
13658
13701
|
]
|
|
13659
13702
|
}
|
|
13660
13703
|
],
|
|
13661
13704
|
PreToolUse: [
|
|
13705
|
+
// P-E-019 (plan-stage-e-low-info-sweep): consolidated PreToolUse
|
|
13706
|
+
// gate. Single hook spawn covers BOTH security-gate (Bash
|
|
13707
|
+
// dangerous-pattern + Write/Edit protected-path + Python
|
|
13708
|
+
// dangerous-code) AND pre-delete-check (sentinel feature impact +
|
|
13709
|
+
// Python import graph). Cuts ~200ms of cold-start spawn overhead
|
|
13710
|
+
// per tool call. Matcher includes both Bash + Write so the single
|
|
13711
|
+
// hook fires once for every operation either check cares about.
|
|
13662
13712
|
{
|
|
13663
|
-
matcher: "Bash",
|
|
13664
|
-
hooks: [
|
|
13665
|
-
{ type: "command", command: hookCmd(version, "security-gate"), timeout: 5 }
|
|
13666
|
-
]
|
|
13667
|
-
},
|
|
13668
|
-
{
|
|
13669
|
-
matcher: "Bash|Write",
|
|
13713
|
+
matcher: "Bash|Write|Edit",
|
|
13670
13714
|
hooks: [
|
|
13671
|
-
{ type: "command", command: hookCmd(version, "pre-
|
|
13715
|
+
{ type: "command", command: hookCmd(version, "pre-tool-use-gate"), timeout: getHookTimeout("pre-tool-use-gate") }
|
|
13672
13716
|
]
|
|
13673
13717
|
}
|
|
13674
13718
|
],
|
|
13675
13719
|
PostToolUse: [
|
|
13676
13720
|
{
|
|
13677
13721
|
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:
|
|
13722
|
+
{ type: "command", command: hookCmd(version, "post-tool-use"), timeout: getHookTimeout("post-tool-use") },
|
|
13723
|
+
{ type: "command", command: hookCmd(version, "quality-event"), timeout: getHookTimeout("quality-event") },
|
|
13724
|
+
{ type: "command", command: hookCmd(version, "cost-tracker"), timeout: getHookTimeout("cost-tracker") }
|
|
13681
13725
|
]
|
|
13682
13726
|
},
|
|
13683
13727
|
{
|
|
13684
13728
|
matcher: "Edit|Write",
|
|
13685
13729
|
hooks: [
|
|
13686
|
-
{ type: "command", command: hookCmd(version, "post-edit-context"), timeout:
|
|
13730
|
+
{ type: "command", command: hookCmd(version, "post-edit-context"), timeout: getHookTimeout("post-edit-context") },
|
|
13687
13731
|
// Auto-learning pipeline — classifies failures and detects fixes on
|
|
13688
13732
|
// 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:
|
|
13733
|
+
{ type: "command", command: hookCmd(version, "fix-detector"), timeout: getHookTimeout("fix-detector") },
|
|
13734
|
+
{ type: "command", command: hookCmd(version, "classify-failure"), timeout: getHookTimeout("classify-failure") }
|
|
13691
13735
|
]
|
|
13692
13736
|
},
|
|
13693
13737
|
{
|
|
@@ -13695,32 +13739,32 @@ function buildHooksConfig(_hooksDir) {
|
|
|
13695
13739
|
hooks: [
|
|
13696
13740
|
// Incident + rule enforcement pipelines fire on Write-only (incidents
|
|
13697
13741
|
// 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:
|
|
13742
|
+
{ type: "command", command: hookCmd(version, "incident-pipeline"), timeout: getHookTimeout("incident-pipeline") },
|
|
13743
|
+
{ type: "command", command: hookCmd(version, "rule-enforcement-pipeline"), timeout: getHookTimeout("rule-enforcement-pipeline") }
|
|
13700
13744
|
]
|
|
13701
13745
|
}
|
|
13702
13746
|
],
|
|
13703
13747
|
Stop: [
|
|
13704
13748
|
{
|
|
13705
13749
|
hooks: [
|
|
13706
|
-
{ type: "command", command: hookCmd(version, "session-end"), timeout:
|
|
13750
|
+
{ type: "command", command: hookCmd(version, "session-end"), timeout: getHookTimeout("session-end") },
|
|
13707
13751
|
// Session-end auto-learning aggregation (failure-class roll-up).
|
|
13708
|
-
{ type: "command", command: hookCmd(version, "auto-learning-pipeline"), timeout:
|
|
13752
|
+
{ type: "command", command: hookCmd(version, "auto-learning-pipeline"), timeout: getHookTimeout("auto-learning-pipeline") }
|
|
13709
13753
|
]
|
|
13710
13754
|
}
|
|
13711
13755
|
],
|
|
13712
13756
|
PreCompact: [
|
|
13713
13757
|
{
|
|
13714
13758
|
hooks: [
|
|
13715
|
-
{ type: "command", command: hookCmd(version, "pre-compact"), timeout:
|
|
13759
|
+
{ type: "command", command: hookCmd(version, "pre-compact"), timeout: getHookTimeout("pre-compact") }
|
|
13716
13760
|
]
|
|
13717
13761
|
}
|
|
13718
13762
|
],
|
|
13719
13763
|
UserPromptSubmit: [
|
|
13720
13764
|
{
|
|
13721
13765
|
hooks: [
|
|
13722
|
-
{ type: "command", command: hookCmd(version, "user-prompt"), timeout:
|
|
13723
|
-
{ type: "command", command: hookCmd(version, "intent-suggester"), timeout:
|
|
13766
|
+
{ type: "command", command: hookCmd(version, "user-prompt"), timeout: getHookTimeout("user-prompt") },
|
|
13767
|
+
{ type: "command", command: hookCmd(version, "intent-suggester"), timeout: getHookTimeout("intent-suggester") }
|
|
13724
13768
|
]
|
|
13725
13769
|
}
|
|
13726
13770
|
]
|
|
@@ -13905,7 +13949,15 @@ function summarizeDetection2(detection) {
|
|
|
13905
13949
|
const dirs = detection.sourceDirs[lang]?.source_dirs ?? [];
|
|
13906
13950
|
const dirSuffix = dirs.length > 0 ? ` in ${dirs.join(",")}` : "";
|
|
13907
13951
|
const fwName = fw?.framework ?? "no-framework";
|
|
13908
|
-
|
|
13952
|
+
const slotParts = [];
|
|
13953
|
+
const router = fw?.router;
|
|
13954
|
+
const orm = fw?.orm;
|
|
13955
|
+
const ui = fw?.ui;
|
|
13956
|
+
if (router && router !== "none") slotParts.push(`router=${router}`);
|
|
13957
|
+
if (orm && orm !== "none") slotParts.push(`orm=${orm}`);
|
|
13958
|
+
if (ui && ui !== "none") slotParts.push(`ui=${ui}`);
|
|
13959
|
+
const slotSuffix = slotParts.length > 0 ? ` (${slotParts.join(", ")})` : "";
|
|
13960
|
+
parts.push(`${capitalize(lang)}/${fwName}${slotSuffix}${dirSuffix}`);
|
|
13909
13961
|
}
|
|
13910
13962
|
const mono = detection.monorepo.type;
|
|
13911
13963
|
const monoSuffix = mono && mono !== "single" ? ` [${mono} monorepo]` : "";
|
|
@@ -14178,6 +14230,7 @@ var init_init = __esm({
|
|
|
14178
14230
|
init_install_commands();
|
|
14179
14231
|
init_settings_local();
|
|
14180
14232
|
init_memory_path();
|
|
14233
|
+
init_hook_timeouts();
|
|
14181
14234
|
init_detect();
|
|
14182
14235
|
init_drift();
|
|
14183
14236
|
__filename2 = fileURLToPath2(import.meta.url);
|
|
@@ -14216,7 +14269,7 @@ var init_license_pubkey_generated = __esm({
|
|
|
14216
14269
|
});
|
|
14217
14270
|
|
|
14218
14271
|
// src/security/license-response-verifier.ts
|
|
14219
|
-
import { createPublicKey } from "crypto";
|
|
14272
|
+
import { createPublicKey, verify as cryptoVerify } from "crypto";
|
|
14220
14273
|
function verifyLicenseResponse(payload) {
|
|
14221
14274
|
if (!KNOWN_LICENSE_PUBKEY_FINGERPRINTS.has(LICENSE_PUBKEY_FINGERPRINT_HEX)) {
|
|
14222
14275
|
return {
|
|
@@ -14263,8 +14316,7 @@ function verifyLicenseResponse(payload) {
|
|
|
14263
14316
|
]);
|
|
14264
14317
|
const der = Buffer.concat([spkiPrefix, Buffer.from(LICENSE_PUBKEY_ED25519)]);
|
|
14265
14318
|
const pubkey = createPublicKey({ key: der, format: "der", type: "spki" });
|
|
14266
|
-
const
|
|
14267
|
-
const ok = verify(
|
|
14319
|
+
const ok = cryptoVerify(
|
|
14268
14320
|
null,
|
|
14269
14321
|
Buffer.from(canonical, "utf-8"),
|
|
14270
14322
|
pubkey,
|
|
@@ -14311,13 +14363,31 @@ function isToolAllowed(toolName, userTier) {
|
|
|
14311
14363
|
return tierLevel(userTier) >= tierLevel(requiredTier);
|
|
14312
14364
|
}
|
|
14313
14365
|
function annotateToolDefinitions(defs) {
|
|
14366
|
+
for (const def of defs) {
|
|
14367
|
+
const expectedTier = getToolTier(def.name);
|
|
14368
|
+
const incomingTier = def.annotations?.tier;
|
|
14369
|
+
if (incomingTier !== void 0 && incomingTier !== expectedTier) {
|
|
14370
|
+
throw new Error(
|
|
14371
|
+
`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.`
|
|
14372
|
+
);
|
|
14373
|
+
}
|
|
14374
|
+
const expectedPrefix = TIER_LABELS[expectedTier];
|
|
14375
|
+
for (const otherPrefix of Object.values(TIER_LABELS)) {
|
|
14376
|
+
if (otherPrefix && otherPrefix !== expectedPrefix && def.description.startsWith(otherPrefix)) {
|
|
14377
|
+
throw new Error(
|
|
14378
|
+
`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}").`
|
|
14379
|
+
);
|
|
14380
|
+
}
|
|
14381
|
+
}
|
|
14382
|
+
}
|
|
14314
14383
|
return defs.map((def) => {
|
|
14315
14384
|
const tier = getToolTier(def.name);
|
|
14316
14385
|
const label = TIER_LABELS[tier];
|
|
14386
|
+
const stripped = label && def.description.startsWith(label) ? def.description.slice(label.length) : def.description;
|
|
14317
14387
|
return {
|
|
14318
14388
|
...def,
|
|
14319
14389
|
annotations: { ...def.annotations ?? {}, tier },
|
|
14320
|
-
description: label ? `${label}${
|
|
14390
|
+
description: label ? `${label}${stripped}` : stripped
|
|
14321
14391
|
};
|
|
14322
14392
|
});
|
|
14323
14393
|
}
|
|
@@ -14329,17 +14399,14 @@ async function validateLicense(apiKey) {
|
|
|
14329
14399
|
const memDb = getMemoryDb();
|
|
14330
14400
|
try {
|
|
14331
14401
|
const cached = memDb.prepare(
|
|
14332
|
-
"SELECT tier, valid_until, last_validated, features FROM license_cache WHERE api_key_hash = ?"
|
|
14402
|
+
"SELECT tier, valid_until, last_validated, features, signed_payload_json FROM license_cache WHERE api_key_hash = ?"
|
|
14333
14403
|
).get(keyHash);
|
|
14334
|
-
|
|
14404
|
+
const trusted = cached ? readTrustedCache(cached) : null;
|
|
14405
|
+
if (cached && trusted) {
|
|
14335
14406
|
const lastValidated = new Date(cached.last_validated);
|
|
14336
14407
|
const hourAgo = new Date(Date.now() - 60 * 60 * 1e3);
|
|
14337
14408
|
if (lastValidated > hourAgo) {
|
|
14338
|
-
return
|
|
14339
|
-
tier: cached.tier,
|
|
14340
|
-
validUntil: cached.valid_until,
|
|
14341
|
-
features: JSON.parse(cached.features || "[]")
|
|
14342
|
-
};
|
|
14409
|
+
return trusted;
|
|
14343
14410
|
}
|
|
14344
14411
|
}
|
|
14345
14412
|
const config = getConfig();
|
|
@@ -14368,7 +14435,13 @@ async function validateLicense(apiKey) {
|
|
|
14368
14435
|
const tier = data.plan ? PLAN_TO_TIER_MAP[data.plan] ?? "free" : data.tier ?? "free";
|
|
14369
14436
|
const validUntil = data.validUntil ?? "";
|
|
14370
14437
|
const features = data.features ?? [];
|
|
14371
|
-
updateLicenseCache(
|
|
14438
|
+
updateLicenseCache(
|
|
14439
|
+
apiKey,
|
|
14440
|
+
tier,
|
|
14441
|
+
validUntil,
|
|
14442
|
+
features,
|
|
14443
|
+
sigResult.kind === "valid" ? data : null
|
|
14444
|
+
);
|
|
14372
14445
|
return { tier, validUntil, features };
|
|
14373
14446
|
}
|
|
14374
14447
|
return { tier: "free", validUntil: "", features: [] };
|
|
@@ -14376,15 +14449,11 @@ async function validateLicense(apiKey) {
|
|
|
14376
14449
|
} catch {
|
|
14377
14450
|
}
|
|
14378
14451
|
}
|
|
14379
|
-
if (cached) {
|
|
14452
|
+
if (cached && trusted) {
|
|
14380
14453
|
const lastValidated = new Date(cached.last_validated);
|
|
14381
14454
|
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3);
|
|
14382
14455
|
if (lastValidated > sevenDaysAgo) {
|
|
14383
|
-
return
|
|
14384
|
-
tier: cached.tier,
|
|
14385
|
-
validUntil: cached.valid_until,
|
|
14386
|
-
features: JSON.parse(cached.features || "[]")
|
|
14387
|
-
};
|
|
14456
|
+
return trusted;
|
|
14388
14457
|
}
|
|
14389
14458
|
}
|
|
14390
14459
|
return { tier: "free", validUntil: "", features: [] };
|
|
@@ -14392,18 +14461,49 @@ async function validateLicense(apiKey) {
|
|
|
14392
14461
|
memDb.close();
|
|
14393
14462
|
}
|
|
14394
14463
|
}
|
|
14395
|
-
function updateLicenseCache(apiKey, tier, validUntil, features = []) {
|
|
14464
|
+
function updateLicenseCache(apiKey, tier, validUntil, features = [], signedPayload = null) {
|
|
14396
14465
|
const keyHash = createHash5("sha256").update(apiKey).digest("hex");
|
|
14397
14466
|
const memDb = getMemoryDb();
|
|
14398
14467
|
try {
|
|
14399
14468
|
memDb.prepare(`
|
|
14400
|
-
INSERT OR REPLACE INTO license_cache (api_key_hash, tier, valid_until, last_validated, features)
|
|
14401
|
-
VALUES (?, ?, ?, datetime('now'), ?)
|
|
14402
|
-
`).run(
|
|
14469
|
+
INSERT OR REPLACE INTO license_cache (api_key_hash, tier, valid_until, last_validated, features, signed_payload_json)
|
|
14470
|
+
VALUES (?, ?, ?, datetime('now'), ?, ?)
|
|
14471
|
+
`).run(
|
|
14472
|
+
keyHash,
|
|
14473
|
+
tier,
|
|
14474
|
+
validUntil,
|
|
14475
|
+
JSON.stringify(features),
|
|
14476
|
+
signedPayload ? JSON.stringify(signedPayload) : ""
|
|
14477
|
+
);
|
|
14403
14478
|
} finally {
|
|
14404
14479
|
memDb.close();
|
|
14405
14480
|
}
|
|
14406
14481
|
}
|
|
14482
|
+
function readTrustedCache(cached) {
|
|
14483
|
+
if (!cached.signed_payload_json) {
|
|
14484
|
+
if (isLicenseSignatureRequired()) return null;
|
|
14485
|
+
warnLicenseSigOnce("cache_unsigned_transition");
|
|
14486
|
+
return {
|
|
14487
|
+
tier: cached.tier,
|
|
14488
|
+
validUntil: cached.valid_until,
|
|
14489
|
+
features: JSON.parse(cached.features || "[]")
|
|
14490
|
+
};
|
|
14491
|
+
}
|
|
14492
|
+
let parsed;
|
|
14493
|
+
try {
|
|
14494
|
+
parsed = JSON.parse(cached.signed_payload_json);
|
|
14495
|
+
} catch {
|
|
14496
|
+
return null;
|
|
14497
|
+
}
|
|
14498
|
+
const result = verifyLicenseResponse(parsed);
|
|
14499
|
+
if (result.kind !== "valid") return null;
|
|
14500
|
+
const verifiedPlan = typeof parsed.plan === "string" ? parsed.plan : null;
|
|
14501
|
+
const verifiedTierField = typeof parsed.tier === "string" ? parsed.tier : null;
|
|
14502
|
+
const tier = verifiedPlan ? PLAN_TO_TIER_MAP[verifiedPlan] ?? "free" : verifiedTierField ?? "free";
|
|
14503
|
+
const validUntil = typeof parsed.validUntil === "string" ? parsed.validUntil : "";
|
|
14504
|
+
const features = Array.isArray(parsed.features) ? parsed.features : [];
|
|
14505
|
+
return { tier, validUntil, features };
|
|
14506
|
+
}
|
|
14407
14507
|
async function getCurrentTier() {
|
|
14408
14508
|
if (cachedTier && Date.now() - cachedTierTimestamp < IN_MEMORY_CACHE_TTL_MS) {
|
|
14409
14509
|
return cachedTier.tier;
|
|
@@ -14511,6 +14611,12 @@ var init_license = __esm({
|
|
|
14511
14611
|
coupling_check: "free",
|
|
14512
14612
|
memory_search: "free",
|
|
14513
14613
|
memory_ingest: "free",
|
|
14614
|
+
// P-M-042 (plan-stage-d-medium-sweep): memory_backfill serves the
|
|
14615
|
+
// customer's MEMORY.md ingestion workflow and was registered in
|
|
14616
|
+
// memory-tools.ts but missing from TOOL_TIER_MAP. The bijection
|
|
14617
|
+
// drift-guard (P-M-033) would have caught it on next add; we land it
|
|
14618
|
+
// explicitly as Free since it's a customer-owned-data ingestion path.
|
|
14619
|
+
memory_backfill: "free",
|
|
14514
14620
|
regression_risk: "free",
|
|
14515
14621
|
feature_health: "free",
|
|
14516
14622
|
license_status: "free",
|
|
@@ -14586,7 +14692,7 @@ var init_license = __esm({
|
|
|
14586
14692
|
cloud_enterprise: "enterprise"
|
|
14587
14693
|
};
|
|
14588
14694
|
TIER_LABELS = {
|
|
14589
|
-
free: "",
|
|
14695
|
+
free: "[FREE] ",
|
|
14590
14696
|
pro: "[PRO] ",
|
|
14591
14697
|
team: "[TEAM] ",
|
|
14592
14698
|
enterprise: "[ENTERPRISE] "
|
|
@@ -14615,7 +14721,12 @@ var init_hook_registry = __esm({
|
|
|
14615
14721
|
"post-edit-context",
|
|
14616
14722
|
"post-tool-use",
|
|
14617
14723
|
"pre-compact",
|
|
14724
|
+
// P-E-019 (1.12.0): security-gate + pre-delete-check are kept registered
|
|
14725
|
+
// for back-compat (operator settings.json may still reference them
|
|
14726
|
+
// individually), but new installs emit only `pre-tool-use-gate` —
|
|
14727
|
+
// the consolidated single-spawn PreToolUse hook.
|
|
14618
14728
|
"pre-delete-check",
|
|
14729
|
+
"pre-tool-use-gate",
|
|
14619
14730
|
"quality-event",
|
|
14620
14731
|
"rule-enforcement-pipeline",
|
|
14621
14732
|
"security-gate",
|
|
@@ -18027,10 +18138,11 @@ var init_adapters = __esm({
|
|
|
18027
18138
|
"src/detect/adapters/index.ts"() {
|
|
18028
18139
|
"use strict";
|
|
18029
18140
|
CORE_BUNDLED_IDS = /* @__PURE__ */ new Set([
|
|
18030
|
-
|
|
18031
|
-
|
|
18141
|
+
// P-M-032 (plan-stage-d-medium-sweep): aspnet, go-chi, phoenix REMOVED
|
|
18142
|
+
// — zero downstream consumers (each had a single 1-line re-export shim).
|
|
18143
|
+
// Rails (5 consumers) + Spring (1 consumer) deferred to Stage E as the
|
|
18144
|
+
// JVM/Ruby ecosystem-watch pair per operator decision.
|
|
18032
18145
|
"nextjs-trpc",
|
|
18033
|
-
"phoenix",
|
|
18034
18146
|
"python-django",
|
|
18035
18147
|
"python-fastapi",
|
|
18036
18148
|
"python-flask",
|
|
@@ -18977,6 +19089,184 @@ var init_db = __esm({
|
|
|
18977
19089
|
}
|
|
18978
19090
|
});
|
|
18979
19091
|
|
|
19092
|
+
// src/knowledge-db.ts
|
|
19093
|
+
import Database3 from "better-sqlite3";
|
|
19094
|
+
import { dirname as dirname14 } from "path";
|
|
19095
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync11 } from "fs";
|
|
19096
|
+
function getKnowledgeDb() {
|
|
19097
|
+
const dbPath = getResolvedPaths().knowledgeDbPath;
|
|
19098
|
+
const dir = dirname14(dbPath);
|
|
19099
|
+
if (!existsSync28(dir)) {
|
|
19100
|
+
mkdirSync11(dir, { recursive: true });
|
|
19101
|
+
}
|
|
19102
|
+
const db = new Database3(dbPath);
|
|
19103
|
+
db.pragma("journal_mode = WAL");
|
|
19104
|
+
db.pragma("foreign_keys = ON");
|
|
19105
|
+
initKnowledgeSchema(db);
|
|
19106
|
+
return db;
|
|
19107
|
+
}
|
|
19108
|
+
function initKnowledgeSchema(db) {
|
|
19109
|
+
db.exec(`
|
|
19110
|
+
-- Core document chunks (parsed from .claude/**/*.md)
|
|
19111
|
+
CREATE TABLE IF NOT EXISTS knowledge_documents (
|
|
19112
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
19113
|
+
file_path TEXT NOT NULL,
|
|
19114
|
+
category TEXT NOT NULL,
|
|
19115
|
+
title TEXT NOT NULL,
|
|
19116
|
+
description TEXT,
|
|
19117
|
+
content_hash TEXT NOT NULL,
|
|
19118
|
+
indexed_at TEXT NOT NULL,
|
|
19119
|
+
indexed_at_epoch INTEGER NOT NULL
|
|
19120
|
+
);
|
|
19121
|
+
|
|
19122
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_kd_filepath ON knowledge_documents(file_path);
|
|
19123
|
+
CREATE INDEX IF NOT EXISTS idx_kd_category ON knowledge_documents(category);
|
|
19124
|
+
|
|
19125
|
+
-- Structured chunks within documents
|
|
19126
|
+
CREATE TABLE IF NOT EXISTS knowledge_chunks (
|
|
19127
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
19128
|
+
document_id INTEGER NOT NULL REFERENCES knowledge_documents(id) ON DELETE CASCADE,
|
|
19129
|
+
chunk_type TEXT NOT NULL CHECK(chunk_type IN (
|
|
19130
|
+
'section', 'table_row', 'code_block', 'rule', 'incident', 'pattern', 'command', 'mismatch'
|
|
19131
|
+
)),
|
|
19132
|
+
heading TEXT,
|
|
19133
|
+
content TEXT NOT NULL,
|
|
19134
|
+
line_start INTEGER,
|
|
19135
|
+
line_end INTEGER,
|
|
19136
|
+
metadata TEXT DEFAULT '{}'
|
|
19137
|
+
);
|
|
19138
|
+
|
|
19139
|
+
CREATE INDEX IF NOT EXISTS idx_kc_doc ON knowledge_chunks(document_id);
|
|
19140
|
+
CREATE INDEX IF NOT EXISTS idx_kc_type ON knowledge_chunks(chunk_type);
|
|
19141
|
+
CREATE INDEX IF NOT EXISTS idx_kc_heading ON knowledge_chunks(heading);
|
|
19142
|
+
|
|
19143
|
+
-- Canonical Rules index
|
|
19144
|
+
CREATE TABLE IF NOT EXISTS knowledge_rules (
|
|
19145
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
19146
|
+
rule_id TEXT UNIQUE NOT NULL,
|
|
19147
|
+
rule_text TEXT NOT NULL,
|
|
19148
|
+
vr_type TEXT,
|
|
19149
|
+
reference_path TEXT,
|
|
19150
|
+
severity TEXT DEFAULT 'HIGH',
|
|
19151
|
+
prevention_summary TEXT
|
|
19152
|
+
);
|
|
19153
|
+
|
|
19154
|
+
CREATE INDEX IF NOT EXISTS idx_kr_id ON knowledge_rules(rule_id);
|
|
19155
|
+
`);
|
|
19156
|
+
const rulesCols = db.prepare(`PRAGMA table_info(knowledge_rules)`).all();
|
|
19157
|
+
if (!rulesCols.some((c2) => c2.name === "source")) {
|
|
19158
|
+
db.exec(
|
|
19159
|
+
`ALTER TABLE knowledge_rules ADD COLUMN source TEXT NOT NULL DEFAULT 'framework'`
|
|
19160
|
+
);
|
|
19161
|
+
}
|
|
19162
|
+
db.exec(`
|
|
19163
|
+
|
|
19164
|
+
-- Verification Requirements index
|
|
19165
|
+
CREATE TABLE IF NOT EXISTS knowledge_verifications (
|
|
19166
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
19167
|
+
vr_type TEXT UNIQUE NOT NULL,
|
|
19168
|
+
command TEXT NOT NULL,
|
|
19169
|
+
expected TEXT NOT NULL,
|
|
19170
|
+
use_when TEXT NOT NULL,
|
|
19171
|
+
catches TEXT,
|
|
19172
|
+
category TEXT
|
|
19173
|
+
);
|
|
19174
|
+
|
|
19175
|
+
CREATE INDEX IF NOT EXISTS idx_kv_type ON knowledge_verifications(vr_type);
|
|
19176
|
+
CREATE INDEX IF NOT EXISTS idx_kv_category ON knowledge_verifications(category);
|
|
19177
|
+
|
|
19178
|
+
-- Incident index
|
|
19179
|
+
CREATE TABLE IF NOT EXISTS knowledge_incidents (
|
|
19180
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
19181
|
+
incident_num INTEGER UNIQUE NOT NULL,
|
|
19182
|
+
date TEXT NOT NULL,
|
|
19183
|
+
type TEXT NOT NULL,
|
|
19184
|
+
gap_found TEXT NOT NULL,
|
|
19185
|
+
prevention TEXT NOT NULL,
|
|
19186
|
+
cr_added TEXT,
|
|
19187
|
+
root_cause TEXT,
|
|
19188
|
+
user_quote TEXT
|
|
19189
|
+
);
|
|
19190
|
+
|
|
19191
|
+
CREATE INDEX IF NOT EXISTS idx_ki_num ON knowledge_incidents(incident_num);
|
|
19192
|
+
CREATE INDEX IF NOT EXISTS idx_ki_type ON knowledge_incidents(type);
|
|
19193
|
+
CREATE INDEX IF NOT EXISTS idx_ki_cr ON knowledge_incidents(cr_added);
|
|
19194
|
+
|
|
19195
|
+
-- Schema mismatch quick lookup
|
|
19196
|
+
-- P-H013 (plan-stage-c-high-batch): the source column no longer has a
|
|
19197
|
+
-- SQL DEFAULT. Previously the value was a JS template-literal
|
|
19198
|
+
-- interpolation baked into the customer SQLite at schema creation
|
|
19199
|
+
-- time, so later config changes were ignored. Default is now applied
|
|
19200
|
+
-- at INSERT time via getConfig().conventions.knowledgeSourceFiles[0]
|
|
19201
|
+
-- in knowledge-indexer.ts -- a runtime config lookup that reflects
|
|
19202
|
+
-- the customers current config.
|
|
19203
|
+
CREATE TABLE IF NOT EXISTS knowledge_schema_mismatches (
|
|
19204
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
19205
|
+
table_name TEXT NOT NULL,
|
|
19206
|
+
wrong_column TEXT NOT NULL,
|
|
19207
|
+
correct_column TEXT NOT NULL,
|
|
19208
|
+
source TEXT NOT NULL
|
|
19209
|
+
);
|
|
19210
|
+
|
|
19211
|
+
CREATE INDEX IF NOT EXISTS idx_ksm_table ON knowledge_schema_mismatches(table_name);
|
|
19212
|
+
|
|
19213
|
+
-- Cross-reference graph
|
|
19214
|
+
CREATE TABLE IF NOT EXISTS knowledge_edges (
|
|
19215
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
19216
|
+
source_type TEXT NOT NULL,
|
|
19217
|
+
source_id TEXT NOT NULL,
|
|
19218
|
+
target_type TEXT NOT NULL,
|
|
19219
|
+
target_id TEXT NOT NULL,
|
|
19220
|
+
edge_type TEXT NOT NULL
|
|
19221
|
+
);
|
|
19222
|
+
|
|
19223
|
+
CREATE INDEX IF NOT EXISTS idx_ke_source ON knowledge_edges(source_type, source_id);
|
|
19224
|
+
CREATE INDEX IF NOT EXISTS idx_ke_target ON knowledge_edges(target_type, target_id);
|
|
19225
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_ke_unique ON knowledge_edges(source_type, source_id, target_type, target_id, edge_type);
|
|
19226
|
+
|
|
19227
|
+
-- Staleness tracking
|
|
19228
|
+
CREATE TABLE IF NOT EXISTS knowledge_meta (
|
|
19229
|
+
key TEXT PRIMARY KEY,
|
|
19230
|
+
value TEXT NOT NULL
|
|
19231
|
+
);
|
|
19232
|
+
`);
|
|
19233
|
+
try {
|
|
19234
|
+
db.exec(`
|
|
19235
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS knowledge_fts USING fts5(
|
|
19236
|
+
heading, content, chunk_type, file_path
|
|
19237
|
+
);
|
|
19238
|
+
|
|
19239
|
+
CREATE TRIGGER IF NOT EXISTS kc_fts_insert AFTER INSERT ON knowledge_chunks BEGIN
|
|
19240
|
+
INSERT INTO knowledge_fts(rowid, heading, content, chunk_type, file_path)
|
|
19241
|
+
SELECT new.id, new.heading, new.content, new.chunk_type, kd.file_path
|
|
19242
|
+
FROM knowledge_documents kd WHERE kd.id = new.document_id;
|
|
19243
|
+
END;
|
|
19244
|
+
|
|
19245
|
+
CREATE TRIGGER IF NOT EXISTS kc_fts_delete AFTER DELETE ON knowledge_chunks BEGIN
|
|
19246
|
+
INSERT INTO knowledge_fts(knowledge_fts, rowid, heading, content, chunk_type, file_path)
|
|
19247
|
+
SELECT 'delete', old.id, old.heading, old.content, old.chunk_type, kd.file_path
|
|
19248
|
+
FROM knowledge_documents kd WHERE kd.id = old.document_id;
|
|
19249
|
+
END;
|
|
19250
|
+
|
|
19251
|
+
CREATE TRIGGER IF NOT EXISTS kc_fts_update AFTER UPDATE ON knowledge_chunks BEGIN
|
|
19252
|
+
INSERT INTO knowledge_fts(knowledge_fts, rowid, heading, content, chunk_type, file_path)
|
|
19253
|
+
SELECT 'delete', old.id, old.heading, old.content, old.chunk_type, kd.file_path
|
|
19254
|
+
FROM knowledge_documents kd WHERE kd.id = old.document_id;
|
|
19255
|
+
INSERT INTO knowledge_fts(rowid, heading, content, chunk_type, file_path)
|
|
19256
|
+
SELECT new.id, new.heading, new.content, new.chunk_type, kd.file_path
|
|
19257
|
+
FROM knowledge_documents kd WHERE kd.id = new.document_id;
|
|
19258
|
+
END;
|
|
19259
|
+
`);
|
|
19260
|
+
} catch {
|
|
19261
|
+
}
|
|
19262
|
+
}
|
|
19263
|
+
var init_knowledge_db = __esm({
|
|
19264
|
+
"src/knowledge-db.ts"() {
|
|
19265
|
+
"use strict";
|
|
19266
|
+
init_config();
|
|
19267
|
+
}
|
|
19268
|
+
});
|
|
19269
|
+
|
|
18980
19270
|
// src/security-utils.ts
|
|
18981
19271
|
import { resolve as resolve24, normalize } from "path";
|
|
18982
19272
|
function ensureWithinRoot(filePath, projectRoot) {
|
|
@@ -19054,8 +19344,8 @@ var init_rules = __esm({
|
|
|
19054
19344
|
});
|
|
19055
19345
|
|
|
19056
19346
|
// src/import-resolver.ts
|
|
19057
|
-
import { readFileSync as readFileSync25, existsSync as
|
|
19058
|
-
import { resolve as resolve25, dirname as
|
|
19347
|
+
import { readFileSync as readFileSync25, existsSync as existsSync29, statSync as statSync12 } from "fs";
|
|
19348
|
+
import { resolve as resolve25, dirname as dirname15, join as join13 } from "path";
|
|
19059
19349
|
function parseImports(source) {
|
|
19060
19350
|
const imports = [];
|
|
19061
19351
|
const lines = source.split("\n");
|
|
@@ -19113,21 +19403,21 @@ function resolveImportPath(specifier, fromFile) {
|
|
|
19113
19403
|
const paths = getResolvedPaths();
|
|
19114
19404
|
basePath = resolve25(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
|
|
19115
19405
|
} else {
|
|
19116
|
-
basePath = resolve25(
|
|
19406
|
+
basePath = resolve25(dirname15(fromFile), specifier);
|
|
19117
19407
|
}
|
|
19118
|
-
if (
|
|
19408
|
+
if (existsSync29(basePath) && !isDirectory(basePath)) {
|
|
19119
19409
|
return toRelative(basePath);
|
|
19120
19410
|
}
|
|
19121
19411
|
const resolvedPaths = getResolvedPaths();
|
|
19122
19412
|
for (const ext of resolvedPaths.extensions) {
|
|
19123
19413
|
const withExt = basePath + ext;
|
|
19124
|
-
if (
|
|
19414
|
+
if (existsSync29(withExt)) {
|
|
19125
19415
|
return toRelative(withExt);
|
|
19126
19416
|
}
|
|
19127
19417
|
}
|
|
19128
19418
|
for (const indexFile of resolvedPaths.indexFiles) {
|
|
19129
19419
|
const indexPath = join13(basePath, indexFile);
|
|
19130
|
-
if (
|
|
19420
|
+
if (existsSync29(indexPath)) {
|
|
19131
19421
|
return toRelative(indexPath);
|
|
19132
19422
|
}
|
|
19133
19423
|
}
|
|
@@ -19164,7 +19454,7 @@ function buildImportIndex(dataDb, codegraphDb) {
|
|
|
19164
19454
|
let batch = [];
|
|
19165
19455
|
for (const file of files) {
|
|
19166
19456
|
const absPath = ensureWithinRoot(resolve25(projectRoot, file.path), projectRoot);
|
|
19167
|
-
if (!
|
|
19457
|
+
if (!existsSync29(absPath)) continue;
|
|
19168
19458
|
let source;
|
|
19169
19459
|
try {
|
|
19170
19460
|
source = readFileSync25(absPath, "utf-8");
|
|
@@ -19204,12 +19494,12 @@ var init_import_resolver = __esm({
|
|
|
19204
19494
|
});
|
|
19205
19495
|
|
|
19206
19496
|
// src/trpc-index.ts
|
|
19207
|
-
import { readFileSync as readFileSync26, existsSync as
|
|
19497
|
+
import { readFileSync as readFileSync26, existsSync as existsSync30, readdirSync as readdirSync16 } from "fs";
|
|
19208
19498
|
import { resolve as resolve26, join as join14 } from "path";
|
|
19209
19499
|
function parseRootRouter() {
|
|
19210
19500
|
const paths = getResolvedPaths();
|
|
19211
19501
|
const rootPath = paths.rootRouterPath;
|
|
19212
|
-
if (!
|
|
19502
|
+
if (!existsSync30(rootPath)) {
|
|
19213
19503
|
throw new Error(`Root router not found at ${rootPath}`);
|
|
19214
19504
|
}
|
|
19215
19505
|
const source = readFileSync26(rootPath, "utf-8");
|
|
@@ -19224,12 +19514,12 @@ function parseRootRouter() {
|
|
|
19224
19514
|
for (const ext of [".ts", ".tsx", ""]) {
|
|
19225
19515
|
const candidate = fullPath + ext;
|
|
19226
19516
|
const routersRelPath = getConfig().paths.routers ?? "src/server/api/routers";
|
|
19227
|
-
if (
|
|
19517
|
+
if (existsSync30(candidate)) {
|
|
19228
19518
|
filePath = routersRelPath + "/" + filePath + ext;
|
|
19229
19519
|
break;
|
|
19230
19520
|
}
|
|
19231
19521
|
const indexCandidate = join14(fullPath, "index.ts");
|
|
19232
|
-
if (
|
|
19522
|
+
if (existsSync30(indexCandidate)) {
|
|
19233
19523
|
filePath = routersRelPath + "/" + filePath + "/index.ts";
|
|
19234
19524
|
break;
|
|
19235
19525
|
}
|
|
@@ -19249,7 +19539,7 @@ function parseRootRouter() {
|
|
|
19249
19539
|
}
|
|
19250
19540
|
function extractProcedures(routerFilePath) {
|
|
19251
19541
|
const absPath = resolve26(getProjectRoot(), routerFilePath);
|
|
19252
|
-
if (!
|
|
19542
|
+
if (!existsSync30(absPath)) return [];
|
|
19253
19543
|
const source = readFileSync26(absPath, "utf-8");
|
|
19254
19544
|
const procedures = [];
|
|
19255
19545
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -19279,7 +19569,7 @@ function findUICallSites(routerKey, procedureName) {
|
|
|
19279
19569
|
];
|
|
19280
19570
|
const searchPattern = `api.${routerKey}.${procedureName}`;
|
|
19281
19571
|
for (const dir of searchDirs) {
|
|
19282
|
-
if (!
|
|
19572
|
+
if (!existsSync30(dir)) continue;
|
|
19283
19573
|
searchDirectory(dir, searchPattern, callSites);
|
|
19284
19574
|
}
|
|
19285
19575
|
return callSites;
|
|
@@ -19357,7 +19647,7 @@ var init_trpc_index = __esm({
|
|
|
19357
19647
|
});
|
|
19358
19648
|
|
|
19359
19649
|
// src/page-deps.ts
|
|
19360
|
-
import { readFileSync as readFileSync27, existsSync as
|
|
19650
|
+
import { readFileSync as readFileSync27, existsSync as existsSync31 } from "fs";
|
|
19361
19651
|
import { resolve as resolve27 } from "path";
|
|
19362
19652
|
function deriveRoute(pageFile) {
|
|
19363
19653
|
let route = pageFile.replace(/^src\/app/, "").replace(/\/page\.tsx?$/, "").replace(/\/page\.jsx?$/, "");
|
|
@@ -19397,7 +19687,7 @@ function findRouterCalls(files) {
|
|
|
19397
19687
|
const projectRoot = getProjectRoot();
|
|
19398
19688
|
for (const file of files) {
|
|
19399
19689
|
const absPath = ensureWithinRoot(resolve27(projectRoot, file), projectRoot);
|
|
19400
|
-
if (!
|
|
19690
|
+
if (!existsSync31(absPath)) continue;
|
|
19401
19691
|
try {
|
|
19402
19692
|
const source = readFileSync27(absPath, "utf-8");
|
|
19403
19693
|
const apiCallRegex = /api\.(\w+)\.\w+/g;
|
|
@@ -19418,7 +19708,7 @@ function findTablesFromRouters(routerNames, dataDb) {
|
|
|
19418
19708
|
).all(routerName);
|
|
19419
19709
|
for (const proc of procs) {
|
|
19420
19710
|
const absPath = ensureWithinRoot(resolve27(getProjectRoot(), proc.router_file), getProjectRoot());
|
|
19421
|
-
if (!
|
|
19711
|
+
if (!existsSync31(absPath)) continue;
|
|
19422
19712
|
try {
|
|
19423
19713
|
const source = readFileSync27(absPath, "utf-8");
|
|
19424
19714
|
const dbPattern = getConfig().dbAccessPattern ?? "ctx.db.{table}";
|
|
@@ -19692,11 +19982,11 @@ var init_domains = __esm({
|
|
|
19692
19982
|
});
|
|
19693
19983
|
|
|
19694
19984
|
// src/schema-mapper.ts
|
|
19695
|
-
import { readFileSync as readFileSync28, existsSync as
|
|
19985
|
+
import { readFileSync as readFileSync28, existsSync as existsSync32, readdirSync as readdirSync17 } from "fs";
|
|
19696
19986
|
import { join as join15 } from "path";
|
|
19697
19987
|
function parsePrismaSchema() {
|
|
19698
19988
|
const schemaPath = getResolvedPaths().prismaSchemaPath;
|
|
19699
|
-
if (!
|
|
19989
|
+
if (!existsSync32(schemaPath)) {
|
|
19700
19990
|
throw new Error(`Prisma schema not found at ${schemaPath}`);
|
|
19701
19991
|
}
|
|
19702
19992
|
const source = readFileSync28(schemaPath, "utf-8");
|
|
@@ -19757,7 +20047,7 @@ function toSnakeCase(str) {
|
|
|
19757
20047
|
function findColumnUsageInRouters(tableName) {
|
|
19758
20048
|
const usage = /* @__PURE__ */ new Map();
|
|
19759
20049
|
const routersDir = getResolvedPaths().routersDir;
|
|
19760
|
-
if (!
|
|
20050
|
+
if (!existsSync32(routersDir)) return usage;
|
|
19761
20051
|
scanDirectory(routersDir, tableName, usage);
|
|
19762
20052
|
return usage;
|
|
19763
20053
|
}
|
|
@@ -19819,7 +20109,7 @@ function detectMismatches(models) {
|
|
|
19819
20109
|
}
|
|
19820
20110
|
function findFilesUsingColumn(dir, column, tableName) {
|
|
19821
20111
|
const result = [];
|
|
19822
|
-
if (!
|
|
20112
|
+
if (!existsSync32(dir)) return result;
|
|
19823
20113
|
const entries = readdirSync17(dir, { withFileTypes: true });
|
|
19824
20114
|
for (const entry of entries) {
|
|
19825
20115
|
const fullPath = join15(dir, entry.name);
|
|
@@ -19972,14 +20262,14 @@ var init_import_parser = __esm({
|
|
|
19972
20262
|
});
|
|
19973
20263
|
|
|
19974
20264
|
// 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
|
|
20265
|
+
import { readFileSync as readFileSync29, existsSync as existsSync33, readdirSync as readdirSync18 } from "fs";
|
|
20266
|
+
import { resolve as resolve29, join as join16, relative as relative6, dirname as dirname16 } from "path";
|
|
19977
20267
|
function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
|
|
19978
20268
|
const projectRoot = getProjectRoot();
|
|
19979
20269
|
if (level > 0) {
|
|
19980
|
-
let baseDir =
|
|
20270
|
+
let baseDir = dirname16(resolve29(projectRoot, fromFile));
|
|
19981
20271
|
for (let i = 1; i < level; i++) {
|
|
19982
|
-
baseDir =
|
|
20272
|
+
baseDir = dirname16(baseDir);
|
|
19983
20273
|
}
|
|
19984
20274
|
const modulePart = module.replace(/^\.+/, "");
|
|
19985
20275
|
if (modulePart) {
|
|
@@ -19993,13 +20283,13 @@ function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
|
|
|
19993
20283
|
return tryResolvePythonPath(candidate, projectRoot);
|
|
19994
20284
|
}
|
|
19995
20285
|
function tryResolvePythonPath(basePath, projectRoot) {
|
|
19996
|
-
if (
|
|
20286
|
+
if (existsSync33(basePath + ".py")) {
|
|
19997
20287
|
return relative6(projectRoot, basePath + ".py");
|
|
19998
20288
|
}
|
|
19999
|
-
if (
|
|
20289
|
+
if (existsSync33(join16(basePath, "__init__.py"))) {
|
|
20000
20290
|
return relative6(projectRoot, join16(basePath, "__init__.py"));
|
|
20001
20291
|
}
|
|
20002
|
-
if (basePath.endsWith(".py") &&
|
|
20292
|
+
if (basePath.endsWith(".py") && existsSync33(basePath)) {
|
|
20003
20293
|
return relative6(projectRoot, basePath);
|
|
20004
20294
|
}
|
|
20005
20295
|
return null;
|
|
@@ -21352,7 +21642,7 @@ var init_memory_tools = __esm({
|
|
|
21352
21642
|
});
|
|
21353
21643
|
|
|
21354
21644
|
// src/docs-tools.ts
|
|
21355
|
-
import { readFileSync as readFileSync34, existsSync as
|
|
21645
|
+
import { readFileSync as readFileSync34, existsSync as existsSync34 } from "fs";
|
|
21356
21646
|
import { resolve as resolve30, basename as basename6 } from "path";
|
|
21357
21647
|
function p3(baseName) {
|
|
21358
21648
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
@@ -21408,7 +21698,7 @@ function handleDocsToolCall(name, args2) {
|
|
|
21408
21698
|
}
|
|
21409
21699
|
function loadDocsMap() {
|
|
21410
21700
|
const mapPath = getResolvedPaths().docsMapPath;
|
|
21411
|
-
if (!
|
|
21701
|
+
if (!existsSync34(mapPath)) {
|
|
21412
21702
|
throw new Error(`docs-map.json not found at ${mapPath}`);
|
|
21413
21703
|
}
|
|
21414
21704
|
return JSON.parse(readFileSync34(mapPath, "utf-8"));
|
|
@@ -21482,9 +21772,9 @@ function extractFrontmatter(content) {
|
|
|
21482
21772
|
function extractProcedureNames(routerPath) {
|
|
21483
21773
|
const root = getProjectRoot();
|
|
21484
21774
|
const absPath = ensureWithinRoot(resolve30(getResolvedPaths().srcDir, "..", routerPath), root);
|
|
21485
|
-
if (!
|
|
21775
|
+
if (!existsSync34(absPath)) {
|
|
21486
21776
|
const altPath = ensureWithinRoot(resolve30(getResolvedPaths().srcDir, "../server/api/routers", basename6(routerPath)), root);
|
|
21487
|
-
if (!
|
|
21777
|
+
if (!existsSync34(altPath)) return [];
|
|
21488
21778
|
return extractProcedureNamesFromContent(readFileSync34(altPath, "utf-8"));
|
|
21489
21779
|
}
|
|
21490
21780
|
return extractProcedureNamesFromContent(readFileSync34(absPath, "utf-8"));
|
|
@@ -21528,7 +21818,7 @@ function handleDocsAudit(args2) {
|
|
|
21528
21818
|
const mapping = docsMap.mappings.find((m3) => m3.id === mappingId);
|
|
21529
21819
|
if (!mapping) continue;
|
|
21530
21820
|
const helpPagePath = ensureWithinRoot(resolve30(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
|
|
21531
|
-
if (!
|
|
21821
|
+
if (!existsSync34(helpPagePath)) {
|
|
21532
21822
|
results.push({
|
|
21533
21823
|
helpPage: mapping.helpPage,
|
|
21534
21824
|
mappingId,
|
|
@@ -21581,7 +21871,7 @@ function handleDocsAudit(args2) {
|
|
|
21581
21871
|
for (const [guideName, parentId] of Object.entries(docsMap.userGuideInheritance.examples)) {
|
|
21582
21872
|
if (parentId === mappingId) {
|
|
21583
21873
|
const guidePath = ensureWithinRoot(resolve30(getResolvedPaths().helpSitePath, `pages/user-guides/${guideName}/index.mdx`), getProjectRoot());
|
|
21584
|
-
if (
|
|
21874
|
+
if (existsSync34(guidePath)) {
|
|
21585
21875
|
const guideContent = readFileSync34(guidePath, "utf-8");
|
|
21586
21876
|
const guideFrontmatter = extractFrontmatter(guideContent);
|
|
21587
21877
|
if (!guideFrontmatter?.lastVerified || status === "STALE") {
|
|
@@ -21616,7 +21906,7 @@ function handleDocsCoverage(args2) {
|
|
|
21616
21906
|
const mappings = filterDomain ? docsMap.mappings.filter((m3) => m3.id === filterDomain) : docsMap.mappings;
|
|
21617
21907
|
for (const mapping of mappings) {
|
|
21618
21908
|
const helpPagePath = ensureWithinRoot(resolve30(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
|
|
21619
|
-
const exists =
|
|
21909
|
+
const exists = existsSync34(helpPagePath);
|
|
21620
21910
|
let hasContent = false;
|
|
21621
21911
|
let lineCount = 0;
|
|
21622
21912
|
let lastVerified = null;
|
|
@@ -21963,7 +22253,7 @@ var init_observability_tools = __esm({
|
|
|
21963
22253
|
});
|
|
21964
22254
|
|
|
21965
22255
|
// src/sentinel-db.ts
|
|
21966
|
-
import { existsSync as
|
|
22256
|
+
import { existsSync as existsSync35 } from "fs";
|
|
21967
22257
|
import { resolve as resolve31 } from "path";
|
|
21968
22258
|
function parsePortalScope(raw) {
|
|
21969
22259
|
if (!raw) return [];
|
|
@@ -22201,22 +22491,22 @@ function validateFeatures(db, domainFilter) {
|
|
|
22201
22491
|
const missingPages = [];
|
|
22202
22492
|
for (const comp of components) {
|
|
22203
22493
|
const absPath = resolve31(PROJECT_ROOT, comp.component_file);
|
|
22204
|
-
if (!
|
|
22494
|
+
if (!existsSync35(absPath)) {
|
|
22205
22495
|
missingComponents.push(comp.component_file);
|
|
22206
22496
|
}
|
|
22207
22497
|
}
|
|
22208
22498
|
for (const proc of procedures) {
|
|
22209
22499
|
const routerPath = resolve31(PROJECT_ROOT, `src/server/api/routers/${proc.router_name}.ts`);
|
|
22210
|
-
if (!
|
|
22500
|
+
if (!existsSync35(routerPath)) {
|
|
22211
22501
|
missingProcedures.push({ router: proc.router_name, procedure: proc.procedure_name });
|
|
22212
22502
|
}
|
|
22213
22503
|
}
|
|
22214
22504
|
for (const page of pages) {
|
|
22215
22505
|
const routeToPath = page.page_route.replace(/^\/(portal-[^/]+\/)?/, "src/app/").replace(/\/$/, "") + "/page.tsx";
|
|
22216
22506
|
const absPath = resolve31(PROJECT_ROOT, routeToPath);
|
|
22217
|
-
if (page.page_route.startsWith("/") && !
|
|
22507
|
+
if (page.page_route.startsWith("/") && !existsSync35(absPath)) {
|
|
22218
22508
|
const altPath = resolve31(PROJECT_ROOT, `src/app${page.page_route}/page.tsx`);
|
|
22219
|
-
if (!
|
|
22509
|
+
if (!existsSync35(altPath)) {
|
|
22220
22510
|
missingPages.push(page.page_route);
|
|
22221
22511
|
}
|
|
22222
22512
|
}
|
|
@@ -22742,8 +23032,8 @@ var init_sentinel_tools = __esm({
|
|
|
22742
23032
|
});
|
|
22743
23033
|
|
|
22744
23034
|
// src/sentinel-scanner.ts
|
|
22745
|
-
import { readFileSync as readFileSync35, existsSync as
|
|
22746
|
-
import { resolve as resolve32, join as join21, basename as basename7, dirname as
|
|
23035
|
+
import { readFileSync as readFileSync35, existsSync as existsSync36, readdirSync as readdirSync23, statSync as statSync13 } from "fs";
|
|
23036
|
+
import { resolve as resolve32, join as join21, basename as basename7, dirname as dirname17, relative as relative11 } from "path";
|
|
22747
23037
|
function inferDomain(filePath) {
|
|
22748
23038
|
const domains = getConfig().domains;
|
|
22749
23039
|
const path = filePath.toLowerCase();
|
|
@@ -22873,7 +23163,7 @@ function scanComponentExports(dataDb) {
|
|
|
22873
23163
|
const componentsBase = config.paths.components ?? config.paths.source + "/components";
|
|
22874
23164
|
const componentDirs = [];
|
|
22875
23165
|
const basePath = resolve32(projectRoot, componentsBase);
|
|
22876
|
-
if (
|
|
23166
|
+
if (existsSync36(basePath)) {
|
|
22877
23167
|
try {
|
|
22878
23168
|
const entries = readdirSync23(basePath, { withFileTypes: true });
|
|
22879
23169
|
for (const entry of entries) {
|
|
@@ -22886,7 +23176,7 @@ function scanComponentExports(dataDb) {
|
|
|
22886
23176
|
}
|
|
22887
23177
|
for (const dir of componentDirs) {
|
|
22888
23178
|
const absDir = resolve32(projectRoot, dir);
|
|
22889
|
-
if (!
|
|
23179
|
+
if (!existsSync36(absDir)) continue;
|
|
22890
23180
|
const files = walkDir2(absDir).filter((f2) => f2.endsWith(".tsx") || f2.endsWith(".ts"));
|
|
22891
23181
|
for (const file of files) {
|
|
22892
23182
|
const relPath = relative11(projectRoot, file);
|
|
@@ -22916,7 +23206,7 @@ function scanComponentExports(dataDb) {
|
|
|
22916
23206
|
if (hasHandlers && exportMatch) {
|
|
22917
23207
|
const componentName = exportMatch[1];
|
|
22918
23208
|
const domain = inferDomain(relPath);
|
|
22919
|
-
const subdomain = basename7(
|
|
23209
|
+
const subdomain = basename7(dirname17(relPath));
|
|
22920
23210
|
const featureKey = `component.${subdomain}.${componentName.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "")}`;
|
|
22921
23211
|
if (!annotations.some((a2) => a2.featureKey === featureKey)) {
|
|
22922
23212
|
features.push({
|
|
@@ -23930,7 +24220,7 @@ var init_audit_trail = __esm({
|
|
|
23930
24220
|
});
|
|
23931
24221
|
|
|
23932
24222
|
// src/validation-engine.ts
|
|
23933
|
-
import { existsSync as
|
|
24223
|
+
import { existsSync as existsSync37, readFileSync as readFileSync36 } from "fs";
|
|
23934
24224
|
function p10(baseName) {
|
|
23935
24225
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
23936
24226
|
}
|
|
@@ -23961,7 +24251,7 @@ function validateFile(filePath, projectRoot) {
|
|
|
23961
24251
|
});
|
|
23962
24252
|
return checks;
|
|
23963
24253
|
}
|
|
23964
|
-
if (!
|
|
24254
|
+
if (!existsSync37(absPath)) {
|
|
23965
24255
|
checks.push({
|
|
23966
24256
|
name: "file_exists",
|
|
23967
24257
|
severity: "error",
|
|
@@ -24415,7 +24705,7 @@ var init_adr_generator = __esm({
|
|
|
24415
24705
|
});
|
|
24416
24706
|
|
|
24417
24707
|
// src/security-scorer.ts
|
|
24418
|
-
import { existsSync as
|
|
24708
|
+
import { existsSync as existsSync38, readFileSync as readFileSync37 } from "fs";
|
|
24419
24709
|
function p12(baseName) {
|
|
24420
24710
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
24421
24711
|
}
|
|
@@ -24439,7 +24729,7 @@ function scoreFileSecurity(filePath, projectRoot) {
|
|
|
24439
24729
|
}]
|
|
24440
24730
|
};
|
|
24441
24731
|
}
|
|
24442
|
-
if (!
|
|
24732
|
+
if (!existsSync38(absPath)) {
|
|
24443
24733
|
return { riskScore: 0, findings: [] };
|
|
24444
24734
|
}
|
|
24445
24735
|
let source;
|
|
@@ -24733,7 +25023,7 @@ var init_security_scorer = __esm({
|
|
|
24733
25023
|
});
|
|
24734
25024
|
|
|
24735
25025
|
// src/dependency-scorer.ts
|
|
24736
|
-
import { existsSync as
|
|
25026
|
+
import { existsSync as existsSync39, readFileSync as readFileSync38 } from "fs";
|
|
24737
25027
|
import { resolve as resolve33 } from "path";
|
|
24738
25028
|
function p13(baseName) {
|
|
24739
25029
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
@@ -24767,7 +25057,7 @@ function calculateDepRisk(factors) {
|
|
|
24767
25057
|
}
|
|
24768
25058
|
function getInstalledPackages(projectRoot) {
|
|
24769
25059
|
const pkgPath = resolve33(projectRoot, "package.json");
|
|
24770
|
-
if (!
|
|
25060
|
+
if (!existsSync39(pkgPath)) return /* @__PURE__ */ new Map();
|
|
24771
25061
|
try {
|
|
24772
25062
|
const pkg = JSON.parse(readFileSync38(pkgPath, "utf-8"));
|
|
24773
25063
|
const packages = /* @__PURE__ */ new Map();
|
|
@@ -25372,7 +25662,7 @@ var init_regression_detector = __esm({
|
|
|
25372
25662
|
|
|
25373
25663
|
// src/knowledge-indexer.ts
|
|
25374
25664
|
import { createHash as createHash9 } from "crypto";
|
|
25375
|
-
import { readFileSync as readFileSync39, readdirSync as readdirSync24, statSync as statSync14, existsSync as
|
|
25665
|
+
import { readFileSync as readFileSync39, readdirSync as readdirSync24, statSync as statSync14, existsSync as existsSync40 } from "fs";
|
|
25376
25666
|
import { resolve as resolve34, relative as relative12, basename as basename8, extname as extname2 } from "path";
|
|
25377
25667
|
function getKnowledgePaths() {
|
|
25378
25668
|
const resolved = getResolvedPaths();
|
|
@@ -25680,11 +25970,11 @@ function indexAllKnowledge(db) {
|
|
|
25680
25970
|
files.push(...memFiles);
|
|
25681
25971
|
} catch {
|
|
25682
25972
|
}
|
|
25683
|
-
if (
|
|
25973
|
+
if (existsSync40(paths.plansDir)) {
|
|
25684
25974
|
const planFiles = discoverMarkdownFiles(paths.plansDir);
|
|
25685
25975
|
files.push(...planFiles);
|
|
25686
25976
|
}
|
|
25687
|
-
if (
|
|
25977
|
+
if (existsSync40(paths.docsDir)) {
|
|
25688
25978
|
const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
|
|
25689
25979
|
const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f2) => !f2.includes("/plans/") && !excludePatterns.some((p19) => f2.includes(p19)));
|
|
25690
25980
|
files.push(...docsFiles);
|
|
@@ -25727,7 +26017,7 @@ function indexAllKnowledge(db) {
|
|
|
25727
26017
|
} catch {
|
|
25728
26018
|
}
|
|
25729
26019
|
for (const filePath of files) {
|
|
25730
|
-
if (!
|
|
26020
|
+
if (!existsSync40(filePath)) continue;
|
|
25731
26021
|
const content = readFileSync39(filePath, "utf-8");
|
|
25732
26022
|
const hash = hashContent2(content);
|
|
25733
26023
|
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 +26116,21 @@ Rule: ${c2.rule}`,
|
|
|
25826
26116
|
}
|
|
25827
26117
|
}
|
|
25828
26118
|
}
|
|
26119
|
+
const customerRules = getConfig().governance_rules ?? [];
|
|
26120
|
+
const insertCustomerRule = db.prepare(
|
|
26121
|
+
`INSERT OR REPLACE INTO knowledge_rules (rule_id, rule_text, vr_type, reference_path, severity, source)
|
|
26122
|
+
VALUES (?, ?, ?, ?, ?, 'customer-config')`
|
|
26123
|
+
);
|
|
26124
|
+
for (const rule of customerRules) {
|
|
26125
|
+
const namespacedId = rule.id.startsWith("customer:") ? rule.id : `customer:${rule.id}`;
|
|
26126
|
+
insertCustomerRule.run(
|
|
26127
|
+
namespacedId,
|
|
26128
|
+
`${rule.title}: ${rule.description}`,
|
|
26129
|
+
rule.vr_type ?? "VR-CUSTOM",
|
|
26130
|
+
rule.reference_path ?? null,
|
|
26131
|
+
rule.severity?.toUpperCase() ?? "MEDIUM"
|
|
26132
|
+
);
|
|
26133
|
+
}
|
|
25829
26134
|
stats.edgesCreated = buildCrossReferences(db);
|
|
25830
26135
|
const finalNow = /* @__PURE__ */ new Date();
|
|
25831
26136
|
const finalIso = finalNow.toISOString();
|
|
@@ -25848,10 +26153,10 @@ function isKnowledgeStale(db) {
|
|
|
25848
26153
|
files.push(...discoverMarkdownFiles(paths.memoryDir));
|
|
25849
26154
|
} catch {
|
|
25850
26155
|
}
|
|
25851
|
-
if (
|
|
26156
|
+
if (existsSync40(paths.plansDir)) {
|
|
25852
26157
|
files.push(...discoverMarkdownFiles(paths.plansDir));
|
|
25853
26158
|
}
|
|
25854
|
-
if (
|
|
26159
|
+
if (existsSync40(paths.docsDir)) {
|
|
25855
26160
|
const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
|
|
25856
26161
|
const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f2) => !f2.includes("/plans/") && !excludePatterns.some((p19) => f2.includes(p19)));
|
|
25857
26162
|
files.push(...docsFiles);
|
|
@@ -26968,176 +27273,6 @@ var init_knowledge_tools = __esm({
|
|
|
26968
27273
|
}
|
|
26969
27274
|
});
|
|
26970
27275
|
|
|
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
27276
|
// src/python/domain-enforcer.ts
|
|
27142
27277
|
function classifyPythonFileDomain(file) {
|
|
27143
27278
|
const config = getConfig();
|
|
@@ -27946,29 +28081,37 @@ function assertCodegraphDb(codegraphDb, toolName) {
|
|
|
27946
28081
|
}
|
|
27947
28082
|
return codegraphDb;
|
|
27948
28083
|
}
|
|
27949
|
-
async function handleToolCall(name, args2, dataDb, codegraphDb) {
|
|
28084
|
+
async function handleToolCall(name, args2, dataDb, codegraphDb, memoryDbCache, knowledgeDbCache) {
|
|
27950
28085
|
const userTier = await getCurrentTier();
|
|
27951
28086
|
const requiredTier = getToolTier(name);
|
|
27952
28087
|
if (!isToolAllowed(name, userTier)) {
|
|
27953
28088
|
return text17(`This tool requires ${requiredTier} tier. Current tier: ${userTier}. Upgrade at https://massu.ai/pricing`);
|
|
27954
28089
|
}
|
|
27955
28090
|
const pfx = prefix2();
|
|
28091
|
+
async function withMemDb(fn) {
|
|
28092
|
+
if (memoryDbCache) return fn(memoryDbCache);
|
|
28093
|
+
const db = getMemoryDb();
|
|
28094
|
+
try {
|
|
28095
|
+
return await fn(db);
|
|
28096
|
+
} finally {
|
|
28097
|
+
db.close();
|
|
28098
|
+
}
|
|
28099
|
+
}
|
|
28100
|
+
async function withKnowledgeDb(fn) {
|
|
28101
|
+
if (knowledgeDbCache) return fn(knowledgeDbCache);
|
|
28102
|
+
const db = getKnowledgeDb();
|
|
28103
|
+
try {
|
|
28104
|
+
return await fn(db);
|
|
28105
|
+
} finally {
|
|
28106
|
+
db.close();
|
|
28107
|
+
}
|
|
28108
|
+
}
|
|
27956
28109
|
try {
|
|
27957
28110
|
if (name.startsWith(pfx + "_memory_")) {
|
|
27958
|
-
|
|
27959
|
-
try {
|
|
27960
|
-
return handleMemoryToolCall(name, args2, memDb);
|
|
27961
|
-
} finally {
|
|
27962
|
-
memDb.close();
|
|
27963
|
-
}
|
|
28111
|
+
return withMemDb((memDb) => handleMemoryToolCall(name, args2, memDb));
|
|
27964
28112
|
}
|
|
27965
28113
|
if (isObservabilityTool(name)) {
|
|
27966
|
-
|
|
27967
|
-
try {
|
|
27968
|
-
return handleObservabilityToolCall(name, args2, memDb);
|
|
27969
|
-
} finally {
|
|
27970
|
-
memDb.close();
|
|
27971
|
-
}
|
|
28114
|
+
return withMemDb((memDb) => handleObservabilityToolCall(name, args2, memDb));
|
|
27972
28115
|
}
|
|
27973
28116
|
if (name.startsWith(pfx + "_docs_")) {
|
|
27974
28117
|
return handleDocsToolCall(name, args2);
|
|
@@ -27977,92 +28120,37 @@ async function handleToolCall(name, args2, dataDb, codegraphDb) {
|
|
|
27977
28120
|
return handleSentinelToolCall(name, args2, assertDataDb(dataDb, name));
|
|
27978
28121
|
}
|
|
27979
28122
|
if (isAnalyticsTool(name)) {
|
|
27980
|
-
|
|
27981
|
-
try {
|
|
27982
|
-
return handleAnalyticsToolCall(name, args2, memDb);
|
|
27983
|
-
} finally {
|
|
27984
|
-
memDb.close();
|
|
27985
|
-
}
|
|
28123
|
+
return withMemDb((memDb) => handleAnalyticsToolCall(name, args2, memDb));
|
|
27986
28124
|
}
|
|
27987
28125
|
if (isCostTool(name)) {
|
|
27988
|
-
|
|
27989
|
-
try {
|
|
27990
|
-
return handleCostToolCall(name, args2, memDb);
|
|
27991
|
-
} finally {
|
|
27992
|
-
memDb.close();
|
|
27993
|
-
}
|
|
28126
|
+
return withMemDb((memDb) => handleCostToolCall(name, args2, memDb));
|
|
27994
28127
|
}
|
|
27995
28128
|
if (isPromptTool(name)) {
|
|
27996
|
-
|
|
27997
|
-
try {
|
|
27998
|
-
return handlePromptToolCall(name, args2, memDb);
|
|
27999
|
-
} finally {
|
|
28000
|
-
memDb.close();
|
|
28001
|
-
}
|
|
28129
|
+
return withMemDb((memDb) => handlePromptToolCall(name, args2, memDb));
|
|
28002
28130
|
}
|
|
28003
28131
|
if (isAuditTool(name)) {
|
|
28004
|
-
|
|
28005
|
-
try {
|
|
28006
|
-
return handleAuditToolCall(name, args2, memDb);
|
|
28007
|
-
} finally {
|
|
28008
|
-
memDb.close();
|
|
28009
|
-
}
|
|
28132
|
+
return withMemDb((memDb) => handleAuditToolCall(name, args2, memDb));
|
|
28010
28133
|
}
|
|
28011
28134
|
if (isValidationTool(name)) {
|
|
28012
|
-
|
|
28013
|
-
try {
|
|
28014
|
-
return handleValidationToolCall(name, args2, memDb);
|
|
28015
|
-
} finally {
|
|
28016
|
-
memDb.close();
|
|
28017
|
-
}
|
|
28135
|
+
return withMemDb((memDb) => handleValidationToolCall(name, args2, memDb));
|
|
28018
28136
|
}
|
|
28019
28137
|
if (isAdrTool(name)) {
|
|
28020
|
-
|
|
28021
|
-
try {
|
|
28022
|
-
return handleAdrToolCall(name, args2, memDb);
|
|
28023
|
-
} finally {
|
|
28024
|
-
memDb.close();
|
|
28025
|
-
}
|
|
28138
|
+
return withMemDb((memDb) => handleAdrToolCall(name, args2, memDb));
|
|
28026
28139
|
}
|
|
28027
28140
|
if (isSecurityTool(name)) {
|
|
28028
|
-
|
|
28029
|
-
try {
|
|
28030
|
-
return handleSecurityToolCall(name, args2, memDb);
|
|
28031
|
-
} finally {
|
|
28032
|
-
memDb.close();
|
|
28033
|
-
}
|
|
28141
|
+
return withMemDb((memDb) => handleSecurityToolCall(name, args2, memDb));
|
|
28034
28142
|
}
|
|
28035
28143
|
if (isDependencyTool(name)) {
|
|
28036
|
-
|
|
28037
|
-
try {
|
|
28038
|
-
return handleDependencyToolCall(name, args2, memDb);
|
|
28039
|
-
} finally {
|
|
28040
|
-
memDb.close();
|
|
28041
|
-
}
|
|
28144
|
+
return withMemDb((memDb) => handleDependencyToolCall(name, args2, memDb));
|
|
28042
28145
|
}
|
|
28043
28146
|
if (isTeamTool(name) && isCloudFeatureAvailable()) {
|
|
28044
|
-
|
|
28045
|
-
try {
|
|
28046
|
-
return handleTeamToolCall(name, args2, memDb);
|
|
28047
|
-
} finally {
|
|
28048
|
-
memDb.close();
|
|
28049
|
-
}
|
|
28147
|
+
return withMemDb((memDb) => handleTeamToolCall(name, args2, memDb));
|
|
28050
28148
|
}
|
|
28051
28149
|
if (isRegressionTool(name)) {
|
|
28052
|
-
|
|
28053
|
-
try {
|
|
28054
|
-
return handleRegressionToolCall(name, args2, memDb);
|
|
28055
|
-
} finally {
|
|
28056
|
-
memDb.close();
|
|
28057
|
-
}
|
|
28150
|
+
return withMemDb((memDb) => handleRegressionToolCall(name, args2, memDb));
|
|
28058
28151
|
}
|
|
28059
28152
|
if (isKnowledgeTool(name)) {
|
|
28060
|
-
|
|
28061
|
-
try {
|
|
28062
|
-
return handleKnowledgeToolCall(name, args2, knowledgeDb);
|
|
28063
|
-
} finally {
|
|
28064
|
-
knowledgeDb.close();
|
|
28065
|
-
}
|
|
28153
|
+
return withKnowledgeDb((kdb) => handleKnowledgeToolCall(name, args2, kdb));
|
|
28066
28154
|
}
|
|
28067
28155
|
if (isPythonTool(name)) {
|
|
28068
28156
|
const pyDataDb = assertDataDb(dataDb, name);
|
|
@@ -28070,12 +28158,7 @@ async function handleToolCall(name, args2, dataDb, codegraphDb) {
|
|
|
28070
28158
|
return handlePythonToolCall(name, args2, pyDataDb);
|
|
28071
28159
|
}
|
|
28072
28160
|
if (isLicenseTool(name)) {
|
|
28073
|
-
|
|
28074
|
-
try {
|
|
28075
|
-
return await handleLicenseToolCall(name, args2, memDb);
|
|
28076
|
-
} finally {
|
|
28077
|
-
memDb.close();
|
|
28078
|
-
}
|
|
28161
|
+
return withMemDb((memDb) => handleLicenseToolCall(name, args2, memDb));
|
|
28079
28162
|
}
|
|
28080
28163
|
const baseName = stripPrefix3(name);
|
|
28081
28164
|
switch (baseName) {
|
|
@@ -28897,10 +28980,14 @@ var init_tool_db_needs = __esm({
|
|
|
28897
28980
|
function createDispatcher(options) {
|
|
28898
28981
|
let codegraphDbCache = null;
|
|
28899
28982
|
let dataDbCache = null;
|
|
28983
|
+
let memoryDbCache = null;
|
|
28984
|
+
let knowledgeDbCache = null;
|
|
28900
28985
|
function resolveDbsForTool(toolName) {
|
|
28901
28986
|
const needs = getToolDbNeeds(toolName, getConfig().toolPrefix);
|
|
28902
28987
|
let dataDbResolved;
|
|
28903
28988
|
let codegraphDbResolved;
|
|
28989
|
+
let memoryDbResolved;
|
|
28990
|
+
let knowledgeDbResolved;
|
|
28904
28991
|
if (needs.includes("data")) {
|
|
28905
28992
|
if (!dataDbCache) dataDbCache = getDataDb();
|
|
28906
28993
|
dataDbResolved = dataDbCache;
|
|
@@ -28909,7 +28996,21 @@ function createDispatcher(options) {
|
|
|
28909
28996
|
if (!codegraphDbCache) codegraphDbCache = getCodeGraphDb();
|
|
28910
28997
|
codegraphDbResolved = codegraphDbCache;
|
|
28911
28998
|
}
|
|
28912
|
-
|
|
28999
|
+
if (needs.includes("memory")) {
|
|
29000
|
+
if (!memoryDbCache) memoryDbCache = getMemoryDb();
|
|
29001
|
+
memoryDbResolved = memoryDbCache;
|
|
29002
|
+
}
|
|
29003
|
+
if (needs.includes("knowledge")) {
|
|
29004
|
+
if (!knowledgeDbCache) knowledgeDbCache = getKnowledgeDb();
|
|
29005
|
+
knowledgeDbResolved = knowledgeDbCache;
|
|
29006
|
+
}
|
|
29007
|
+
return {
|
|
29008
|
+
needs,
|
|
29009
|
+
dataDb: dataDbResolved,
|
|
29010
|
+
codegraphDb: codegraphDbResolved,
|
|
29011
|
+
memoryDb: memoryDbResolved,
|
|
29012
|
+
knowledgeDb: knowledgeDbResolved
|
|
29013
|
+
};
|
|
28913
29014
|
}
|
|
28914
29015
|
async function handleRequest(request) {
|
|
28915
29016
|
const { method, params, id } = request;
|
|
@@ -28939,8 +29040,8 @@ function createDispatcher(options) {
|
|
|
28939
29040
|
const toolName = params?.name ?? "";
|
|
28940
29041
|
const toolArgs = params?.arguments ?? {};
|
|
28941
29042
|
try {
|
|
28942
|
-
const { dataDb, codegraphDb } = resolveDbsForTool(toolName);
|
|
28943
|
-
const result = await handleToolCall(toolName, toolArgs, dataDb, codegraphDb);
|
|
29043
|
+
const { dataDb, codegraphDb, memoryDb, knowledgeDb } = resolveDbsForTool(toolName);
|
|
29044
|
+
const result = await handleToolCall(toolName, toolArgs, dataDb, codegraphDb, memoryDb, knowledgeDb);
|
|
28944
29045
|
return { jsonrpc: "2.0", id: id ?? null, result };
|
|
28945
29046
|
} catch (err) {
|
|
28946
29047
|
if (err instanceof CodegraphDbNotInitializedError) {
|
|
@@ -29032,6 +29133,14 @@ function createDispatcher(options) {
|
|
|
29032
29133
|
dataDbCache.close();
|
|
29033
29134
|
dataDbCache = null;
|
|
29034
29135
|
}
|
|
29136
|
+
if (memoryDbCache) {
|
|
29137
|
+
memoryDbCache.close();
|
|
29138
|
+
memoryDbCache = null;
|
|
29139
|
+
}
|
|
29140
|
+
if (knowledgeDbCache) {
|
|
29141
|
+
knowledgeDbCache.close();
|
|
29142
|
+
knowledgeDbCache = null;
|
|
29143
|
+
}
|
|
29035
29144
|
}
|
|
29036
29145
|
return { handleRequest, processLine, closeCachedDbs };
|
|
29037
29146
|
}
|
|
@@ -29039,6 +29148,8 @@ var init_server_dispatch = __esm({
|
|
|
29039
29148
|
"src/server-dispatch.ts"() {
|
|
29040
29149
|
"use strict";
|
|
29041
29150
|
init_db();
|
|
29151
|
+
init_memory_db();
|
|
29152
|
+
init_knowledge_db();
|
|
29042
29153
|
init_config();
|
|
29043
29154
|
init_tools();
|
|
29044
29155
|
init_tool_db_needs();
|
|
@@ -29646,6 +29757,15 @@ async function main() {
|
|
|
29646
29757
|
break;
|
|
29647
29758
|
}
|
|
29648
29759
|
default: {
|
|
29760
|
+
if (subcommand && !subcommand.startsWith("-")) {
|
|
29761
|
+
process.stderr.write(
|
|
29762
|
+
`Error: Unknown subcommand '${subcommand}'.
|
|
29763
|
+
Run \`npx @massu/core --help\` for the list of available subcommands.
|
|
29764
|
+
To start the MCP server, run without a subcommand (e.g. \`npx @massu/core\`).
|
|
29765
|
+
`
|
|
29766
|
+
);
|
|
29767
|
+
process.exit(2);
|
|
29768
|
+
}
|
|
29649
29769
|
await Promise.resolve().then(() => (init_server(), server_exports));
|
|
29650
29770
|
}
|
|
29651
29771
|
}
|