@massu/core 2.5.4 → 2.5.6
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 +66 -0
- package/dist/cli.js +505 -407
- package/dist/hooks/auto-learning-pipeline.js +26 -2
- package/dist/hooks/classify-failure.js +53 -656
- package/dist/hooks/cost-tracker.js +26 -2
- package/dist/hooks/fix-detector.js +30 -4
- package/dist/hooks/incident-pipeline.js +26 -2
- package/dist/hooks/intent-suggester.js +26 -2
- package/dist/hooks/memory-recall.js +26 -2
- package/dist/hooks/post-edit-context.js +26 -2
- package/dist/hooks/post-tool-use.js +26 -2
- package/dist/hooks/pre-compact.js +26 -2
- package/dist/hooks/pre-delete-check.js +405 -71
- package/dist/hooks/pre-tool-use-gate.js +196 -223
- package/dist/hooks/quality-event.js +26 -2
- package/dist/hooks/rule-enforcement-pipeline.js +26 -2
- package/dist/hooks/security-gate.js +570 -181
- package/dist/hooks/session-end.js +1081 -354
- package/dist/hooks/session-start.js +26 -2
- package/dist/hooks/user-prompt.js +26 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -421,12 +421,12 @@ function runRebuild(pkgDir, abiFrom, abiTo, start) {
|
|
|
421
421
|
}
|
|
422
422
|
const prebuildBin = resolveLocalBin(pkgDir, "prebuild-install");
|
|
423
423
|
if (prebuildBin) {
|
|
424
|
-
const
|
|
424
|
+
const r3 = spawnSync2(process.execPath, [prebuildBin], {
|
|
425
425
|
cwd: pkgDir,
|
|
426
426
|
encoding: "utf-8",
|
|
427
427
|
timeout: 12e4
|
|
428
428
|
});
|
|
429
|
-
if (
|
|
429
|
+
if (r3.status === 0) {
|
|
430
430
|
const res2 = { healed: true, method: "prebuild-install", abiFrom, abiTo, durationMs: Date.now() - start };
|
|
431
431
|
recordHealEvent({ phase: "success", reason: "abi-mismatch", ...res2 });
|
|
432
432
|
return res2;
|
|
@@ -434,12 +434,12 @@ function runRebuild(pkgDir, abiFrom, abiTo, start) {
|
|
|
434
434
|
}
|
|
435
435
|
const gypBin = resolveNodeGypBin();
|
|
436
436
|
if (gypBin && hasCompiler()) {
|
|
437
|
-
const
|
|
437
|
+
const r3 = spawnSync2(process.execPath, [gypBin, "rebuild", "--release"], {
|
|
438
438
|
cwd: pkgDir,
|
|
439
439
|
encoding: "utf-8",
|
|
440
440
|
timeout: 3e5
|
|
441
441
|
});
|
|
442
|
-
if (
|
|
442
|
+
if (r3.status === 0) {
|
|
443
443
|
const res2 = { healed: true, method: "node-gyp", abiFrom, abiTo, durationMs: Date.now() - start };
|
|
444
444
|
recordHealEvent({ phase: "success", reason: "abi-mismatch", ...res2 });
|
|
445
445
|
return res2;
|
|
@@ -518,8 +518,8 @@ function resolveNodeGypBin() {
|
|
|
518
518
|
function hasCompiler() {
|
|
519
519
|
for (const cc of ["cc", "clang", "gcc"]) {
|
|
520
520
|
try {
|
|
521
|
-
const
|
|
522
|
-
if (
|
|
521
|
+
const r3 = spawnSync2(cc, ["--version"], { encoding: "utf-8", timeout: 5e3 });
|
|
522
|
+
if (r3.status === 0) return true;
|
|
523
523
|
} catch {
|
|
524
524
|
}
|
|
525
525
|
}
|
|
@@ -593,10 +593,10 @@ function openNodeSqlite(dbPath, opts) {
|
|
|
593
593
|
const st = raw.prepare(sql);
|
|
594
594
|
return {
|
|
595
595
|
run: (...p19) => {
|
|
596
|
-
const
|
|
596
|
+
const r3 = st.run(...p19);
|
|
597
597
|
return {
|
|
598
|
-
changes: typeof
|
|
599
|
-
lastInsertRowid:
|
|
598
|
+
changes: typeof r3.changes === "bigint" ? Number(r3.changes) : r3.changes,
|
|
599
|
+
lastInsertRowid: r3.lastInsertRowid
|
|
600
600
|
};
|
|
601
601
|
},
|
|
602
602
|
get: (...p19) => plainRow(st.get(...p19)),
|
|
@@ -1170,7 +1170,8 @@ var init_config = __esm({
|
|
|
1170
1170
|
input_per_million: z2.number(),
|
|
1171
1171
|
output_per_million: z2.number(),
|
|
1172
1172
|
cache_read_per_million: z2.number().optional(),
|
|
1173
|
-
cache_write_per_million: z2.number().optional()
|
|
1173
|
+
cache_write_per_million: z2.number().optional(),
|
|
1174
|
+
cache_write_1h_per_million: z2.number().optional()
|
|
1174
1175
|
});
|
|
1175
1176
|
AnalyticsConfigSchema = z2.object({
|
|
1176
1177
|
quality: z2.object({
|
|
@@ -1793,7 +1794,7 @@ function tableExists(db, table) {
|
|
|
1793
1794
|
}
|
|
1794
1795
|
function columns(db, table) {
|
|
1795
1796
|
const rows = db.prepare(`PRAGMA table_info(${table})`).all();
|
|
1796
|
-
return new Set(rows.map((
|
|
1797
|
+
return new Set(rows.map((r3) => r3.name));
|
|
1797
1798
|
}
|
|
1798
1799
|
function migrateRuleDelivery(db) {
|
|
1799
1800
|
const ADDITIONS = [
|
|
@@ -1814,8 +1815,8 @@ function migrateRuleDelivery(db) {
|
|
|
1814
1815
|
function undeliveredSnapshot(db) {
|
|
1815
1816
|
const count = (t2) => {
|
|
1816
1817
|
if (!tableExists(db, t2)) return 0;
|
|
1817
|
-
const
|
|
1818
|
-
return
|
|
1818
|
+
const r3 = db.prepare(`SELECT COUNT(*) AS n FROM ${t2}`).get();
|
|
1819
|
+
return r3.n;
|
|
1819
1820
|
};
|
|
1820
1821
|
const promotions = count("team_promotion_outbound");
|
|
1821
1822
|
const revocations = count("team_revocation_outbound");
|
|
@@ -1825,9 +1826,9 @@ function undeliveredSnapshot(db) {
|
|
|
1825
1826
|
for (const t2 of RULE_OUTBOUND_TABLES) {
|
|
1826
1827
|
if (!tableExists(db, t2)) continue;
|
|
1827
1828
|
if (!columns(db, t2).has("attempts")) continue;
|
|
1828
|
-
const
|
|
1829
|
-
if (
|
|
1830
|
-
if (
|
|
1829
|
+
const r3 = db.prepare(`SELECT MAX(attempts) AS a, MIN(created_at) AS c FROM ${t2}`).get();
|
|
1830
|
+
if (r3.a !== null && r3.a > maxAttempts) maxAttempts = r3.a;
|
|
1831
|
+
if (r3.c !== null && (oldest === null || r3.c < oldest)) oldest = r3.c;
|
|
1831
1832
|
}
|
|
1832
1833
|
return {
|
|
1833
1834
|
promotions,
|
|
@@ -1918,9 +1919,9 @@ function setCandidateStatus(db, promptHash, status, destination) {
|
|
|
1918
1919
|
}
|
|
1919
1920
|
function getCandidate(db, promptHash) {
|
|
1920
1921
|
ensureRuleCandidatesTable(db);
|
|
1921
|
-
const
|
|
1922
|
-
if (!
|
|
1923
|
-
return { ...
|
|
1922
|
+
const r3 = db.prepare("SELECT * FROM rule_candidates WHERE prompt_hash = ?").get(promptHash);
|
|
1923
|
+
if (!r3) return null;
|
|
1924
|
+
return { ...r3, payload: safeParse(r3.payload_json) };
|
|
1924
1925
|
}
|
|
1925
1926
|
function safeParse(raw) {
|
|
1926
1927
|
try {
|
|
@@ -2138,11 +2139,11 @@ function getActiveEmbedModel() {
|
|
|
2138
2139
|
}
|
|
2139
2140
|
function loadEmbedSettings() {
|
|
2140
2141
|
try {
|
|
2141
|
-
const
|
|
2142
|
+
const r3 = getConfig().memory?.recall;
|
|
2142
2143
|
return {
|
|
2143
|
-
enabled:
|
|
2144
|
-
endpoint:
|
|
2145
|
-
model:
|
|
2144
|
+
enabled: r3?.embedEnabled ?? true,
|
|
2145
|
+
endpoint: r3?.embedEndpoint,
|
|
2146
|
+
model: r3?.embedModel
|
|
2146
2147
|
};
|
|
2147
2148
|
} catch {
|
|
2148
2149
|
return { enabled: true };
|
|
@@ -2416,13 +2417,13 @@ async function runEmbedSweep(db, cfg, opts = {}) {
|
|
|
2416
2417
|
break;
|
|
2417
2418
|
}
|
|
2418
2419
|
const units = [];
|
|
2419
|
-
for (const
|
|
2420
|
+
for (const r3 of rows) {
|
|
2420
2421
|
if (cfg.chunked) {
|
|
2421
|
-
const parts = chunkForEmbedding(
|
|
2422
|
+
const parts = chunkForEmbedding(r3.text);
|
|
2422
2423
|
if (parts.length === 0) continue;
|
|
2423
|
-
parts.forEach((text18, chunkIx) => units.push({ id:
|
|
2424
|
+
parts.forEach((text18, chunkIx) => units.push({ id: r3.id, chunkIx, text: text18 }));
|
|
2424
2425
|
} else {
|
|
2425
|
-
units.push({ id:
|
|
2426
|
+
units.push({ id: r3.id, chunkIx: 0, text: r3.text });
|
|
2426
2427
|
}
|
|
2427
2428
|
}
|
|
2428
2429
|
const vecs = await embedBatch(units.map((u2) => u2.text));
|
|
@@ -2541,6 +2542,7 @@ __export(memory_db_exports, {
|
|
|
2541
2542
|
pruneToolCostEvents: () => pruneToolCostEvents,
|
|
2542
2543
|
recordRecallHits: () => recordRecallHits,
|
|
2543
2544
|
recordTelemetry: () => recordTelemetry,
|
|
2545
|
+
releaseMetaLease: () => releaseMetaLease,
|
|
2544
2546
|
removePendingSync: () => removePendingSync,
|
|
2545
2547
|
sanitizeFts5Query: () => sanitizeFts5Query,
|
|
2546
2548
|
sanitizeFts5QueryOr: () => sanitizeFts5QueryOr,
|
|
@@ -2549,6 +2551,7 @@ __export(memory_db_exports, {
|
|
|
2549
2551
|
searchObservations: () => searchObservations,
|
|
2550
2552
|
setLastProcessedLine: () => setLastProcessedLine,
|
|
2551
2553
|
setMemoryMeta: () => setMemoryMeta,
|
|
2554
|
+
tryAcquireMetaLease: () => tryAcquireMetaLease,
|
|
2552
2555
|
upsertObservationEmbedding: () => upsertObservationEmbedding,
|
|
2553
2556
|
usageWarmupElapsed: () => usageWarmupElapsed
|
|
2554
2557
|
});
|
|
@@ -3054,6 +3057,15 @@ function initMemorySchema(db) {
|
|
|
3054
3057
|
);
|
|
3055
3058
|
CREATE INDEX IF NOT EXISTS idx_sc_session ON session_costs(session_id);
|
|
3056
3059
|
`);
|
|
3060
|
+
const costCols = db.prepare(`PRAGMA table_info(session_costs)`).all();
|
|
3061
|
+
const addCostCol = (name, ddl) => {
|
|
3062
|
+
if (!costCols.some((c2) => c2.name === name)) db.exec(`ALTER TABLE session_costs ADD COLUMN ${ddl}`);
|
|
3063
|
+
};
|
|
3064
|
+
addCostCol("cost_status", `cost_status TEXT NOT NULL DEFAULT 'unknown'`);
|
|
3065
|
+
addCostCol("unpriced_models", `unpriced_models TEXT NOT NULL DEFAULT ''`);
|
|
3066
|
+
addCostCol("priced_responses", `priced_responses INTEGER NOT NULL DEFAULT 0`);
|
|
3067
|
+
addCostCol("unpriced_responses", `unpriced_responses INTEGER NOT NULL DEFAULT 0`);
|
|
3068
|
+
addCostCol("pricing_version", `pricing_version INTEGER NOT NULL DEFAULT 0`);
|
|
3057
3069
|
db.exec(`
|
|
3058
3070
|
CREATE TABLE IF NOT EXISTS feature_costs (
|
|
3059
3071
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -3483,6 +3495,18 @@ function getMemoryMeta(db, key) {
|
|
|
3483
3495
|
function setMemoryMeta(db, key, value) {
|
|
3484
3496
|
db.prepare("INSERT OR REPLACE INTO memory_meta (key, value) VALUES (?, ?)").run(key, value);
|
|
3485
3497
|
}
|
|
3498
|
+
function tryAcquireMetaLease(db, key, ttlSec, nowSec = Math.floor(Date.now() / 1e3)) {
|
|
3499
|
+
const r3 = db.prepare(
|
|
3500
|
+
`INSERT INTO memory_meta (key, value) VALUES (?, ?)
|
|
3501
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value
|
|
3502
|
+
WHERE memory_meta.value = ''
|
|
3503
|
+
OR CAST(substr(memory_meta.value, instr(memory_meta.value, ':') + 1) AS INTEGER) <= ?`
|
|
3504
|
+
).run(key, `${process.pid}:${nowSec + ttlSec}`, nowSec);
|
|
3505
|
+
return Number(r3.changes) === 1;
|
|
3506
|
+
}
|
|
3507
|
+
function releaseMetaLease(db, key) {
|
|
3508
|
+
db.prepare(`UPDATE memory_meta SET value = '' WHERE key = ? AND value LIKE ?`).run(key, `${process.pid}:%`);
|
|
3509
|
+
}
|
|
3486
3510
|
function upsertObservationEmbedding(db, observationId, vec, modelId, dim) {
|
|
3487
3511
|
db.prepare(
|
|
3488
3512
|
`INSERT OR REPLACE INTO observation_embeddings (observation_id, model_id, dim, vec, created_at)
|
|
@@ -3524,7 +3548,7 @@ async function embedMissingObservations(db, opts = {}) {
|
|
|
3524
3548
|
)
|
|
3525
3549
|
ORDER BY o.id LIMIT ?`
|
|
3526
3550
|
).all(cursor, batchSize);
|
|
3527
|
-
return rows.map((
|
|
3551
|
+
return rows.map((r3) => ({ id: r3.id, text: observationEmbedText(r3.title, r3.detail) }));
|
|
3528
3552
|
}
|
|
3529
3553
|
},
|
|
3530
3554
|
opts
|
|
@@ -3612,9 +3636,9 @@ function rescueLearningFromStalePayloads(db, stale) {
|
|
|
3612
3636
|
rescued++;
|
|
3613
3637
|
}
|
|
3614
3638
|
const revocations = Array.isArray(payload.rule_revocations) ? payload.rule_revocations : [];
|
|
3615
|
-
for (const
|
|
3616
|
-
if (typeof
|
|
3617
|
-
enqueueTeamRevocation(db,
|
|
3639
|
+
for (const r3 of revocations) {
|
|
3640
|
+
if (typeof r3?.prompt_hash === "string") {
|
|
3641
|
+
enqueueTeamRevocation(db, r3.prompt_hash);
|
|
3618
3642
|
rescued++;
|
|
3619
3643
|
}
|
|
3620
3644
|
}
|
|
@@ -4351,7 +4375,7 @@ var init_memory_db = __esm({
|
|
|
4351
4375
|
init_rule_candidate_store();
|
|
4352
4376
|
init_hook_failure_signal();
|
|
4353
4377
|
init_memory_embed_sweep();
|
|
4354
|
-
MEMORY_SCHEMA_VERSION =
|
|
4378
|
+
MEMORY_SCHEMA_VERSION = 2;
|
|
4355
4379
|
TOOL_COST_EVENTS_RETENTION_DAYS = 90;
|
|
4356
4380
|
MAX_DRAFT_TEXT_LEN = 16384;
|
|
4357
4381
|
FUNNEL_EVENT_OUTBOX_CAP = 2e4;
|
|
@@ -5831,7 +5855,7 @@ var init_install_commands = __esm({
|
|
|
5831
5855
|
});
|
|
5832
5856
|
|
|
5833
5857
|
// src/lib/hook-timeouts.ts
|
|
5834
|
-
var HOOK_TIMEOUTS;
|
|
5858
|
+
var HOOK_TIMEOUTS, BACKGROUND_HOOKS;
|
|
5835
5859
|
var init_hook_timeouts = __esm({
|
|
5836
5860
|
"src/lib/hook-timeouts.ts"() {
|
|
5837
5861
|
"use strict";
|
|
@@ -5865,6 +5889,43 @@ var init_hook_timeouts = __esm({
|
|
|
5865
5889
|
// always fails open on its own terms before the platform kills it.
|
|
5866
5890
|
"memory-recall": 10
|
|
5867
5891
|
};
|
|
5892
|
+
BACKGROUND_HOOKS = /* @__PURE__ */ new Set(["session-end"]);
|
|
5893
|
+
}
|
|
5894
|
+
});
|
|
5895
|
+
|
|
5896
|
+
// src/lib/hook-registry.ts
|
|
5897
|
+
function getExpectedHookFiles() {
|
|
5898
|
+
return REGISTERED_HOOKS.map((name) => `${name}.js`);
|
|
5899
|
+
}
|
|
5900
|
+
var REGISTERED_HOOKS, DECISION_HOOKS;
|
|
5901
|
+
var init_hook_registry = __esm({
|
|
5902
|
+
"src/lib/hook-registry.ts"() {
|
|
5903
|
+
"use strict";
|
|
5904
|
+
REGISTERED_HOOKS = [
|
|
5905
|
+
"auto-learning-pipeline",
|
|
5906
|
+
"classify-failure",
|
|
5907
|
+
"cost-tracker",
|
|
5908
|
+
"fix-detector",
|
|
5909
|
+
"incident-pipeline",
|
|
5910
|
+
"intent-suggester",
|
|
5911
|
+
"memory-recall",
|
|
5912
|
+
"post-edit-context",
|
|
5913
|
+
"post-tool-use",
|
|
5914
|
+
"pre-compact",
|
|
5915
|
+
// P-E-019 (1.12.0): security-gate + pre-delete-check are kept registered
|
|
5916
|
+
// for back-compat (operator settings.json may still reference them
|
|
5917
|
+
// individually), but new installs emit only `pre-tool-use-gate` —
|
|
5918
|
+
// the consolidated single-spawn PreToolUse hook.
|
|
5919
|
+
"pre-delete-check",
|
|
5920
|
+
"pre-tool-use-gate",
|
|
5921
|
+
"quality-event",
|
|
5922
|
+
"rule-enforcement-pipeline",
|
|
5923
|
+
"security-gate",
|
|
5924
|
+
"session-end",
|
|
5925
|
+
"session-start",
|
|
5926
|
+
"user-prompt"
|
|
5927
|
+
];
|
|
5928
|
+
DECISION_HOOKS = ["pre-delete-check", "pre-tool-use-gate", "security-gate"];
|
|
5868
5929
|
}
|
|
5869
5930
|
});
|
|
5870
5931
|
|
|
@@ -6583,13 +6644,13 @@ var init_package_detector = __esm({
|
|
|
6583
6644
|
// src/detect/framework-detector.ts
|
|
6584
6645
|
function matchRule(rules, language, kind, deps) {
|
|
6585
6646
|
let best = null;
|
|
6586
|
-
for (const
|
|
6587
|
-
if (
|
|
6588
|
-
if (
|
|
6589
|
-
if (!deps.has(
|
|
6590
|
-
const pr =
|
|
6647
|
+
for (const r3 of rules) {
|
|
6648
|
+
if (r3.language !== language) continue;
|
|
6649
|
+
if (r3.kind !== kind) continue;
|
|
6650
|
+
if (!deps.has(r3.keyword.toLowerCase())) continue;
|
|
6651
|
+
const pr = r3.priority ?? 0;
|
|
6591
6652
|
if (!best || pr > best.priority) {
|
|
6592
|
-
best = { value:
|
|
6653
|
+
best = { value: r3.value, priority: pr };
|
|
6593
6654
|
}
|
|
6594
6655
|
}
|
|
6595
6656
|
return best;
|
|
@@ -15121,12 +15182,12 @@ async function introspectAsync(detection, projectRoot) {
|
|
|
15121
15182
|
}
|
|
15122
15183
|
return out;
|
|
15123
15184
|
}
|
|
15124
|
-
function serializeAdapterBlock(
|
|
15125
|
-
const block = { ...
|
|
15126
|
-
if (Object.keys(
|
|
15127
|
-
block._provenance =
|
|
15185
|
+
function serializeAdapterBlock(r3) {
|
|
15186
|
+
const block = { ...r3.conventions };
|
|
15187
|
+
if (Object.keys(r3._provenance).length > 0) {
|
|
15188
|
+
block._provenance = r3._provenance;
|
|
15128
15189
|
}
|
|
15129
|
-
block._confidence =
|
|
15190
|
+
block._confidence = r3.confidence;
|
|
15130
15191
|
return block;
|
|
15131
15192
|
}
|
|
15132
15193
|
var FIRST_PARTY_ADAPTERS;
|
|
@@ -16058,7 +16119,7 @@ var init_dist2 = __esm({
|
|
|
16058
16119
|
}
|
|
16059
16120
|
};
|
|
16060
16121
|
k2 = (s) => {
|
|
16061
|
-
const { cursor: n, options: t2, style: i } = s,
|
|
16122
|
+
const { cursor: n, options: t2, style: i } = s, r3 = s.maxItems ?? Number.POSITIVE_INFINITY, c2 = Math.max(process.stdout.rows - 4, 0), o = Math.min(c2, Math.max(r3, 5));
|
|
16062
16123
|
let l2 = 0;
|
|
16063
16124
|
n >= l2 + o - 3 ? l2 = Math.max(Math.min(n - o + 3, t2.length - o), 0) : n < l2 + 2 && (l2 = Math.max(n - 2, 0));
|
|
16064
16125
|
const $2 = o < t2.length && l2 > 0, d2 = o < t2.length && l2 + o < t2.length;
|
|
@@ -16114,12 +16175,12 @@ ${import_picocolors2.default.cyan(m2)}
|
|
|
16114
16175
|
return new fD({ active: n, inactive: t2, initialValue: s.initialValue ?? true, render() {
|
|
16115
16176
|
const i = `${import_picocolors2.default.gray(a)}
|
|
16116
16177
|
${y2(this.state)} ${s.message}
|
|
16117
|
-
`,
|
|
16178
|
+
`, r3 = this.value ? n : t2;
|
|
16118
16179
|
switch (this.state) {
|
|
16119
16180
|
case "submit":
|
|
16120
|
-
return `${i}${import_picocolors2.default.gray(a)} ${import_picocolors2.default.dim(
|
|
16181
|
+
return `${i}${import_picocolors2.default.gray(a)} ${import_picocolors2.default.dim(r3)}`;
|
|
16121
16182
|
case "cancel":
|
|
16122
|
-
return `${i}${import_picocolors2.default.gray(a)} ${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(
|
|
16183
|
+
return `${i}${import_picocolors2.default.gray(a)} ${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(r3))}
|
|
16123
16184
|
${import_picocolors2.default.gray(a)}`;
|
|
16124
16185
|
default:
|
|
16125
16186
|
return `${i}${import_picocolors2.default.cyan(a)} ${this.value ? `${import_picocolors2.default.green(j2)} ${n}` : `${import_picocolors2.default.dim(R2)} ${import_picocolors2.default.dim(n)}`} ${import_picocolors2.default.dim("/")} ${this.value ? `${import_picocolors2.default.dim(R2)} ${import_picocolors2.default.dim(t2)}` : `${import_picocolors2.default.green(j2)} ${t2}`}
|
|
@@ -16130,16 +16191,16 @@ ${import_picocolors2.default.cyan(m2)}
|
|
|
16130
16191
|
};
|
|
16131
16192
|
de = (s) => {
|
|
16132
16193
|
const n = (t2, i) => {
|
|
16133
|
-
const
|
|
16194
|
+
const r3 = t2.label ?? String(t2.value);
|
|
16134
16195
|
switch (i) {
|
|
16135
16196
|
case "selected":
|
|
16136
|
-
return `${import_picocolors2.default.dim(
|
|
16197
|
+
return `${import_picocolors2.default.dim(r3)}`;
|
|
16137
16198
|
case "active":
|
|
16138
|
-
return `${import_picocolors2.default.green(j2)} ${
|
|
16199
|
+
return `${import_picocolors2.default.green(j2)} ${r3} ${t2.hint ? import_picocolors2.default.dim(`(${t2.hint})`) : ""}`;
|
|
16139
16200
|
case "cancelled":
|
|
16140
|
-
return `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(
|
|
16201
|
+
return `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(r3))}`;
|
|
16141
16202
|
default:
|
|
16142
|
-
return `${import_picocolors2.default.dim(R2)} ${import_picocolors2.default.dim(
|
|
16203
|
+
return `${import_picocolors2.default.dim(R2)} ${import_picocolors2.default.dim(r3)}`;
|
|
16143
16204
|
}
|
|
16144
16205
|
};
|
|
16145
16206
|
return new jD({ options: s.options, initialValue: s.initialValue, render() {
|
|
@@ -16153,7 +16214,7 @@ ${y2(this.state)} ${s.message}
|
|
|
16153
16214
|
return `${t2}${import_picocolors2.default.gray(a)} ${n(this.options[this.cursor], "cancelled")}
|
|
16154
16215
|
${import_picocolors2.default.gray(a)}`;
|
|
16155
16216
|
default:
|
|
16156
|
-
return `${t2}${import_picocolors2.default.cyan(a)} ${k2({ cursor: this.cursor, options: this.options, maxItems: s.maxItems, style: (i,
|
|
16217
|
+
return `${t2}${import_picocolors2.default.cyan(a)} ${k2({ cursor: this.cursor, options: this.options, maxItems: s.maxItems, style: (i, r3) => n(i, r3 ? "active" : "inactive") }).join(`
|
|
16157
16218
|
${import_picocolors2.default.cyan(a)} `)}
|
|
16158
16219
|
${import_picocolors2.default.cyan(m2)}
|
|
16159
16220
|
`;
|
|
@@ -16162,8 +16223,8 @@ ${import_picocolors2.default.cyan(m2)}
|
|
|
16162
16223
|
};
|
|
16163
16224
|
he = (s) => {
|
|
16164
16225
|
const n = (t2, i = "inactive") => {
|
|
16165
|
-
const
|
|
16166
|
-
return i === "selected" ? `${import_picocolors2.default.dim(
|
|
16226
|
+
const r3 = t2.label ?? String(t2.value);
|
|
16227
|
+
return i === "selected" ? `${import_picocolors2.default.dim(r3)}` : i === "cancelled" ? `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(r3))}` : i === "active" ? `${import_picocolors2.default.bgCyan(import_picocolors2.default.gray(` ${t2.value} `))} ${r3} ${t2.hint ? import_picocolors2.default.dim(`(${t2.hint})`) : ""}` : `${import_picocolors2.default.gray(import_picocolors2.default.bgWhite(import_picocolors2.default.inverse(` ${t2.value} `)))} ${r3} ${t2.hint ? import_picocolors2.default.dim(`(${t2.hint})`) : ""}`;
|
|
16167
16228
|
};
|
|
16168
16229
|
return new OD({ options: s.options, initialValue: s.initialValue, render() {
|
|
16169
16230
|
const t2 = `${import_picocolors2.default.gray(a)}
|
|
@@ -16176,7 +16237,7 @@ ${y2(this.state)} ${s.message}
|
|
|
16176
16237
|
return `${t2}${import_picocolors2.default.gray(a)} ${n(this.options[0], "cancelled")}
|
|
16177
16238
|
${import_picocolors2.default.gray(a)}`;
|
|
16178
16239
|
default:
|
|
16179
|
-
return `${t2}${import_picocolors2.default.cyan(a)} ${this.options.map((i,
|
|
16240
|
+
return `${t2}${import_picocolors2.default.cyan(a)} ${this.options.map((i, r3) => n(i, r3 === this.cursor ? "active" : "inactive")).join(`
|
|
16180
16241
|
${import_picocolors2.default.cyan(a)} `)}
|
|
16181
16242
|
${import_picocolors2.default.cyan(m2)}
|
|
16182
16243
|
`;
|
|
@@ -16185,8 +16246,8 @@ ${import_picocolors2.default.cyan(m2)}
|
|
|
16185
16246
|
};
|
|
16186
16247
|
pe = (s) => {
|
|
16187
16248
|
const n = (t2, i) => {
|
|
16188
|
-
const
|
|
16189
|
-
return i === "active" ? `${import_picocolors2.default.cyan(V2)} ${
|
|
16249
|
+
const r3 = t2.label ?? String(t2.value);
|
|
16250
|
+
return i === "active" ? `${import_picocolors2.default.cyan(V2)} ${r3} ${t2.hint ? import_picocolors2.default.dim(`(${t2.hint})`) : ""}` : i === "selected" ? `${import_picocolors2.default.green(M2)} ${import_picocolors2.default.dim(r3)}` : i === "cancelled" ? `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(r3))}` : i === "active-selected" ? `${import_picocolors2.default.green(M2)} ${r3} ${t2.hint ? import_picocolors2.default.dim(`(${t2.hint})`) : ""}` : i === "submitted" ? `${import_picocolors2.default.dim(r3)}` : `${import_picocolors2.default.dim(G2)} ${import_picocolors2.default.dim(r3)}`;
|
|
16190
16251
|
};
|
|
16191
16252
|
return new wD({ options: s.options, initialValues: s.initialValues, required: s.required ?? true, cursorAt: s.cursorAt, validate(t2) {
|
|
16192
16253
|
if (this.required && t2.length === 0) return `Please select at least one option.
|
|
@@ -16194,25 +16255,25 @@ ${import_picocolors2.default.reset(import_picocolors2.default.dim(`Press ${impor
|
|
|
16194
16255
|
}, render() {
|
|
16195
16256
|
const t2 = `${import_picocolors2.default.gray(a)}
|
|
16196
16257
|
${y2(this.state)} ${s.message}
|
|
16197
|
-
`, i = (
|
|
16198
|
-
const o = this.value.includes(
|
|
16199
|
-
return c2 && o ? n(
|
|
16258
|
+
`, i = (r3, c2) => {
|
|
16259
|
+
const o = this.value.includes(r3.value);
|
|
16260
|
+
return c2 && o ? n(r3, "active-selected") : o ? n(r3, "selected") : n(r3, c2 ? "active" : "inactive");
|
|
16200
16261
|
};
|
|
16201
16262
|
switch (this.state) {
|
|
16202
16263
|
case "submit":
|
|
16203
|
-
return `${t2}${import_picocolors2.default.gray(a)} ${this.options.filter(({ value:
|
|
16264
|
+
return `${t2}${import_picocolors2.default.gray(a)} ${this.options.filter(({ value: r3 }) => this.value.includes(r3)).map((r3) => n(r3, "submitted")).join(import_picocolors2.default.dim(", ")) || import_picocolors2.default.dim("none")}`;
|
|
16204
16265
|
case "cancel": {
|
|
16205
|
-
const
|
|
16206
|
-
return `${t2}${import_picocolors2.default.gray(a)} ${
|
|
16266
|
+
const r3 = this.options.filter(({ value: c2 }) => this.value.includes(c2)).map((c2) => n(c2, "cancelled")).join(import_picocolors2.default.dim(", "));
|
|
16267
|
+
return `${t2}${import_picocolors2.default.gray(a)} ${r3.trim() ? `${r3}
|
|
16207
16268
|
${import_picocolors2.default.gray(a)}` : ""}`;
|
|
16208
16269
|
}
|
|
16209
16270
|
case "error": {
|
|
16210
|
-
const
|
|
16271
|
+
const r3 = this.error.split(`
|
|
16211
16272
|
`).map((c2, o) => o === 0 ? `${import_picocolors2.default.yellow(m2)} ${import_picocolors2.default.yellow(c2)}` : ` ${c2}`).join(`
|
|
16212
16273
|
`);
|
|
16213
16274
|
return `${t2 + import_picocolors2.default.yellow(a)} ${k2({ options: this.options, cursor: this.cursor, maxItems: s.maxItems, style: i }).join(`
|
|
16214
16275
|
${import_picocolors2.default.yellow(a)} `)}
|
|
16215
|
-
${
|
|
16276
|
+
${r3}
|
|
16216
16277
|
`;
|
|
16217
16278
|
}
|
|
16218
16279
|
default:
|
|
@@ -16224,8 +16285,8 @@ ${import_picocolors2.default.cyan(m2)}
|
|
|
16224
16285
|
} }).prompt();
|
|
16225
16286
|
};
|
|
16226
16287
|
ge = (s) => {
|
|
16227
|
-
const n = (t2, i,
|
|
16228
|
-
const c2 = t2.label ?? String(t2.value), o = typeof t2.group == "string", l2 = o && (
|
|
16288
|
+
const n = (t2, i, r3 = []) => {
|
|
16289
|
+
const c2 = t2.label ?? String(t2.value), o = typeof t2.group == "string", l2 = o && (r3[r3.indexOf(t2) + 1] ?? { group: true }), $2 = o && l2.group === true, d2 = o ? `${$2 ? m2 : a} ` : "";
|
|
16229
16290
|
return i === "active" ? `${import_picocolors2.default.dim(d2)}${import_picocolors2.default.cyan(V2)} ${c2} ${t2.hint ? import_picocolors2.default.dim(`(${t2.hint})`) : ""}` : i === "group-active" ? `${d2}${import_picocolors2.default.cyan(V2)} ${import_picocolors2.default.dim(c2)}` : i === "group-active-selected" ? `${d2}${import_picocolors2.default.green(M2)} ${import_picocolors2.default.dim(c2)}` : i === "selected" ? `${import_picocolors2.default.dim(d2)}${import_picocolors2.default.green(M2)} ${import_picocolors2.default.dim(c2)}` : i === "cancelled" ? `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(c2))}` : i === "active-selected" ? `${import_picocolors2.default.dim(d2)}${import_picocolors2.default.green(M2)} ${c2} ${t2.hint ? import_picocolors2.default.dim(`(${t2.hint})`) : ""}` : i === "submitted" ? `${import_picocolors2.default.dim(c2)}` : `${import_picocolors2.default.dim(d2)}${import_picocolors2.default.dim(G2)} ${import_picocolors2.default.dim(c2)}`;
|
|
16230
16291
|
};
|
|
16231
16292
|
return new dD({ options: s.options, initialValues: s.initialValues, required: s.required ?? true, cursorAt: s.cursorAt, validate(t2) {
|
|
@@ -16239,25 +16300,25 @@ ${y2(this.state)} ${s.message}
|
|
|
16239
16300
|
case "submit":
|
|
16240
16301
|
return `${t2}${import_picocolors2.default.gray(a)} ${this.options.filter(({ value: i }) => this.value.includes(i)).map((i) => n(i, "submitted")).join(import_picocolors2.default.dim(", "))}`;
|
|
16241
16302
|
case "cancel": {
|
|
16242
|
-
const i = this.options.filter(({ value:
|
|
16303
|
+
const i = this.options.filter(({ value: r3 }) => this.value.includes(r3)).map((r3) => n(r3, "cancelled")).join(import_picocolors2.default.dim(", "));
|
|
16243
16304
|
return `${t2}${import_picocolors2.default.gray(a)} ${i.trim() ? `${i}
|
|
16244
16305
|
${import_picocolors2.default.gray(a)}` : ""}`;
|
|
16245
16306
|
}
|
|
16246
16307
|
case "error": {
|
|
16247
16308
|
const i = this.error.split(`
|
|
16248
|
-
`).map((
|
|
16309
|
+
`).map((r3, c2) => c2 === 0 ? `${import_picocolors2.default.yellow(m2)} ${import_picocolors2.default.yellow(r3)}` : ` ${r3}`).join(`
|
|
16249
16310
|
`);
|
|
16250
|
-
return `${t2}${import_picocolors2.default.yellow(a)} ${this.options.map((
|
|
16251
|
-
const l2 = this.value.includes(
|
|
16252
|
-
return !$2 && typeof
|
|
16311
|
+
return `${t2}${import_picocolors2.default.yellow(a)} ${this.options.map((r3, c2, o) => {
|
|
16312
|
+
const l2 = this.value.includes(r3.value) || r3.group === true && this.isGroupSelected(`${r3.value}`), $2 = c2 === this.cursor;
|
|
16313
|
+
return !$2 && typeof r3.group == "string" && this.options[this.cursor].value === r3.group ? n(r3, l2 ? "group-active-selected" : "group-active", o) : $2 && l2 ? n(r3, "active-selected", o) : l2 ? n(r3, "selected", o) : n(r3, $2 ? "active" : "inactive", o);
|
|
16253
16314
|
}).join(`
|
|
16254
16315
|
${import_picocolors2.default.yellow(a)} `)}
|
|
16255
16316
|
${i}
|
|
16256
16317
|
`;
|
|
16257
16318
|
}
|
|
16258
16319
|
default:
|
|
16259
|
-
return `${t2}${import_picocolors2.default.cyan(a)} ${this.options.map((i,
|
|
16260
|
-
const o = this.value.includes(i.value) || i.group === true && this.isGroupSelected(`${i.value}`), l2 =
|
|
16320
|
+
return `${t2}${import_picocolors2.default.cyan(a)} ${this.options.map((i, r3, c2) => {
|
|
16321
|
+
const o = this.value.includes(i.value) || i.group === true && this.isGroupSelected(`${i.value}`), l2 = r3 === this.cursor;
|
|
16261
16322
|
return !l2 && typeof i.group == "string" && this.options[this.cursor].value === i.group ? n(i, o ? "group-active-selected" : "group-active", c2) : l2 && o ? n(i, "active-selected", c2) : o ? n(i, "selected", c2) : n(i, l2 ? "active" : "inactive", c2);
|
|
16262
16323
|
}).join(`
|
|
16263
16324
|
${import_picocolors2.default.cyan(a)} `)}
|
|
@@ -16270,15 +16331,15 @@ ${import_picocolors2.default.cyan(m2)}
|
|
|
16270
16331
|
const t2 = `
|
|
16271
16332
|
${s}
|
|
16272
16333
|
`.split(`
|
|
16273
|
-
`), i = T2(n).length,
|
|
16334
|
+
`), i = T2(n).length, r3 = Math.max(t2.reduce((o, l2) => {
|
|
16274
16335
|
const $2 = T2(l2);
|
|
16275
16336
|
return $2.length > o ? $2.length : o;
|
|
16276
|
-
}, 0), i) + 2, c2 = t2.map((o) => `${import_picocolors2.default.gray(a)} ${import_picocolors2.default.dim(o)}${" ".repeat(
|
|
16337
|
+
}, 0), i) + 2, c2 = t2.map((o) => `${import_picocolors2.default.gray(a)} ${import_picocolors2.default.dim(o)}${" ".repeat(r3 - T2(o).length)}${import_picocolors2.default.gray(a)}`).join(`
|
|
16277
16338
|
`);
|
|
16278
16339
|
process.stdout.write(`${import_picocolors2.default.gray(a)}
|
|
16279
|
-
${import_picocolors2.default.green(S2)} ${import_picocolors2.default.reset(n)} ${import_picocolors2.default.gray(N2.repeat(Math.max(
|
|
16340
|
+
${import_picocolors2.default.green(S2)} ${import_picocolors2.default.reset(n)} ${import_picocolors2.default.gray(N2.repeat(Math.max(r3 - i - 1, 1)) + re)}
|
|
16280
16341
|
${c2}
|
|
16281
|
-
${import_picocolors2.default.gray(ie + N2.repeat(
|
|
16342
|
+
${import_picocolors2.default.gray(ie + N2.repeat(r3 + 2) + ne)}
|
|
16282
16343
|
`);
|
|
16283
16344
|
};
|
|
16284
16345
|
ve = (s = "") => {
|
|
@@ -16299,9 +16360,9 @@ ${import_picocolors2.default.gray(m2)} ${s}
|
|
|
16299
16360
|
v2 = { message: (s = "", { symbol: n = import_picocolors2.default.gray(a) } = {}) => {
|
|
16300
16361
|
const t2 = [`${import_picocolors2.default.gray(a)}`];
|
|
16301
16362
|
if (s) {
|
|
16302
|
-
const [i, ...
|
|
16363
|
+
const [i, ...r3] = s.split(`
|
|
16303
16364
|
`);
|
|
16304
|
-
t2.push(`${n} ${i}`, ...
|
|
16365
|
+
t2.push(`${n} ${i}`, ...r3.map((c2) => `${import_picocolors2.default.gray(a)} ${c2}`));
|
|
16305
16366
|
}
|
|
16306
16367
|
process.stdout.write(`${t2.join(`
|
|
16307
16368
|
`)}
|
|
@@ -16321,7 +16382,7 @@ ${import_picocolors2.default.gray(m2)} ${s}
|
|
|
16321
16382
|
} };
|
|
16322
16383
|
L2 = () => {
|
|
16323
16384
|
const s = E ? ["\u25D2", "\u25D0", "\u25D3", "\u25D1"] : ["\u2022", "o", "O", "0"], n = E ? 80 : 120, t2 = process.env.CI === "true";
|
|
16324
|
-
let i,
|
|
16385
|
+
let i, r3, c2 = false, o = "", l2;
|
|
16325
16386
|
const $2 = (h2) => {
|
|
16326
16387
|
const g2 = h2 > 1 ? "Something went wrong" : "Canceled";
|
|
16327
16388
|
c2 && P2(g2, h2);
|
|
@@ -16340,14 +16401,14 @@ ${import_picocolors2.default.gray(m2)} ${s}
|
|
|
16340
16401
|
c2 = true, i = cD(), o = x2(h2), process.stdout.write(`${import_picocolors2.default.gray(a)}
|
|
16341
16402
|
`);
|
|
16342
16403
|
let g2 = 0, f2 = 0;
|
|
16343
|
-
b2(),
|
|
16404
|
+
b2(), r3 = setInterval(() => {
|
|
16344
16405
|
if (t2 && o === l2) return;
|
|
16345
16406
|
I2(), l2 = o;
|
|
16346
16407
|
const W2 = import_picocolors2.default.magenta(s[g2]), _2 = t2 ? "..." : ".".repeat(Math.floor(f2)).slice(0, 3);
|
|
16347
16408
|
process.stdout.write(`${W2} ${o}${_2}`), g2 = g2 + 1 < s.length ? g2 + 1 : 0, f2 = f2 < s.length ? f2 + 0.125 : 0;
|
|
16348
16409
|
}, n);
|
|
16349
16410
|
}, P2 = (h2 = "", g2 = 0) => {
|
|
16350
|
-
c2 = false, clearInterval(
|
|
16411
|
+
c2 = false, clearInterval(r3), I2();
|
|
16351
16412
|
const f2 = g2 === 0 ? import_picocolors2.default.green(S2) : g2 === 1 ? import_picocolors2.default.red(A2) : import_picocolors2.default.red(B);
|
|
16352
16413
|
o = x2(h2 ?? o), process.stdout.write(`${f2} ${o}
|
|
16353
16414
|
`), C(), i();
|
|
@@ -16358,15 +16419,15 @@ ${import_picocolors2.default.gray(m2)} ${s}
|
|
|
16358
16419
|
};
|
|
16359
16420
|
be = async (s, n) => {
|
|
16360
16421
|
const t2 = {}, i = Object.keys(s);
|
|
16361
|
-
for (const
|
|
16362
|
-
const c2 = s[
|
|
16422
|
+
for (const r3 of i) {
|
|
16423
|
+
const c2 = s[r3], o = await c2({ results: t2 })?.catch((l2) => {
|
|
16363
16424
|
throw l2;
|
|
16364
16425
|
});
|
|
16365
16426
|
if (typeof n?.onCancel == "function" && BD(o)) {
|
|
16366
|
-
t2[
|
|
16427
|
+
t2[r3] = "canceled", n.onCancel({ results: t2 });
|
|
16367
16428
|
continue;
|
|
16368
16429
|
}
|
|
16369
|
-
t2[
|
|
16430
|
+
t2[r3] = o;
|
|
16370
16431
|
}
|
|
16371
16432
|
return t2;
|
|
16372
16433
|
};
|
|
@@ -16901,10 +16962,10 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
|
16901
16962
|
}
|
|
16902
16963
|
const mRoots = cfg.paths.monorepo_roots;
|
|
16903
16964
|
if (Array.isArray(mRoots)) {
|
|
16904
|
-
for (const
|
|
16905
|
-
if (typeof
|
|
16906
|
-
if (!existsSync21(resolve12(projectRoot,
|
|
16907
|
-
return `paths.monorepo_roots '${
|
|
16965
|
+
for (const r3 of mRoots) {
|
|
16966
|
+
if (typeof r3 !== "string" || r3 === ".") continue;
|
|
16967
|
+
if (!existsSync21(resolve12(projectRoot, r3))) {
|
|
16968
|
+
return `paths.monorepo_roots '${r3}' does not exist on disk`;
|
|
16908
16969
|
}
|
|
16909
16970
|
}
|
|
16910
16971
|
}
|
|
@@ -17077,6 +17138,22 @@ fi
|
|
|
17077
17138
|
MASSU_VERSION="$1"
|
|
17078
17139
|
shift
|
|
17079
17140
|
MASSU_CLI="\${HOME}/.massu/runtime/\${MASSU_VERSION}/node_modules/@massu/core/dist/cli.js"
|
|
17141
|
+
# A DECIDING hook's exit code is its verdict: 0 allows, 2 denies. If the launch itself fails
|
|
17142
|
+
# (a runtime that cannot load exits 1), Claude Code would RUN the call, so for these hooks
|
|
17143
|
+
# any other status becomes a deny. Every other subcommand keeps the exec path below.
|
|
17144
|
+
if [ "\${1:-}" = hook-runner ]; then
|
|
17145
|
+
case "\${2:-}" in
|
|
17146
|
+
${DECISION_HOOKS.join("|")})
|
|
17147
|
+
if [ -f "$MASSU_CLI" ]; then node "$MASSU_CLI" "$@"; else npx -y "@massu/core@\${MASSU_VERSION}" "$@"; fi
|
|
17148
|
+
st=$?
|
|
17149
|
+
if [ "$st" -ne 0 ] && [ "$st" -ne 2 ]; then
|
|
17150
|
+
echo "MASSU SECURITY GATE \u2014 BLOCKED: the $2 hook ended without a verdict (exit $st)" >&2
|
|
17151
|
+
exit 2
|
|
17152
|
+
fi
|
|
17153
|
+
exit "$st"
|
|
17154
|
+
;;
|
|
17155
|
+
esac
|
|
17156
|
+
fi
|
|
17080
17157
|
if [ -f "$MASSU_CLI" ]; then
|
|
17081
17158
|
exec node "$MASSU_CLI" "$@"
|
|
17082
17159
|
fi
|
|
@@ -17121,13 +17198,19 @@ function hookCmd(version, hookName, shim) {
|
|
|
17121
17198
|
function getHookTimeout(name) {
|
|
17122
17199
|
return HOOK_TIMEOUTS[name] ?? 5;
|
|
17123
17200
|
}
|
|
17201
|
+
function hookEntry(version, name, shim) {
|
|
17202
|
+
const entry = { type: "command", command: hookCmd(version, name, shim), timeout: getHookTimeout(name) };
|
|
17203
|
+
if (BACKGROUND_HOOKS.has(name)) entry.async = true;
|
|
17204
|
+
if (DECISION_HOOKS.includes(name)) entry.onFailure = "block";
|
|
17205
|
+
return entry;
|
|
17206
|
+
}
|
|
17124
17207
|
function buildHooksConfig(_hooksDir, shim = resolveMassuShim()) {
|
|
17125
17208
|
const version = getInstallerVersion();
|
|
17126
17209
|
return {
|
|
17127
17210
|
SessionStart: [
|
|
17128
17211
|
{
|
|
17129
17212
|
hooks: [
|
|
17130
|
-
|
|
17213
|
+
hookEntry(version, "session-start", shim)
|
|
17131
17214
|
]
|
|
17132
17215
|
}
|
|
17133
17216
|
],
|
|
@@ -17142,26 +17225,26 @@ function buildHooksConfig(_hooksDir, shim = resolveMassuShim()) {
|
|
|
17142
17225
|
{
|
|
17143
17226
|
matcher: "Bash|Write|Edit",
|
|
17144
17227
|
hooks: [
|
|
17145
|
-
|
|
17228
|
+
hookEntry(version, "pre-tool-use-gate", shim)
|
|
17146
17229
|
]
|
|
17147
17230
|
}
|
|
17148
17231
|
],
|
|
17149
17232
|
PostToolUse: [
|
|
17150
17233
|
{
|
|
17151
17234
|
hooks: [
|
|
17152
|
-
|
|
17153
|
-
|
|
17154
|
-
|
|
17235
|
+
hookEntry(version, "post-tool-use", shim),
|
|
17236
|
+
hookEntry(version, "quality-event", shim),
|
|
17237
|
+
hookEntry(version, "cost-tracker", shim)
|
|
17155
17238
|
]
|
|
17156
17239
|
},
|
|
17157
17240
|
{
|
|
17158
17241
|
matcher: "Edit|Write",
|
|
17159
17242
|
hooks: [
|
|
17160
|
-
|
|
17243
|
+
hookEntry(version, "post-edit-context", shim),
|
|
17161
17244
|
// Auto-learning pipeline — classifies failures and detects fixes on
|
|
17162
17245
|
// file changes. See Phase 5-6 of the autodetect plan.
|
|
17163
|
-
|
|
17164
|
-
|
|
17246
|
+
hookEntry(version, "fix-detector", shim),
|
|
17247
|
+
hookEntry(version, "classify-failure", shim)
|
|
17165
17248
|
]
|
|
17166
17249
|
},
|
|
17167
17250
|
{
|
|
@@ -17169,35 +17252,35 @@ function buildHooksConfig(_hooksDir, shim = resolveMassuShim()) {
|
|
|
17169
17252
|
hooks: [
|
|
17170
17253
|
// Incident + rule enforcement pipelines fire on Write-only (incidents
|
|
17171
17254
|
// are authored as .md files; rules are enforced after new-file drops).
|
|
17172
|
-
|
|
17173
|
-
|
|
17255
|
+
hookEntry(version, "incident-pipeline", shim),
|
|
17256
|
+
hookEntry(version, "rule-enforcement-pipeline", shim)
|
|
17174
17257
|
]
|
|
17175
17258
|
}
|
|
17176
17259
|
],
|
|
17177
17260
|
Stop: [
|
|
17178
17261
|
{
|
|
17179
17262
|
hooks: [
|
|
17180
|
-
|
|
17263
|
+
hookEntry(version, "session-end", shim),
|
|
17181
17264
|
// Session-end auto-learning aggregation (failure-class roll-up).
|
|
17182
|
-
|
|
17265
|
+
hookEntry(version, "auto-learning-pipeline", shim)
|
|
17183
17266
|
]
|
|
17184
17267
|
}
|
|
17185
17268
|
],
|
|
17186
17269
|
PreCompact: [
|
|
17187
17270
|
{
|
|
17188
17271
|
hooks: [
|
|
17189
|
-
|
|
17272
|
+
hookEntry(version, "pre-compact", shim)
|
|
17190
17273
|
]
|
|
17191
17274
|
}
|
|
17192
17275
|
],
|
|
17193
17276
|
UserPromptSubmit: [
|
|
17194
17277
|
{
|
|
17195
17278
|
hooks: [
|
|
17196
|
-
|
|
17197
|
-
|
|
17279
|
+
hookEntry(version, "user-prompt", shim),
|
|
17280
|
+
hookEntry(version, "intent-suggester", shim),
|
|
17198
17281
|
// plan-living-memory-slice-1 P3-002: automatic relevant-recall.
|
|
17199
17282
|
// Injects a compact "🧠 Relevant memory" block; fails open (empty).
|
|
17200
|
-
|
|
17283
|
+
hookEntry(version, "memory-recall", shim)
|
|
17201
17284
|
]
|
|
17202
17285
|
}
|
|
17203
17286
|
]
|
|
@@ -17675,6 +17758,7 @@ var init_init = __esm({
|
|
|
17675
17758
|
init_settings_local();
|
|
17676
17759
|
init_memory_path();
|
|
17677
17760
|
init_hook_timeouts();
|
|
17761
|
+
init_hook_registry();
|
|
17678
17762
|
init_detect();
|
|
17679
17763
|
init_drift();
|
|
17680
17764
|
__filename2 = fileURLToPath3(import.meta.url);
|
|
@@ -18261,41 +18345,6 @@ var init_license = __esm({
|
|
|
18261
18345
|
}
|
|
18262
18346
|
});
|
|
18263
18347
|
|
|
18264
|
-
// src/lib/hook-registry.ts
|
|
18265
|
-
function getExpectedHookFiles() {
|
|
18266
|
-
return REGISTERED_HOOKS.map((name) => `${name}.js`);
|
|
18267
|
-
}
|
|
18268
|
-
var REGISTERED_HOOKS;
|
|
18269
|
-
var init_hook_registry = __esm({
|
|
18270
|
-
"src/lib/hook-registry.ts"() {
|
|
18271
|
-
"use strict";
|
|
18272
|
-
REGISTERED_HOOKS = [
|
|
18273
|
-
"auto-learning-pipeline",
|
|
18274
|
-
"classify-failure",
|
|
18275
|
-
"cost-tracker",
|
|
18276
|
-
"fix-detector",
|
|
18277
|
-
"incident-pipeline",
|
|
18278
|
-
"intent-suggester",
|
|
18279
|
-
"memory-recall",
|
|
18280
|
-
"post-edit-context",
|
|
18281
|
-
"post-tool-use",
|
|
18282
|
-
"pre-compact",
|
|
18283
|
-
// P-E-019 (1.12.0): security-gate + pre-delete-check are kept registered
|
|
18284
|
-
// for back-compat (operator settings.json may still reference them
|
|
18285
|
-
// individually), but new installs emit only `pre-tool-use-gate` —
|
|
18286
|
-
// the consolidated single-spawn PreToolUse hook.
|
|
18287
|
-
"pre-delete-check",
|
|
18288
|
-
"pre-tool-use-gate",
|
|
18289
|
-
"quality-event",
|
|
18290
|
-
"rule-enforcement-pipeline",
|
|
18291
|
-
"security-gate",
|
|
18292
|
-
"session-end",
|
|
18293
|
-
"session-start",
|
|
18294
|
-
"user-prompt"
|
|
18295
|
-
];
|
|
18296
|
-
}
|
|
18297
|
-
});
|
|
18298
|
-
|
|
18299
18348
|
// src/preflight.ts
|
|
18300
18349
|
import { existsSync as existsSync22 } from "fs";
|
|
18301
18350
|
function assertCodegraphUsable(dbPath) {
|
|
@@ -18382,6 +18431,7 @@ var init_preflight = __esm({
|
|
|
18382
18431
|
var hook_runner_exports = {};
|
|
18383
18432
|
__export(hook_runner_exports, {
|
|
18384
18433
|
HOOK_NAME_TO_FILE: () => HOOK_NAME_TO_FILE,
|
|
18434
|
+
decisionExitCode: () => decisionExitCode,
|
|
18385
18435
|
resolveHookFile: () => resolveHookFile,
|
|
18386
18436
|
runHookRunner: () => runHookRunner
|
|
18387
18437
|
});
|
|
@@ -18413,6 +18463,16 @@ function resolveHookFile(hookName) {
|
|
|
18413
18463
|
`Hook file not found for "${hookName}". Searched: ${candidates.join(", ")}. This indicates a broken @massu/core install. Re-run \`npx -y @massu/core init\`.`
|
|
18414
18464
|
);
|
|
18415
18465
|
}
|
|
18466
|
+
function decisionExitCode(hookName, code) {
|
|
18467
|
+
if (!DECISION_HOOKS.includes(hookName) || code === 0 || code === 2) return code;
|
|
18468
|
+
process.stderr.write(
|
|
18469
|
+
`MASSU SECURITY GATE \u2014 BLOCKED
|
|
18470
|
+
|
|
18471
|
+
The ${hookName} hook ended without a verdict (exit ${code}). A gate that did not decide has not approved this call.
|
|
18472
|
+
`
|
|
18473
|
+
);
|
|
18474
|
+
return 2;
|
|
18475
|
+
}
|
|
18416
18476
|
async function runHookRunner(args2) {
|
|
18417
18477
|
const hookName = args2[0];
|
|
18418
18478
|
if (!hookName) {
|
|
@@ -18441,11 +18501,7 @@ Recognized: ${Object.keys(HOOK_NAME_TO_FILE).join(", ")}
|
|
|
18441
18501
|
env: { ...process.env, MASSU_HOOK_RUNTIME: "1" }
|
|
18442
18502
|
});
|
|
18443
18503
|
child.on("exit", (code, signal) => {
|
|
18444
|
-
|
|
18445
|
-
resolvePromise({ exitCode: 128 });
|
|
18446
|
-
return;
|
|
18447
|
-
}
|
|
18448
|
-
resolvePromise({ exitCode: code ?? 0 });
|
|
18504
|
+
resolvePromise({ exitCode: decisionExitCode(hookName, signal ? 128 : code ?? 0) });
|
|
18449
18505
|
});
|
|
18450
18506
|
child.on("error", (err) => {
|
|
18451
18507
|
process.stderr.write(`massu hook-runner: failed to spawn hook "${hookName}": ${err.message}
|
|
@@ -20165,7 +20221,7 @@ async function embedMissingChunks(db, opts = {}) {
|
|
|
20165
20221
|
)
|
|
20166
20222
|
ORDER BY c.id LIMIT ?`
|
|
20167
20223
|
).all(cursor, batchSize);
|
|
20168
|
-
return rows.map((
|
|
20224
|
+
return rows.map((r3) => ({ id: r3.id, text: chunkEmbedText(r3.heading, r3.content) }));
|
|
20169
20225
|
}
|
|
20170
20226
|
},
|
|
20171
20227
|
opts
|
|
@@ -21285,7 +21341,7 @@ function buildIndexLines(db, memoryDir, planned, config) {
|
|
|
21285
21341
|
ORDER BY o.importance DESC, o.created_at_epoch DESC
|
|
21286
21342
|
LIMIT ?`
|
|
21287
21343
|
).all(config.indexMaxLines + planned.length);
|
|
21288
|
-
const byPath = new Map(rows.map((
|
|
21344
|
+
const byPath = new Map(rows.map((r3) => [r3.rel_path.toLowerCase(), r3]));
|
|
21289
21345
|
for (const p19 of planned) {
|
|
21290
21346
|
byPath.set(p19.relPath.toLowerCase(), {
|
|
21291
21347
|
rel_path: p19.relPath,
|
|
@@ -21294,11 +21350,11 @@ function buildIndexLines(db, memoryDir, planned, config) {
|
|
|
21294
21350
|
created_at_epoch: 0
|
|
21295
21351
|
});
|
|
21296
21352
|
}
|
|
21297
|
-
const live = [...byPath.values()].filter((
|
|
21353
|
+
const live = [...byPath.values()].filter((r3) => !isTombstoned(memoryDir, r3.rel_path));
|
|
21298
21354
|
const ranked = live.sort(
|
|
21299
21355
|
(a2, b2) => b2.importance - a2.importance || b2.created_at_epoch - a2.created_at_epoch
|
|
21300
21356
|
);
|
|
21301
|
-
return ranked.slice(0, config.indexMaxLines).map((
|
|
21357
|
+
return ranked.slice(0, config.indexMaxLines).map((r3) => `- [${oneLine(r3.name)}](${r3.rel_path}) \u2014 learned by Massu`);
|
|
21302
21358
|
}
|
|
21303
21359
|
function oneLine(s) {
|
|
21304
21360
|
return s.replace(/[\r\n]+/g, " ").slice(0, 120);
|
|
@@ -21494,14 +21550,14 @@ function memoryRestore(memoryDir, opts = {}) {
|
|
|
21494
21550
|
return { exitCode: 1, output: "No backups exist yet. Nothing to restore." };
|
|
21495
21551
|
}
|
|
21496
21552
|
try {
|
|
21497
|
-
const
|
|
21553
|
+
const r3 = restoreBackup(memoryDir, { ...opts, home });
|
|
21498
21554
|
const lines = [
|
|
21499
|
-
|
|
21555
|
+
r3.dryRun ? `--dry-run: would restore ${r3.wouldRestore.length} file(s) from ${r3.stamp}. Nothing changed.` : `Restored ${r3.filesRestored} file(s) from ${r3.stamp}.`
|
|
21500
21556
|
];
|
|
21501
|
-
for (const f2 of
|
|
21502
|
-
if (
|
|
21557
|
+
for (const f2 of r3.wouldRestore) lines.push(` ${f2}`);
|
|
21558
|
+
if (r3.presentButNotInBackup.length > 0) {
|
|
21503
21559
|
lines.push("", "These files exist now but are NOT in the backup. Massu left them alone:");
|
|
21504
|
-
for (const f2 of
|
|
21560
|
+
for (const f2 of r3.presentButNotInBackup) lines.push(` ${f2}`);
|
|
21505
21561
|
}
|
|
21506
21562
|
return { exitCode: 0, output: lines.join("\n") };
|
|
21507
21563
|
} catch (err) {
|
|
@@ -21535,8 +21591,8 @@ function loadRenderCandidates(db) {
|
|
|
21535
21591
|
ORDER BY importance DESC, created_at_epoch DESC
|
|
21536
21592
|
LIMIT ${CANDIDATE_WINDOW}`
|
|
21537
21593
|
).all(cfg.renderMinImportance, ...RENDERABLE_MEMORY_TYPES);
|
|
21538
|
-
const reingest = rows.filter((
|
|
21539
|
-
const kept = rows.filter((
|
|
21594
|
+
const reingest = rows.filter((r3) => r3.title.startsWith("[memory-file]"));
|
|
21595
|
+
const kept = rows.filter((r3) => !r3.title.startsWith("[memory-file]"));
|
|
21540
21596
|
const excluded = reingest.length ? [{ reason: EXCLUSION_MEMORY_FILE_REINGEST, count: reingest.length }] : [];
|
|
21541
21597
|
const ledger = {
|
|
21542
21598
|
population,
|
|
@@ -21553,13 +21609,13 @@ function loadRenderCandidates(db) {
|
|
|
21553
21609
|
}
|
|
21554
21610
|
return {
|
|
21555
21611
|
ledger,
|
|
21556
|
-
candidates: kept.map((
|
|
21557
|
-
observationId:
|
|
21558
|
-
name:
|
|
21559
|
-
title:
|
|
21560
|
-
body:
|
|
21561
|
-
importance:
|
|
21562
|
-
origin:
|
|
21612
|
+
candidates: kept.map((r3) => ({
|
|
21613
|
+
observationId: r3.id,
|
|
21614
|
+
name: r3.title,
|
|
21615
|
+
title: r3.title,
|
|
21616
|
+
body: r3.detail ?? "",
|
|
21617
|
+
importance: r3.importance,
|
|
21618
|
+
origin: r3.origin
|
|
21563
21619
|
}))
|
|
21564
21620
|
};
|
|
21565
21621
|
}
|
|
@@ -21742,8 +21798,8 @@ async function runMemoryRenderCli(sub, args2) {
|
|
|
21742
21798
|
}
|
|
21743
21799
|
if (result.refusals.length > 0) {
|
|
21744
21800
|
lines.push("", `${result.refusals.length} memory/memories REFUSED:`);
|
|
21745
|
-
for (const
|
|
21746
|
-
lines.push(` ${
|
|
21801
|
+
for (const r3 of result.refusals) {
|
|
21802
|
+
lines.push(` ${r3.name}: ${r3.reason}${r3.detail ? ` (${r3.detail})` : ""}`);
|
|
21747
21803
|
}
|
|
21748
21804
|
}
|
|
21749
21805
|
lines.push("", `Bytes written: ${result.bytesWritten} (a dry run always writes 0).`);
|
|
@@ -21765,7 +21821,7 @@ async function runMemoryRenderCli(sub, args2) {
|
|
|
21765
21821
|
"",
|
|
21766
21822
|
"Sample (first 10):"
|
|
21767
21823
|
];
|
|
21768
|
-
for (const
|
|
21824
|
+
for (const r3 of sampleNoise2(db, 10)) lines.push(` [${r3.reason}] id=${r3.id} ${r3.type}: ${r3.title.slice(0, 60)}`);
|
|
21769
21825
|
if (apply) {
|
|
21770
21826
|
const result = pruneNoiseObservations2(db, { dryRun: false });
|
|
21771
21827
|
lines.push("", `Expired ${result.expired} row(s) (CR-61: expired, NOT deleted; still asOf-queryable).`);
|
|
@@ -21859,12 +21915,12 @@ function readReposRegistry(home = homedir13()) {
|
|
|
21859
21915
|
try {
|
|
21860
21916
|
const parsed = JSON.parse(readFileSync29(p19, "utf-8"));
|
|
21861
21917
|
if (!parsed || typeof parsed !== "object") return emptyRegistry();
|
|
21862
|
-
const
|
|
21863
|
-
if (!Array.isArray(
|
|
21864
|
-
const repos =
|
|
21918
|
+
const r3 = parsed;
|
|
21919
|
+
if (!Array.isArray(r3.repos)) return emptyRegistry();
|
|
21920
|
+
const repos = r3.repos.filter(
|
|
21865
21921
|
(e2) => !!e2 && typeof e2 === "object" && typeof e2.repo_id === "string" && typeof e2.label === "string"
|
|
21866
21922
|
);
|
|
21867
|
-
return { version: typeof
|
|
21923
|
+
return { version: typeof r3.version === "number" ? r3.version : REPOS_REGISTRY_VERSION, repos };
|
|
21868
21924
|
} catch {
|
|
21869
21925
|
return emptyRegistry();
|
|
21870
21926
|
}
|
|
@@ -22038,7 +22094,7 @@ function acceptSharedMemory(db, recordHash, opts = {}) {
|
|
|
22038
22094
|
const verdict = verifyLocalShareEnvelope(env, pinned, home);
|
|
22039
22095
|
if (verdict.kind !== "valid") return refuse(db, recordHash, `reverify_${verdict.kind}`);
|
|
22040
22096
|
if (!hasSignedLoadBearingKeys(env)) return refuse(db, recordHash, "unsigned_field");
|
|
22041
|
-
const signed = parseRecords(env.records_json).find((
|
|
22097
|
+
const signed = parseRecords(env.records_json).find((r3) => r3.record_hash === recordHash);
|
|
22042
22098
|
if (!signed) return refuse(db, recordHash, "not_in_signed_records");
|
|
22043
22099
|
if (hashSharedMemoryRecord(stripHash(signed)) !== recordHash) {
|
|
22044
22100
|
return refuse(db, recordHash, "record_hash_mismatch");
|
|
@@ -22134,20 +22190,20 @@ function listPendingSharedMemories(db, opts = {}) {
|
|
|
22134
22190
|
LIMIT 500`
|
|
22135
22191
|
).all();
|
|
22136
22192
|
const out = [];
|
|
22137
|
-
for (const
|
|
22193
|
+
for (const r3 of rows) {
|
|
22138
22194
|
let rec = null;
|
|
22139
22195
|
let valid = false;
|
|
22140
22196
|
try {
|
|
22141
|
-
rec = JSON.parse(
|
|
22142
|
-
const env = JSON.parse(
|
|
22143
|
-
const pinned = getSharedPin(db,
|
|
22197
|
+
rec = JSON.parse(r3.record_json);
|
|
22198
|
+
const env = JSON.parse(r3.envelope_raw);
|
|
22199
|
+
const pinned = getSharedPin(db, r3.origin_repo_id);
|
|
22144
22200
|
valid = !!pinned && verifyLocalShareEnvelope(env, pinned, home).kind === "valid";
|
|
22145
22201
|
} catch {
|
|
22146
22202
|
}
|
|
22147
22203
|
out.push({
|
|
22148
|
-
record_hash:
|
|
22149
|
-
origin_repo_label: deriveRepoLabel(
|
|
22150
|
-
received_at_epoch:
|
|
22204
|
+
record_hash: r3.record_hash,
|
|
22205
|
+
origin_repo_label: deriveRepoLabel(r3.origin_repo_label),
|
|
22206
|
+
received_at_epoch: r3.received_at_epoch,
|
|
22151
22207
|
type: rec?.type ?? "unknown",
|
|
22152
22208
|
title: sanitizeCrossRepoTitle(rec?.title ?? ""),
|
|
22153
22209
|
detail: sanitizeCrossRepoBody(rec?.detail ?? ""),
|
|
@@ -22168,14 +22224,14 @@ function parseRecords(json) {
|
|
|
22168
22224
|
return [];
|
|
22169
22225
|
}
|
|
22170
22226
|
}
|
|
22171
|
-
function stripHash(
|
|
22227
|
+
function stripHash(r3) {
|
|
22172
22228
|
return {
|
|
22173
|
-
type:
|
|
22174
|
-
title:
|
|
22175
|
-
detail:
|
|
22176
|
-
importance:
|
|
22177
|
-
created_at_epoch:
|
|
22178
|
-
superseded_by_hash:
|
|
22229
|
+
type: r3.type,
|
|
22230
|
+
title: r3.title,
|
|
22231
|
+
detail: r3.detail,
|
|
22232
|
+
importance: r3.importance,
|
|
22233
|
+
created_at_epoch: r3.created_at_epoch,
|
|
22234
|
+
superseded_by_hash: r3.superseded_by_hash
|
|
22179
22235
|
};
|
|
22180
22236
|
}
|
|
22181
22237
|
function clampImportance(n) {
|
|
@@ -22902,40 +22958,40 @@ function validateCandidatePayload(raw) {
|
|
|
22902
22958
|
if (!raw || typeof raw !== "object") {
|
|
22903
22959
|
throw new CandidatePayloadValidationError("payload is not an object");
|
|
22904
22960
|
}
|
|
22905
|
-
const
|
|
22906
|
-
if (typeof
|
|
22961
|
+
const r3 = raw;
|
|
22962
|
+
if (typeof r3.prompt !== "string" || r3.prompt.length === 0) {
|
|
22907
22963
|
throw new CandidatePayloadValidationError("prompt must be a non-empty string");
|
|
22908
22964
|
}
|
|
22909
|
-
if (typeof
|
|
22965
|
+
if (typeof r3.prompt_hash !== "string" || !/^[a-f0-9]{16}$/.test(r3.prompt_hash)) {
|
|
22910
22966
|
throw new CandidatePayloadValidationError("prompt_hash must be 16 hex chars");
|
|
22911
22967
|
}
|
|
22912
|
-
if (typeof
|
|
22968
|
+
if (typeof r3.score !== "number" || r3.score < -200 || r3.score > 200 || !Number.isFinite(r3.score)) {
|
|
22913
22969
|
throw new CandidatePayloadValidationError("score must be a finite number in [-200, 200]");
|
|
22914
22970
|
}
|
|
22915
|
-
if (!Array.isArray(
|
|
22971
|
+
if (!Array.isArray(r3.signals)) {
|
|
22916
22972
|
throw new CandidatePayloadValidationError("signals must be an array");
|
|
22917
22973
|
}
|
|
22918
|
-
for (const s of
|
|
22974
|
+
for (const s of r3.signals) {
|
|
22919
22975
|
if (!s || typeof s !== "object") throw new CandidatePayloadValidationError("signal entry not an object");
|
|
22920
22976
|
const sig = s;
|
|
22921
22977
|
if (typeof sig.name !== "string") throw new CandidatePayloadValidationError("signal.name must be a string");
|
|
22922
22978
|
if (typeof sig.baseWeight !== "number") throw new CandidatePayloadValidationError("signal.baseWeight must be a number");
|
|
22923
22979
|
if (typeof sig.applied !== "number") throw new CandidatePayloadValidationError("signal.applied must be a number");
|
|
22924
22980
|
}
|
|
22925
|
-
if (!Array.isArray(
|
|
22981
|
+
if (!Array.isArray(r3.prior_turn_files)) {
|
|
22926
22982
|
throw new CandidatePayloadValidationError("prior_turn_files must be an array");
|
|
22927
22983
|
}
|
|
22928
|
-
if (typeof
|
|
22984
|
+
if (typeof r3.timestamp !== "string") {
|
|
22929
22985
|
throw new CandidatePayloadValidationError("timestamp must be a string");
|
|
22930
22986
|
}
|
|
22931
|
-
if (typeof
|
|
22987
|
+
if (typeof r3.session_id !== "string") {
|
|
22932
22988
|
throw new CandidatePayloadValidationError("session_id must be a string");
|
|
22933
22989
|
}
|
|
22934
|
-
if (
|
|
22935
|
-
if (!
|
|
22990
|
+
if (r3.provenance !== void 0) {
|
|
22991
|
+
if (!r3.provenance || typeof r3.provenance !== "object") {
|
|
22936
22992
|
throw new CandidatePayloadValidationError("provenance must be an object when present");
|
|
22937
22993
|
}
|
|
22938
|
-
const p19 =
|
|
22994
|
+
const p19 = r3.provenance;
|
|
22939
22995
|
const origin = p19.origin;
|
|
22940
22996
|
const isTeamOrPack = origin === "team" || origin === "pack";
|
|
22941
22997
|
const isRepo = typeof origin === "string" && isCrossRepoOrigin(origin);
|
|
@@ -22960,17 +23016,17 @@ function validateCandidatePayload(raw) {
|
|
|
22960
23016
|
if (p19.apply_mac !== void 0 && typeof p19.apply_mac !== "string") {
|
|
22961
23017
|
throw new CandidatePayloadValidationError("provenance.apply_mac, when present, must be a string");
|
|
22962
23018
|
}
|
|
22963
|
-
if (!isRuleDestination(
|
|
23019
|
+
if (!isRuleDestination(r3.destination)) {
|
|
22964
23020
|
throw new CandidatePayloadValidationError("provenance-bearing candidate must declare a valid destination");
|
|
22965
23021
|
}
|
|
22966
|
-
if (
|
|
23022
|
+
if (r3.draft_text !== void 0 && typeof r3.draft_text !== "string") {
|
|
22967
23023
|
throw new CandidatePayloadValidationError("draft_text must be a string when present");
|
|
22968
23024
|
}
|
|
22969
23025
|
}
|
|
22970
|
-
} else if (
|
|
23026
|
+
} else if (r3.destination !== void 0 && !isRuleDestination(r3.destination)) {
|
|
22971
23027
|
throw new CandidatePayloadValidationError("destination, when present, must be a valid RuleDestination");
|
|
22972
23028
|
}
|
|
22973
|
-
return
|
|
23029
|
+
return r3;
|
|
22974
23030
|
}
|
|
22975
23031
|
async function applyRuleCandidate(db, opts) {
|
|
22976
23032
|
const ent = await assertAutoLearningEntitled();
|
|
@@ -23325,8 +23381,8 @@ function dismissRuleCandidate(db, opts) {
|
|
|
23325
23381
|
`);
|
|
23326
23382
|
for (const sig of candidate.signals) {
|
|
23327
23383
|
if (sig.baseWeight <= 0) continue;
|
|
23328
|
-
const
|
|
23329
|
-
blacklistUpdates +=
|
|
23384
|
+
const r3 = upsert.run(sig.name);
|
|
23385
|
+
blacklistUpdates += r3.changes;
|
|
23330
23386
|
}
|
|
23331
23387
|
const entry = {
|
|
23332
23388
|
eventType: "rule_dismissed",
|
|
@@ -23758,27 +23814,27 @@ function validateRulePackRule(rule) {
|
|
|
23758
23814
|
if (typeof rule !== "object" || rule === null || Array.isArray(rule)) {
|
|
23759
23815
|
return { valid: false, errors: ["rule must be a non-null object"], requiresHardened: false };
|
|
23760
23816
|
}
|
|
23761
|
-
const
|
|
23762
|
-
if (!isNonEmptyString(
|
|
23817
|
+
const r3 = rule;
|
|
23818
|
+
if (!isNonEmptyString(r3.title)) {
|
|
23763
23819
|
errors.push("missing required field: title");
|
|
23764
23820
|
}
|
|
23765
|
-
if (!isNonEmptyString(
|
|
23821
|
+
if (!isNonEmptyString(r3.description)) {
|
|
23766
23822
|
errors.push("missing required field: description");
|
|
23767
23823
|
}
|
|
23768
|
-
if (!isNonEmptyString(
|
|
23824
|
+
if (!isNonEmptyString(r3.severity)) {
|
|
23769
23825
|
errors.push("missing required field: severity");
|
|
23770
23826
|
}
|
|
23771
|
-
const destinationKnown = isRuleDestination2(
|
|
23827
|
+
const destinationKnown = isRuleDestination2(r3.destination);
|
|
23772
23828
|
if (!destinationKnown) {
|
|
23773
23829
|
errors.push(
|
|
23774
|
-
`invalid destination: ${JSON.stringify(
|
|
23830
|
+
`invalid destination: ${JSON.stringify(r3.destination)} is not a known RuleDestination (one of ${RULE_DESTINATIONS.join(", ")})`
|
|
23775
23831
|
);
|
|
23776
23832
|
}
|
|
23777
23833
|
if (destinationKnown) {
|
|
23778
|
-
const destination =
|
|
23779
|
-
const hasPattern = isNonEmptyString(
|
|
23780
|
-
const hasCheck = isNonEmptyString(
|
|
23781
|
-
const hasTextBody = isNonEmptyString(
|
|
23834
|
+
const destination = r3.destination;
|
|
23835
|
+
const hasPattern = isNonEmptyString(r3.pattern);
|
|
23836
|
+
const hasCheck = isNonEmptyString(r3.check);
|
|
23837
|
+
const hasTextBody = isNonEmptyString(r3.description);
|
|
23782
23838
|
switch (destination) {
|
|
23783
23839
|
case "pattern-scanner":
|
|
23784
23840
|
if (!hasPattern && !hasCheck) {
|
|
@@ -23804,7 +23860,7 @@ function validateRulePackRule(rule) {
|
|
|
23804
23860
|
break;
|
|
23805
23861
|
}
|
|
23806
23862
|
}
|
|
23807
|
-
const requiresHardened = destinationKnown && isExecutableDestination(
|
|
23863
|
+
const requiresHardened = destinationKnown && isExecutableDestination(r3.destination);
|
|
23808
23864
|
return { valid: errors.length === 0, errors, requiresHardened };
|
|
23809
23865
|
}
|
|
23810
23866
|
var RULE_DESTINATIONS;
|
|
@@ -23916,8 +23972,8 @@ function countUntrusted(envelope) {
|
|
|
23916
23972
|
}
|
|
23917
23973
|
function isValidInstalledPack(p19) {
|
|
23918
23974
|
if (!p19 || typeof p19 !== "object") return false;
|
|
23919
|
-
const
|
|
23920
|
-
return typeof
|
|
23975
|
+
const r3 = p19;
|
|
23976
|
+
return typeof r3.slug === "string" && r3.slug.length > 0 && Array.isArray(r3.rules);
|
|
23921
23977
|
}
|
|
23922
23978
|
function packRulePromptHash(slug, rule) {
|
|
23923
23979
|
const { createHash: createHash17 } = __require("node:crypto");
|
|
@@ -24403,14 +24459,14 @@ function collectObservations(memDb, queryText, pool, nowMs, loadVec, modelId, di
|
|
|
24403
24459
|
WHERE observations_fts MATCH ?${bm25Temporal ? ` AND ${bm25Temporal}` : ""}
|
|
24404
24460
|
ORDER BY rank LIMIT ?`
|
|
24405
24461
|
).all(sanitizeFts5QueryOr(queryText), pool);
|
|
24406
|
-
rows.forEach((
|
|
24407
|
-
map.set(
|
|
24408
|
-
id:
|
|
24462
|
+
rows.forEach((r3, i) => {
|
|
24463
|
+
map.set(r3.id, {
|
|
24464
|
+
id: r3.id,
|
|
24409
24465
|
source: "observation",
|
|
24410
|
-
title:
|
|
24411
|
-
snippet: snippetOf(
|
|
24412
|
-
importance:
|
|
24413
|
-
ageDays: ageDaysFrom(
|
|
24466
|
+
title: r3.title,
|
|
24467
|
+
snippet: snippetOf(r3.detail ?? r3.title),
|
|
24468
|
+
importance: r3.importance,
|
|
24469
|
+
ageDays: ageDaysFrom(r3.created_at_epoch * 1e3, nowMs),
|
|
24414
24470
|
bm25Order: i,
|
|
24415
24471
|
vecs: []
|
|
24416
24472
|
});
|
|
@@ -24421,15 +24477,15 @@ function collectObservations(memDb, queryText, pool, nowMs, loadVec, modelId, di
|
|
|
24421
24477
|
`SELECT id, title, detail, importance, created_at_epoch
|
|
24422
24478
|
FROM observations${plainTemporal ? ` WHERE ${plainTemporal}` : ""} ORDER BY created_at_epoch DESC LIMIT ?`
|
|
24423
24479
|
).all(pool);
|
|
24424
|
-
for (const
|
|
24425
|
-
if (!map.has(
|
|
24426
|
-
map.set(
|
|
24427
|
-
id:
|
|
24480
|
+
for (const r3 of recent) {
|
|
24481
|
+
if (!map.has(r3.id)) {
|
|
24482
|
+
map.set(r3.id, {
|
|
24483
|
+
id: r3.id,
|
|
24428
24484
|
source: "observation",
|
|
24429
|
-
title:
|
|
24430
|
-
snippet: snippetOf(
|
|
24431
|
-
importance:
|
|
24432
|
-
ageDays: ageDaysFrom(
|
|
24485
|
+
title: r3.title,
|
|
24486
|
+
snippet: snippetOf(r3.detail ?? r3.title),
|
|
24487
|
+
importance: r3.importance,
|
|
24488
|
+
ageDays: ageDaysFrom(r3.created_at_epoch * 1e3, nowMs),
|
|
24433
24489
|
bm25Order: null,
|
|
24434
24490
|
vecs: []
|
|
24435
24491
|
});
|
|
@@ -24469,14 +24525,14 @@ function collectLikeSource(db, source, cfg, queryText, pool, nowMs) {
|
|
|
24469
24525
|
FROM ${cfg.table} WHERE (${likeClause})${cfg.temporalClause ? ` AND ${cfg.temporalClause}` : ""}
|
|
24470
24526
|
ORDER BY ${cfg.createdCol} DESC LIMIT ?`
|
|
24471
24527
|
).all(...params, pool);
|
|
24472
|
-
rows.forEach((
|
|
24473
|
-
map.set(
|
|
24474
|
-
id:
|
|
24528
|
+
rows.forEach((r3, i) => {
|
|
24529
|
+
map.set(r3.id, {
|
|
24530
|
+
id: r3.id,
|
|
24475
24531
|
source,
|
|
24476
|
-
title:
|
|
24477
|
-
snippet: snippetOf(
|
|
24532
|
+
title: r3.title,
|
|
24533
|
+
snippet: snippetOf(r3.snippet ?? r3.title),
|
|
24478
24534
|
importance: 3,
|
|
24479
|
-
ageDays: ageExpr(
|
|
24535
|
+
ageDays: ageExpr(r3.created),
|
|
24480
24536
|
bm25Order: i,
|
|
24481
24537
|
vecs: []
|
|
24482
24538
|
});
|
|
@@ -24489,15 +24545,15 @@ function collectLikeSource(db, source, cfg, queryText, pool, nowMs) {
|
|
|
24489
24545
|
${cfg.snippetCol} AS snippet, ${cfg.createdCol} AS created
|
|
24490
24546
|
FROM ${cfg.table}${cfg.temporalClause ? ` WHERE ${cfg.temporalClause}` : ""} ORDER BY ${cfg.createdCol} DESC LIMIT ?`
|
|
24491
24547
|
).all(pool);
|
|
24492
|
-
for (const
|
|
24493
|
-
if (!map.has(
|
|
24494
|
-
map.set(
|
|
24495
|
-
id:
|
|
24548
|
+
for (const r3 of recent) {
|
|
24549
|
+
if (!map.has(r3.id)) {
|
|
24550
|
+
map.set(r3.id, {
|
|
24551
|
+
id: r3.id,
|
|
24496
24552
|
source,
|
|
24497
|
-
title:
|
|
24498
|
-
snippet: snippetOf(
|
|
24553
|
+
title: r3.title,
|
|
24554
|
+
snippet: snippetOf(r3.snippet ?? r3.title),
|
|
24499
24555
|
importance: 3,
|
|
24500
|
-
ageDays: ageExpr(
|
|
24556
|
+
ageDays: ageExpr(r3.created),
|
|
24501
24557
|
bm25Order: null,
|
|
24502
24558
|
vecs: []
|
|
24503
24559
|
});
|
|
@@ -24517,14 +24573,14 @@ function collectKnowledgeChunks(knowledgeDb, queryText, pool, nowMs, loadVec, mo
|
|
|
24517
24573
|
WHERE knowledge_fts MATCH ?
|
|
24518
24574
|
ORDER BY rank LIMIT ?`
|
|
24519
24575
|
).all(sanitizeFts5QueryOr(queryText), pool);
|
|
24520
|
-
rows.forEach((
|
|
24521
|
-
map.set(
|
|
24522
|
-
id:
|
|
24576
|
+
rows.forEach((r3, i) => {
|
|
24577
|
+
map.set(r3.id, {
|
|
24578
|
+
id: r3.id,
|
|
24523
24579
|
source: "knowledge_chunk",
|
|
24524
|
-
title:
|
|
24525
|
-
snippet: snippetOf(
|
|
24580
|
+
title: r3.heading || snippetOf(r3.content, 60),
|
|
24581
|
+
snippet: snippetOf(r3.content),
|
|
24526
24582
|
importance: 3,
|
|
24527
|
-
ageDays: ageDaysFrom((
|
|
24583
|
+
ageDays: ageDaysFrom((r3.epoch ?? 0) * 1e3, nowMs),
|
|
24528
24584
|
bm25Order: i,
|
|
24529
24585
|
vecs: []
|
|
24530
24586
|
});
|
|
@@ -24556,9 +24612,9 @@ function loadEmbeddings(db, table, idCol, ids, modelId, dim, assign) {
|
|
|
24556
24612
|
WHERE ${idCol} IN (${placeholders}) AND model_id = ? AND dim = ?
|
|
24557
24613
|
LIMIT ?`
|
|
24558
24614
|
).all(...ids, modelId, dim, ids.length);
|
|
24559
|
-
for (const
|
|
24560
|
-
const v3 = blobToFloat32(
|
|
24561
|
-
if (v3 && v3.length === dim) assign(
|
|
24615
|
+
for (const r3 of rows) {
|
|
24616
|
+
const v3 = blobToFloat32(r3.vec);
|
|
24617
|
+
if (v3 && v3.length === dim) assign(r3.id, v3);
|
|
24562
24618
|
}
|
|
24563
24619
|
} catch {
|
|
24564
24620
|
}
|
|
@@ -24668,17 +24724,17 @@ function hybridSearch(memDb, knowledgeDb, opts) {
|
|
|
24668
24724
|
ageDays: c2.ageDays
|
|
24669
24725
|
};
|
|
24670
24726
|
});
|
|
24671
|
-
const top = ranked.filter((
|
|
24727
|
+
const top = ranked.filter((r3) => r3.score >= minScore).sort((a2, b2) => b2.score - a2.score || a2.ageDays - b2.ageDays).slice(0, limit);
|
|
24672
24728
|
if (includeSuperseded && opts.asOf == null) {
|
|
24673
|
-
for (const
|
|
24674
|
-
const table =
|
|
24729
|
+
for (const r3 of top) {
|
|
24730
|
+
const table = r3.source === "observation" ? "observations" : r3.source === "architecture_decision" ? "architecture_decisions" : null;
|
|
24675
24731
|
if (!table) continue;
|
|
24676
24732
|
try {
|
|
24677
|
-
const row = memDb.prepare(`SELECT valid_to, superseded_by, expired_at_epoch FROM ${table} WHERE id = ?`).get(
|
|
24733
|
+
const row = memDb.prepare(`SELECT valid_to, superseded_by, expired_at_epoch FROM ${table} WHERE id = ?`).get(r3.id);
|
|
24678
24734
|
if (row && row.expired_at_epoch != null) {
|
|
24679
24735
|
const when = row.valid_to ? row.valid_to.slice(0, 10) : "an earlier date";
|
|
24680
24736
|
const by = row.superseded_by != null ? ` by #${row.superseded_by}` : "";
|
|
24681
|
-
|
|
24737
|
+
r3.snippet = `(superseded on ${when}${by}) ${r3.snippet}`;
|
|
24682
24738
|
}
|
|
24683
24739
|
} catch {
|
|
24684
24740
|
}
|
|
@@ -24808,11 +24864,11 @@ async function supersedeIfContradicted(memDb, knowledgeDb, args2) {
|
|
|
24808
24864
|
candidatePool: 20
|
|
24809
24865
|
});
|
|
24810
24866
|
const scored = [];
|
|
24811
|
-
for (const
|
|
24812
|
-
if (
|
|
24867
|
+
for (const r3 of results) {
|
|
24868
|
+
if (r3.id === args2.newId) continue;
|
|
24813
24869
|
if (Date.now() - start > cfg.budgetMs) break;
|
|
24814
|
-
const vec = await candidateVec(memDb, args2.source,
|
|
24815
|
-
if (vec) scored.push({ id:
|
|
24870
|
+
const vec = await candidateVec(memDb, args2.source, r3.id);
|
|
24871
|
+
if (vec) scored.push({ id: r3.id, source: args2.source, cosine: cosineSim(queryVec, vec) });
|
|
24816
24872
|
}
|
|
24817
24873
|
let verdict = null;
|
|
24818
24874
|
if (cfg.judgeEndpoint && Date.now() - start < cfg.budgetMs) {
|
|
@@ -24871,17 +24927,11 @@ function emptyResult() {
|
|
|
24871
24927
|
};
|
|
24872
24928
|
}
|
|
24873
24929
|
function acquireLease(db, now, ttlSec) {
|
|
24874
|
-
|
|
24875
|
-
if (raw) {
|
|
24876
|
-
const expiry = Number(raw.split(":")[1]);
|
|
24877
|
-
if (Number.isFinite(expiry) && expiry > now) return false;
|
|
24878
|
-
}
|
|
24879
|
-
setMemoryMeta(db, LEASE_KEY, `${process.pid}:${now + ttlSec}`);
|
|
24880
|
-
return true;
|
|
24930
|
+
return tryAcquireMetaLease(db, LEASE_KEY, ttlSec, now);
|
|
24881
24931
|
}
|
|
24882
24932
|
function releaseLease(db) {
|
|
24883
24933
|
try {
|
|
24884
|
-
|
|
24934
|
+
releaseMetaLease(db, LEASE_KEY);
|
|
24885
24935
|
} catch {
|
|
24886
24936
|
}
|
|
24887
24937
|
}
|
|
@@ -24900,19 +24950,19 @@ async function stageDedupe(db, cfg, deadline, now) {
|
|
|
24900
24950
|
return 0;
|
|
24901
24951
|
}
|
|
24902
24952
|
let deduped = 0;
|
|
24903
|
-
for (const
|
|
24953
|
+
for (const r3 of rows) {
|
|
24904
24954
|
if (Date.now() > deadline) break;
|
|
24905
|
-
const live = db.prepare(`SELECT expired_at FROM observations WHERE id = ?`).get(
|
|
24955
|
+
const live = db.prepare(`SELECT expired_at FROM observations WHERE id = ?`).get(r3.id);
|
|
24906
24956
|
if (!live || live.expired_at != null) continue;
|
|
24907
24957
|
const res = await supersedeIfContradicted(db, null, {
|
|
24908
|
-
text: `${
|
|
24909
|
-
${
|
|
24958
|
+
text: `${r3.title}
|
|
24959
|
+
${r3.detail ?? ""}`.trim(),
|
|
24910
24960
|
source: "observation",
|
|
24911
|
-
newId:
|
|
24961
|
+
newId: r3.id,
|
|
24912
24962
|
nowEpochSec: now
|
|
24913
24963
|
});
|
|
24914
24964
|
if (res.superseded != null) deduped++;
|
|
24915
|
-
setMemoryMeta(db, DEDUPE_CURSOR, String(
|
|
24965
|
+
setMemoryMeta(db, DEDUPE_CURSOR, String(r3.id));
|
|
24916
24966
|
}
|
|
24917
24967
|
return deduped;
|
|
24918
24968
|
}
|
|
@@ -24990,11 +25040,11 @@ async function stagePromote(db, cfg, projectRoot, deadline, now, dryRun) {
|
|
|
24990
25040
|
).all(MAX_ROWS_PER_STAGE);
|
|
24991
25041
|
if (rows.length === 0) return { promoted: 0, refusedByTier: 0, embedderUnavailable: false };
|
|
24992
25042
|
const vecs = /* @__PURE__ */ new Map();
|
|
24993
|
-
for (const
|
|
25043
|
+
for (const r3 of rows) {
|
|
24994
25044
|
if (Date.now() > deadline) break;
|
|
24995
|
-
const v3 = await embed(`${
|
|
24996
|
-
${
|
|
24997
|
-
if (v3) vecs.set(
|
|
25045
|
+
const v3 = await embed(`${r3.title}
|
|
25046
|
+
${r3.detail ?? ""}`.trim());
|
|
25047
|
+
if (v3) vecs.set(r3.id, v3);
|
|
24998
25048
|
}
|
|
24999
25049
|
if (vecs.size === 0) {
|
|
25000
25050
|
return { promoted: 0, refusedByTier: 0, embedderUnavailable: true };
|
|
@@ -25002,13 +25052,13 @@ ${r2.detail ?? ""}`.trim());
|
|
|
25002
25052
|
const CLUSTER_THRESHOLD = 0.8;
|
|
25003
25053
|
const used = /* @__PURE__ */ new Set();
|
|
25004
25054
|
const clusters = [];
|
|
25005
|
-
for (const
|
|
25006
|
-
if (used.has(
|
|
25007
|
-
const cluster = [
|
|
25008
|
-
used.add(
|
|
25055
|
+
for (const r3 of rows) {
|
|
25056
|
+
if (used.has(r3.id) || !vecs.has(r3.id)) continue;
|
|
25057
|
+
const cluster = [r3];
|
|
25058
|
+
used.add(r3.id);
|
|
25009
25059
|
for (const other of rows) {
|
|
25010
25060
|
if (used.has(other.id) || !vecs.has(other.id)) continue;
|
|
25011
|
-
if (cosineSim(vecs.get(
|
|
25061
|
+
if (cosineSim(vecs.get(r3.id), vecs.get(other.id)) >= CLUSTER_THRESHOLD) {
|
|
25012
25062
|
cluster.push(other);
|
|
25013
25063
|
used.add(other.id);
|
|
25014
25064
|
}
|
|
@@ -25107,14 +25157,14 @@ function stageReweight(db, cfg, now) {
|
|
|
25107
25157
|
ON CONFLICT(source, record_id) DO UPDATE SET last_reweight_epoch = excluded.last_reweight_epoch`
|
|
25108
25158
|
);
|
|
25109
25159
|
const tx = db.transaction(() => {
|
|
25110
|
-
for (const
|
|
25111
|
-
bump.run(
|
|
25112
|
-
mark.run(
|
|
25160
|
+
for (const r3 of promote) {
|
|
25161
|
+
bump.run(r3.id);
|
|
25162
|
+
mark.run(r3.id, now);
|
|
25113
25163
|
changed++;
|
|
25114
25164
|
}
|
|
25115
|
-
for (const
|
|
25116
|
-
drop.run(
|
|
25117
|
-
mark.run(
|
|
25165
|
+
for (const r3 of demote) {
|
|
25166
|
+
drop.run(r3.id);
|
|
25167
|
+
mark.run(r3.id, now);
|
|
25118
25168
|
changed++;
|
|
25119
25169
|
}
|
|
25120
25170
|
});
|
|
@@ -28060,8 +28110,8 @@ async function runAdaptersList(_args) {
|
|
|
28060
28110
|
`);
|
|
28061
28111
|
}
|
|
28062
28112
|
} else {
|
|
28063
|
-
for (const
|
|
28064
|
-
process.stderr.write(`[manifest] ${
|
|
28113
|
+
for (const r3 of manifestResult.reasons) {
|
|
28114
|
+
process.stderr.write(`[manifest] ${r3}
|
|
28065
28115
|
`);
|
|
28066
28116
|
}
|
|
28067
28117
|
}
|
|
@@ -28980,9 +29030,9 @@ var init_security_utils = __esm({
|
|
|
28980
29030
|
|
|
28981
29031
|
// src/rules.ts
|
|
28982
29032
|
function getPatternRules() {
|
|
28983
|
-
return getConfig().rules.map((
|
|
28984
|
-
match:
|
|
28985
|
-
rules:
|
|
29033
|
+
return getConfig().rules.map((r3) => ({
|
|
29034
|
+
match: r3.pattern,
|
|
29035
|
+
rules: r3.rules
|
|
28986
29036
|
}));
|
|
28987
29037
|
}
|
|
28988
29038
|
function matchRules(filePath) {
|
|
@@ -31244,8 +31294,8 @@ function handleSearch(args2, db) {
|
|
|
31244
31294
|
const lines = [`## Search Results for "${query}" (${results.length} matches)`, ""];
|
|
31245
31295
|
lines.push("| ID | Type | Title | Date | Importance |");
|
|
31246
31296
|
lines.push("|----|------|-------|------|------------|");
|
|
31247
|
-
for (const
|
|
31248
|
-
lines.push(`| ${
|
|
31297
|
+
for (const r3 of results) {
|
|
31298
|
+
lines.push(`| ${r3.id} | ${r3.type} | ${r3.title.slice(0, 80)} | ${r3.created_at.split("T")[0]} | ${r3.importance} |`);
|
|
31249
31299
|
}
|
|
31250
31300
|
lines.push("");
|
|
31251
31301
|
lines.push(`Use ${p3("memory_detail")} with IDs for full details.`);
|
|
@@ -31678,9 +31728,9 @@ function handleDocsAudit(args2) {
|
|
|
31678
31728
|
}
|
|
31679
31729
|
}
|
|
31680
31730
|
}
|
|
31681
|
-
const staleCount = results.filter((
|
|
31682
|
-
const newCount = results.filter((
|
|
31683
|
-
const okCount = results.filter((
|
|
31731
|
+
const staleCount = results.filter((r3) => r3.status === "STALE").length;
|
|
31732
|
+
const newCount = results.filter((r3) => r3.status === "NEW").length;
|
|
31733
|
+
const okCount = results.filter((r3) => r3.status === "OK").length;
|
|
31684
31734
|
const report = {
|
|
31685
31735
|
affectedPages: results,
|
|
31686
31736
|
summary: `${results.length} pages checked: ${staleCount} STALE, ${newCount} NEW, ${okCount} OK. ${staleCount + newCount > 0 ? `${staleCount + newCount} pages need updates.` : "All docs are current."}`
|
|
@@ -31932,8 +31982,8 @@ function handlePromptAnalysis(args2, db) {
|
|
|
31932
31982
|
const lines = [`## Prompt Search: "${query}" (${results.length} results)`, ""];
|
|
31933
31983
|
lines.push("| Session | Turn | Prompt (truncated) | Tools | Response Tokens | Date |");
|
|
31934
31984
|
lines.push("|---------|------|--------------------|-------|-----------------|------|");
|
|
31935
|
-
for (const
|
|
31936
|
-
lines.push(`| ${
|
|
31985
|
+
for (const r3 of results) {
|
|
31986
|
+
lines.push(`| ${r3.session_id.slice(0, 8)}... | ${r3.turn_number} | ${r3.user_prompt.slice(0, 60).replace(/\|/g, "\\|")} | ${r3.tool_call_count} | ${r3.response_tokens ?? "-"} | ${r3.created_at.split("T")[0]} |`);
|
|
31937
31987
|
}
|
|
31938
31988
|
return text4(lines.join("\n"));
|
|
31939
31989
|
}
|
|
@@ -33373,12 +33423,49 @@ var init_analytics = __esm({
|
|
|
33373
33423
|
}
|
|
33374
33424
|
});
|
|
33375
33425
|
|
|
33426
|
+
// src/model-pricing.ts
|
|
33427
|
+
var PRICE_TABLE_SOURCE, PRICE_TABLE_AS_OF, r2, US, BUILT_IN_PRICES;
|
|
33428
|
+
var init_model_pricing = __esm({
|
|
33429
|
+
"src/model-pricing.ts"() {
|
|
33430
|
+
"use strict";
|
|
33431
|
+
PRICE_TABLE_SOURCE = "https://platform.claude.com/docs/en/about-claude/pricing";
|
|
33432
|
+
PRICE_TABLE_AS_OF = "2026-11-09";
|
|
33433
|
+
r2 = (input, cacheWrite5m, cacheWrite1h, cacheRead, output) => ({ input, cacheWrite5m, cacheWrite1h, cacheRead, output });
|
|
33434
|
+
US = 1.1;
|
|
33435
|
+
BUILT_IN_PRICES = {
|
|
33436
|
+
"claude-fable-5-1": { ...r2(10, 12.5, 20, 0.25, 50), usGeoMultiplier: US },
|
|
33437
|
+
"claude-mythos-5-1": { ...r2(10, 12.5, 20, 0.25, 50), usGeoMultiplier: US },
|
|
33438
|
+
"claude-fable-5": { ...r2(10, 12.5, 20, 1, 50), usGeoMultiplier: US },
|
|
33439
|
+
"claude-mythos-5": { ...r2(10, 12.5, 20, 1, 50), usGeoMultiplier: US },
|
|
33440
|
+
"claude-opus-5-5": { ...r2(4, 5, 8, 0.2, 20), fast: { input: 8, output: 40 }, usGeoMultiplier: US },
|
|
33441
|
+
"claude-opus-5": { ...r2(5, 6.25, 10, 0.5, 25), fast: { input: 10, output: 50 }, usGeoMultiplier: US },
|
|
33442
|
+
"claude-opus-4-8": { ...r2(5, 6.25, 10, 0.5, 25), fast: { input: 10, output: 50 }, usGeoMultiplier: US },
|
|
33443
|
+
"claude-opus-4-7": { ...r2(5, 6.25, 10, 0.5, 25), usGeoMultiplier: US },
|
|
33444
|
+
"claude-opus-4-6": { ...r2(5, 6.25, 10, 0.5, 25), usGeoMultiplier: US },
|
|
33445
|
+
"claude-opus-4-5": r2(5, 6.25, 10, 0.5, 25),
|
|
33446
|
+
"claude-opus-4-1": r2(15, 18.75, 30, 1.5, 75),
|
|
33447
|
+
"claude-opus-4": r2(15, 18.75, 30, 1.5, 75),
|
|
33448
|
+
"claude-sonnet-5-5": { ...r2(2, 2.5, 4, 0.1, 10), usGeoMultiplier: US },
|
|
33449
|
+
"claude-sonnet-5": { ...r2(2, 2.5, 4, 0.2, 10), usGeoMultiplier: US },
|
|
33450
|
+
"claude-sonnet-4-6": { ...r2(3, 3.75, 6, 0.3, 15), usGeoMultiplier: US },
|
|
33451
|
+
"claude-sonnet-4-5": r2(3, 3.75, 6, 0.3, 15),
|
|
33452
|
+
"claude-sonnet-4": r2(3, 3.75, 6, 0.3, 15),
|
|
33453
|
+
"claude-haiku-5-5": {
|
|
33454
|
+
...r2(0.1, 0.125, 0.2, 0.01, 0.5),
|
|
33455
|
+
longPrompt: { overTokens: 1e5, rates: r2(0.5, 0.625, 1, 0.05, 2.5) },
|
|
33456
|
+
usGeoMultiplier: US
|
|
33457
|
+
},
|
|
33458
|
+
"claude-haiku-4-5": r2(1, 1.25, 2, 0.1, 5)
|
|
33459
|
+
};
|
|
33460
|
+
}
|
|
33461
|
+
});
|
|
33462
|
+
|
|
33376
33463
|
// src/cost-tracker.ts
|
|
33377
33464
|
function p8(baseName) {
|
|
33378
33465
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
33379
33466
|
}
|
|
33380
|
-
function
|
|
33381
|
-
return getConfig().analytics?.cost?.
|
|
33467
|
+
function getCurrency() {
|
|
33468
|
+
return getConfig().analytics?.cost?.currency ?? "USD";
|
|
33382
33469
|
}
|
|
33383
33470
|
function getCostToolDefinitions() {
|
|
33384
33471
|
return [
|
|
@@ -33452,25 +33539,31 @@ function handleCostSession(args2, db) {
|
|
|
33452
33539
|
if (!cost) {
|
|
33453
33540
|
return text7(`No cost data found for session ${sessionId}. Cost data is recorded automatically when sessions end via the session-end hook. If the session is still active, data will appear after it completes. Try: ${p8("cost_trend")} { days: 30 } to see aggregate cost data instead.`);
|
|
33454
33541
|
}
|
|
33455
|
-
const
|
|
33456
|
-
const
|
|
33457
|
-
const
|
|
33458
|
-
const outputCost = cost.output_tokens / 1e6 * mp.output_per_million;
|
|
33459
|
-
const cacheReadCost = cost.cache_read_tokens / 1e6 * (mp.cache_read_per_million ?? 0);
|
|
33460
|
-
const cacheWriteCost = cost.cache_write_tokens / 1e6 * (mp.cache_write_per_million ?? 0);
|
|
33542
|
+
const status = cost.cost_status ?? "unknown";
|
|
33543
|
+
const dollars = cost.estimated_cost_usd;
|
|
33544
|
+
const headline = status === "unknown" ? "unknown" : `$${dollars.toFixed(4)}${status === "partial" ? " (partial, see below)" : ""}`;
|
|
33461
33545
|
const lines = [
|
|
33462
|
-
`## Session Cost:
|
|
33463
|
-
`Model: ${cost.model}`,
|
|
33546
|
+
`## Session Cost: ${headline}`,
|
|
33547
|
+
`Model: ${cost.model ?? "unknown"}`,
|
|
33464
33548
|
"",
|
|
33465
33549
|
"### Token Usage",
|
|
33466
|
-
`| Type | Tokens
|
|
33467
|
-
|
|
33468
|
-
`| Input | ${cost.input_tokens.toLocaleString()}
|
|
33469
|
-
`| Output | ${cost.output_tokens.toLocaleString()}
|
|
33470
|
-
`| Cache Read | ${cost.cache_read_tokens.toLocaleString()}
|
|
33471
|
-
`| Cache Write | ${cost.cache_write_tokens.toLocaleString()}
|
|
33472
|
-
`| **Total** | ${cost.total_tokens.toLocaleString()}
|
|
33550
|
+
`| Type | Tokens |`,
|
|
33551
|
+
`|------|--------|`,
|
|
33552
|
+
`| Input | ${cost.input_tokens.toLocaleString()} |`,
|
|
33553
|
+
`| Output | ${cost.output_tokens.toLocaleString()} |`,
|
|
33554
|
+
`| Cache Read | ${cost.cache_read_tokens.toLocaleString()} |`,
|
|
33555
|
+
`| Cache Write | ${cost.cache_write_tokens.toLocaleString()} |`,
|
|
33556
|
+
`| **Total** | ${cost.total_tokens.toLocaleString()} |`,
|
|
33557
|
+
"",
|
|
33558
|
+
`Responses priced: ${cost.priced_responses ?? 0}; without a price: ${cost.unpriced_responses ?? 0}.`
|
|
33473
33559
|
];
|
|
33560
|
+
if (status === "unknown" && !cost.unpriced_responses) {
|
|
33561
|
+
lines.push("No transcript was available to price this session.");
|
|
33562
|
+
}
|
|
33563
|
+
if (cost.unpriced_models) {
|
|
33564
|
+
lines.push(`No price known for: ${cost.unpriced_models}. Add it under analytics.cost.models in massu.config.yaml.`);
|
|
33565
|
+
}
|
|
33566
|
+
lines.push(`Prices: ${PRICE_TABLE_SOURCE} as of ${PRICE_TABLE_AS_OF} (${getCurrency()}).`);
|
|
33474
33567
|
return text7(lines.join("\n"));
|
|
33475
33568
|
}
|
|
33476
33569
|
function handleCostTrend(args2, db) {
|
|
@@ -33481,17 +33574,21 @@ function handleCostTrend(args2, db) {
|
|
|
33481
33574
|
}
|
|
33482
33575
|
const sql = groupBy === "week" ? `SELECT strftime('%Y-W%W', created_at) as period,
|
|
33483
33576
|
COUNT(*) as sessions,
|
|
33484
|
-
SUM(estimated_cost_usd) as total_cost,
|
|
33577
|
+
SUM(CASE WHEN cost_status = 'unknown' THEN 0 ELSE estimated_cost_usd END) as total_cost,
|
|
33485
33578
|
SUM(total_tokens) as total_tokens,
|
|
33486
|
-
|
|
33579
|
+
SUM(CASE WHEN cost_status = 'priced' THEN 1 ELSE 0 END) as priced_sessions,
|
|
33580
|
+
SUM(CASE WHEN cost_status = 'partial' THEN 1 ELSE 0 END) as partial_sessions,
|
|
33581
|
+
SUM(CASE WHEN cost_status = 'unknown' THEN 1 ELSE 0 END) as unknown_sessions
|
|
33487
33582
|
FROM session_costs
|
|
33488
33583
|
WHERE created_at >= datetime('now', ?)
|
|
33489
33584
|
GROUP BY period
|
|
33490
33585
|
ORDER BY period ASC` : `SELECT strftime('%Y-%m-%d', created_at) as period,
|
|
33491
33586
|
COUNT(*) as sessions,
|
|
33492
|
-
SUM(estimated_cost_usd) as total_cost,
|
|
33587
|
+
SUM(CASE WHEN cost_status = 'unknown' THEN 0 ELSE estimated_cost_usd END) as total_cost,
|
|
33493
33588
|
SUM(total_tokens) as total_tokens,
|
|
33494
|
-
|
|
33589
|
+
SUM(CASE WHEN cost_status = 'priced' THEN 1 ELSE 0 END) as priced_sessions,
|
|
33590
|
+
SUM(CASE WHEN cost_status = 'partial' THEN 1 ELSE 0 END) as partial_sessions,
|
|
33591
|
+
SUM(CASE WHEN cost_status = 'unknown' THEN 1 ELSE 0 END) as unknown_sessions
|
|
33495
33592
|
FROM session_costs
|
|
33496
33593
|
WHERE created_at >= datetime('now', ?)
|
|
33497
33594
|
GROUP BY period
|
|
@@ -33500,22 +33597,28 @@ function handleCostTrend(args2, db) {
|
|
|
33500
33597
|
if (rows.length === 0) {
|
|
33501
33598
|
return text7(`No cost data found in the last ${days} days. Cost tracking records token usage automatically at session end via hooks. Ensure session-end hooks are configured in your settings. Try: ${p8("cost_session")} { session_id: "..." } to check a specific session.`);
|
|
33502
33599
|
}
|
|
33503
|
-
const
|
|
33504
|
-
const
|
|
33600
|
+
const sum = (k3) => rows.reduce((acc, r3) => acc + (r3[k3] ?? 0), 0);
|
|
33601
|
+
const totalCost = sum("total_cost");
|
|
33602
|
+
const totalSessions = sum("sessions");
|
|
33603
|
+
const known = sum("priced_sessions") + sum("partial_sessions");
|
|
33604
|
+
const unknown = sum("unknown_sessions");
|
|
33605
|
+
const partial = sum("partial_sessions");
|
|
33505
33606
|
const lines = [
|
|
33506
33607
|
`## Cost Trend (${days} days)`,
|
|
33507
|
-
`Total: $${totalCost.toFixed(2)} across ${totalSessions}
|
|
33508
|
-
`Average per session: $${(totalCost /
|
|
33608
|
+
`Total: $${totalCost.toFixed(2)} across ${known} priced session(s)` + (unknown ? `; ${unknown} session(s) of ${totalSessions} have unknown cost and are NOT in this total` : ""),
|
|
33609
|
+
known ? `Average per priced session: $${(totalCost / known).toFixed(4)}` : "No session has a known cost in this window."
|
|
33610
|
+
];
|
|
33611
|
+
if (partial) lines.push(`${partial} session(s) are partial: a model they used has no price, so they are under-counted.`);
|
|
33612
|
+
lines.push(
|
|
33509
33613
|
"",
|
|
33510
33614
|
`### By ${groupBy === "week" ? "Week" : "Day"}`,
|
|
33511
|
-
`| Period | Sessions | Cost |
|
|
33512
|
-
|
|
33513
|
-
|
|
33615
|
+
`| Period | Sessions | Cost | Unknown |`,
|
|
33616
|
+
`|--------|----------|------|---------|`
|
|
33617
|
+
);
|
|
33514
33618
|
for (const row of rows) {
|
|
33515
|
-
lines.push(
|
|
33516
|
-
`| ${row.period} | ${row.sessions} | $${row.total_cost.toFixed(2)} | $${row.avg_cost.toFixed(4)} |`
|
|
33517
|
-
);
|
|
33619
|
+
lines.push(`| ${row.period} | ${row.sessions} | $${row.total_cost.toFixed(2)} | ${row.unknown_sessions} |`);
|
|
33518
33620
|
}
|
|
33621
|
+
lines.push("", `Prices: ${PRICE_TABLE_SOURCE} as of ${PRICE_TABLE_AS_OF} (${getCurrency()}).`);
|
|
33519
33622
|
return text7(lines.join("\n"));
|
|
33520
33623
|
}
|
|
33521
33624
|
function handleCostFeature(args2, db) {
|
|
@@ -33528,12 +33631,13 @@ function handleCostFeature(args2, db) {
|
|
|
33528
33631
|
ORDER BY total_cost DESC
|
|
33529
33632
|
`).all(`-${days} days`);
|
|
33530
33633
|
if (rows.length === 0) {
|
|
33531
|
-
return text7(`No feature cost data found in the last ${days} days.
|
|
33634
|
+
return text7(`No feature cost data found in the last ${days} days. Each session's priced cost is allocated across registered features (massu_sentinel_register) by the files it touched; sessions with unknown cost are skipped. Try: ${p8("cost_trend")} { days: 30 } for session-level cost trends instead.`);
|
|
33532
33635
|
}
|
|
33533
|
-
const totalCost = rows.reduce((sum,
|
|
33636
|
+
const totalCost = rows.reduce((sum, r3) => sum + r3.total_cost, 0);
|
|
33534
33637
|
const lines = [
|
|
33535
33638
|
`## Feature Cost Attribution (${days} days)`,
|
|
33536
|
-
`Total: $${totalCost.toFixed(2)} across ${rows.length}
|
|
33639
|
+
`Total: $${totalCost.toFixed(2)} across ${rows.length} feature row(s)`,
|
|
33640
|
+
"Each session's priced cost is ALLOCATED across the registered features whose files it touched, in proportion to those touches. It is an allocation by activity, not a measured per-feature cost. Sessions with unknown cost are not included.",
|
|
33537
33641
|
"",
|
|
33538
33642
|
"| Feature | Cost | Tokens | Sessions | % of Total |",
|
|
33539
33643
|
"|---------|------|--------|----------|------------|"
|
|
@@ -33549,18 +33653,12 @@ function handleCostFeature(args2, db) {
|
|
|
33549
33653
|
function text7(content) {
|
|
33550
33654
|
return { content: [{ type: "text", text: content }] };
|
|
33551
33655
|
}
|
|
33552
|
-
var
|
|
33656
|
+
var COST_BASE_NAMES;
|
|
33553
33657
|
var init_cost_tracker = __esm({
|
|
33554
33658
|
"src/cost-tracker.ts"() {
|
|
33555
33659
|
"use strict";
|
|
33556
33660
|
init_config();
|
|
33557
|
-
|
|
33558
|
-
"claude-opus-4-6": { input_per_million: 15, output_per_million: 75, cache_read_per_million: 1.5, cache_write_per_million: 18.75 },
|
|
33559
|
-
"claude-sonnet-4-6": { input_per_million: 3, output_per_million: 15, cache_read_per_million: 0.3, cache_write_per_million: 3.75 },
|
|
33560
|
-
"claude-sonnet-4-5": { input_per_million: 3, output_per_million: 15, cache_read_per_million: 0.3, cache_write_per_million: 3.75 },
|
|
33561
|
-
"claude-haiku-4-5-20251001": { input_per_million: 0.8, output_per_million: 4, cache_read_per_million: 0.08, cache_write_per_million: 1 },
|
|
33562
|
-
"default": { input_per_million: 3, output_per_million: 15, cache_read_per_million: 0.3, cache_write_per_million: 3.75 }
|
|
33563
|
-
};
|
|
33661
|
+
init_model_pricing();
|
|
33564
33662
|
COST_BASE_NAMES = /* @__PURE__ */ new Set(["cost_session", "cost_trend", "cost_feature"]);
|
|
33565
33663
|
}
|
|
33566
33664
|
});
|
|
@@ -33973,8 +34071,8 @@ function handleValidationReport(args2, db) {
|
|
|
33973
34071
|
if (results.length === 0) {
|
|
33974
34072
|
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 }.`);
|
|
33975
34073
|
}
|
|
33976
|
-
const passedCount = results.filter((
|
|
33977
|
-
const failedCount = results.filter((
|
|
34074
|
+
const passedCount = results.filter((r3) => r3.passed === 1).length;
|
|
34075
|
+
const failedCount = results.filter((r3) => r3.passed === 0).length;
|
|
33978
34076
|
const lines = [
|
|
33979
34077
|
`## Validation Report (${days} days)`,
|
|
33980
34078
|
`Files validated: ${results.length}`,
|
|
@@ -33985,11 +34083,11 @@ function handleValidationReport(args2, db) {
|
|
|
33985
34083
|
"| File | Rules Violated | Date |",
|
|
33986
34084
|
"|------|----------------|------|"
|
|
33987
34085
|
];
|
|
33988
|
-
const failedResults = results.filter((
|
|
33989
|
-
for (const
|
|
33990
|
-
const filename =
|
|
33991
|
-
const rules =
|
|
33992
|
-
lines.push(`| ${filename} | ${rules.slice(0, 60)} | ${
|
|
34086
|
+
const failedResults = results.filter((r3) => r3.passed === 0);
|
|
34087
|
+
for (const r3 of failedResults.slice(0, 30)) {
|
|
34088
|
+
const filename = r3.file_path.split("/").pop();
|
|
34089
|
+
const rules = r3.rules_violated ?? "-";
|
|
34090
|
+
lines.push(`| ${filename} | ${rules.slice(0, 60)} | ${r3.created_at.slice(0, 10)} |`);
|
|
33993
34091
|
}
|
|
33994
34092
|
if (failedResults.length > 30) {
|
|
33995
34093
|
lines.push(`... and ${failedResults.length - 30} more`);
|
|
@@ -34918,9 +35016,9 @@ function handleTeamSearch(args2, db) {
|
|
|
34918
35016
|
"| Developer | Type | Summary | Module | Date |",
|
|
34919
35017
|
"|-----------|------|---------|--------|------|"
|
|
34920
35018
|
];
|
|
34921
|
-
for (const
|
|
35019
|
+
for (const r3 of results) {
|
|
34922
35020
|
lines.push(
|
|
34923
|
-
`| ${
|
|
35021
|
+
`| ${r3.developer_id} | ${r3.observation_type} | ${r3.summary.slice(0, 60)} | ${r3.module ?? "-"} | ${r3.created_at.split("T")[0]} |`
|
|
34924
35022
|
);
|
|
34925
35023
|
}
|
|
34926
35024
|
return text13(lines.join("\n"));
|
|
@@ -36013,10 +36111,10 @@ function handleSearch3(db, args2) {
|
|
|
36013
36111
|
} else {
|
|
36014
36112
|
lines.push(`Found ${results.length} result(s):`);
|
|
36015
36113
|
lines.push("");
|
|
36016
|
-
for (const
|
|
36017
|
-
const preview =
|
|
36018
|
-
lines.push(`### ${
|
|
36019
|
-
lines.push(`**File**: ${claudeDir}/${
|
|
36114
|
+
for (const r3 of results) {
|
|
36115
|
+
const preview = r3.content.length > 500 ? r3.content.substring(0, 500) + "..." : r3.content;
|
|
36116
|
+
lines.push(`### ${r3.heading || r3.title} [${r3.chunk_type}]`);
|
|
36117
|
+
lines.push(`**File**: ${claudeDir}/${r3.file_path} | **Category**: ${r3.category}`);
|
|
36020
36118
|
lines.push("");
|
|
36021
36119
|
lines.push(preview);
|
|
36022
36120
|
lines.push("");
|
|
@@ -36250,11 +36348,11 @@ function handlePattern(db, args2) {
|
|
|
36250
36348
|
LIMIT 10
|
|
36251
36349
|
`).all(sanitizeFts5Query(topic), ...docIds);
|
|
36252
36350
|
if (topicResults.length > 0) {
|
|
36253
|
-
for (const
|
|
36254
|
-
lines.push(`### ${
|
|
36255
|
-
lines.push(`*Source: ${claudeDir}/${
|
|
36351
|
+
for (const r3 of topicResults) {
|
|
36352
|
+
lines.push(`### ${r3.heading || "(section)"} [${r3.chunk_type}]`);
|
|
36353
|
+
lines.push(`*Source: ${claudeDir}/${r3.file_path}*`);
|
|
36256
36354
|
lines.push("");
|
|
36257
|
-
lines.push(
|
|
36355
|
+
lines.push(r3.content.length > 800 ? r3.content.substring(0, 800) + "..." : r3.content);
|
|
36258
36356
|
lines.push("");
|
|
36259
36357
|
lines.push("---");
|
|
36260
36358
|
lines.push("");
|
|
@@ -36298,8 +36396,8 @@ function handlePattern(db, args2) {
|
|
|
36298
36396
|
if (ftsResults.length > 0) {
|
|
36299
36397
|
lines.push("### Broad Search Results");
|
|
36300
36398
|
lines.push("");
|
|
36301
|
-
for (const
|
|
36302
|
-
lines.push(`- **${
|
|
36399
|
+
for (const r3 of ftsResults) {
|
|
36400
|
+
lines.push(`- **${r3.heading || "(section)"}** (${claudeDir}/${r3.file_path}): ${r3.content.substring(0, 200)}...`);
|
|
36303
36401
|
}
|
|
36304
36402
|
}
|
|
36305
36403
|
} catch {
|
|
@@ -36447,9 +36545,9 @@ function handleCommand(db, args2) {
|
|
|
36447
36545
|
`).all(`%${keyword}%`, `%${keyword}%`);
|
|
36448
36546
|
lines.push(`## Commands matching "${keyword}" (${results.length} found)`);
|
|
36449
36547
|
lines.push("");
|
|
36450
|
-
for (const
|
|
36451
|
-
const preview =
|
|
36452
|
-
lines.push(`- **${
|
|
36548
|
+
for (const r3 of results) {
|
|
36549
|
+
const preview = r3.content.substring(0, 200).replace(/\n/g, " ");
|
|
36550
|
+
lines.push(`- **${r3.heading}** (${claudeDir}/${r3.file_path}): ${preview}...`);
|
|
36453
36551
|
}
|
|
36454
36552
|
} else {
|
|
36455
36553
|
const commands = db.prepare(`
|
|
@@ -36534,8 +36632,8 @@ function handlePlan(db, args2) {
|
|
|
36534
36632
|
`).all(`%${basename12(file)}%`);
|
|
36535
36633
|
lines.push(`## Plans referencing \`${file}\` (${results.length} found)`);
|
|
36536
36634
|
lines.push("");
|
|
36537
|
-
for (const
|
|
36538
|
-
lines.push(`- **${
|
|
36635
|
+
for (const r3 of results) {
|
|
36636
|
+
lines.push(`- **${r3.title}** (${r3.file_path})`);
|
|
36539
36637
|
}
|
|
36540
36638
|
} else if (keyword) {
|
|
36541
36639
|
try {
|
|
@@ -36550,8 +36648,8 @@ function handlePlan(db, args2) {
|
|
|
36550
36648
|
`).all(sanitizeFts5Query(keyword));
|
|
36551
36649
|
lines.push(`## Plans matching "${keyword}" (${results.length} found)`);
|
|
36552
36650
|
lines.push("");
|
|
36553
|
-
for (const
|
|
36554
|
-
lines.push(`- **${
|
|
36651
|
+
for (const r3 of results) {
|
|
36652
|
+
lines.push(`- **${r3.title}** (${r3.file_path})`);
|
|
36555
36653
|
}
|
|
36556
36654
|
} catch {
|
|
36557
36655
|
lines.push(`FTS search error for "${keyword}". Try simpler terms.`);
|
|
@@ -36744,7 +36842,7 @@ function handleEffectiveness2(db, args2) {
|
|
|
36744
36842
|
}
|
|
36745
36843
|
if (ruleId || mode === "detail") {
|
|
36746
36844
|
const targetRule = ruleId || "";
|
|
36747
|
-
const rule = allRules.find((
|
|
36845
|
+
const rule = allRules.find((r3) => r3.rule_id === targetRule);
|
|
36748
36846
|
if (!rule) {
|
|
36749
36847
|
return text15(`Rule ${targetRule} not found.`);
|
|
36750
36848
|
}
|
|
@@ -36767,10 +36865,10 @@ function handleEffectiveness2(db, args2) {
|
|
|
36767
36865
|
lines.push(`| Linked Corrections | ${correctionEdges.cnt} |`);
|
|
36768
36866
|
lines.push(`| Effectiveness Score | ${violations === 0 ? "HIGH (never violated)" : violations < 3 ? "MEDIUM" : "LOW (frequently violated)"} |`);
|
|
36769
36867
|
} else {
|
|
36770
|
-
const rulesWithCounts = allRules.map((
|
|
36771
|
-
...
|
|
36772
|
-
violations: violationCounts.get(
|
|
36773
|
-
last_at: lastViolated.get(
|
|
36868
|
+
const rulesWithCounts = allRules.map((r3) => ({
|
|
36869
|
+
...r3,
|
|
36870
|
+
violations: violationCounts.get(r3.rule_id) || 0,
|
|
36871
|
+
last_at: lastViolated.get(r3.rule_id) || "Never"
|
|
36774
36872
|
}));
|
|
36775
36873
|
if (mode === "most_violated") {
|
|
36776
36874
|
rulesWithCounts.sort((a2, b2) => b2.violations - a2.violations);
|
|
@@ -36786,9 +36884,9 @@ function handleEffectiveness2(db, args2) {
|
|
|
36786
36884
|
lines.push("");
|
|
36787
36885
|
lines.push(`| Rule | Text | Violations | Last Violated |`);
|
|
36788
36886
|
lines.push(`|------|------|------------|---------------|`);
|
|
36789
|
-
for (const
|
|
36790
|
-
const ruleText =
|
|
36791
|
-
lines.push(`| ${
|
|
36887
|
+
for (const r3 of rulesWithCounts.slice(0, topN)) {
|
|
36888
|
+
const ruleText = r3.rule_text.length > 40 ? r3.rule_text.substring(0, 40) + "..." : r3.rule_text;
|
|
36889
|
+
lines.push(`| ${r3.rule_id} | ${ruleText} | ${r3.violations} | ${r3.last_at} |`);
|
|
36792
36890
|
}
|
|
36793
36891
|
}
|
|
36794
36892
|
return text15(lines.join("\n"));
|
|
@@ -37085,21 +37183,21 @@ function handlePyRoutes(args2, dataDb) {
|
|
|
37085
37183
|
const routes = dataDb.prepare(query).all(...params);
|
|
37086
37184
|
if (args2.uncoupled) {
|
|
37087
37185
|
const callerCountStmt = dataDb.prepare(`SELECT COUNT(*) as cnt FROM ${t("py_route_callers")} WHERE route_id = ?`);
|
|
37088
|
-
const uncoupledRoutes = routes.filter((
|
|
37089
|
-
const callers = callerCountStmt.get(
|
|
37186
|
+
const uncoupledRoutes = routes.filter((r3) => {
|
|
37187
|
+
const callers = callerCountStmt.get(r3.id).cnt;
|
|
37090
37188
|
return callers === 0;
|
|
37091
37189
|
});
|
|
37092
37190
|
lines.push(`## Uncoupled Routes (${uncoupledRoutes.length} with no frontend callers)`);
|
|
37093
|
-
for (const
|
|
37094
|
-
lines.push(`- ${
|
|
37191
|
+
for (const r3 of uncoupledRoutes) {
|
|
37192
|
+
lines.push(`- ${r3.method} ${r3.path} \u2192 ${r3.function_name} (${r3.file}:${r3.line})`);
|
|
37095
37193
|
}
|
|
37096
37194
|
} else {
|
|
37097
37195
|
lines.push(`## Python Routes (${routes.length} found)`);
|
|
37098
|
-
for (const
|
|
37099
|
-
const auth =
|
|
37100
|
-
const deps = safeParseArray(
|
|
37101
|
-
lines.push(`- ${
|
|
37102
|
-
if (
|
|
37196
|
+
for (const r3 of routes) {
|
|
37197
|
+
const auth = r3.is_authenticated ? "" : " [UNAUTH]";
|
|
37198
|
+
const deps = safeParseArray(r3.dependencies);
|
|
37199
|
+
lines.push(`- ${r3.method} ${r3.path} \u2192 ${r3.function_name} (${r3.file}:${r3.line})${auth}`);
|
|
37200
|
+
if (r3.response_model) lines.push(` Response: ${r3.response_model}`);
|
|
37103
37201
|
if (deps.length > 0) lines.push(` Depends: ${deps.join(", ")}`);
|
|
37104
37202
|
}
|
|
37105
37203
|
}
|
|
@@ -37301,8 +37399,8 @@ function handlePyImpact(args2, dataDb) {
|
|
|
37301
37399
|
if (routes.length > 0) {
|
|
37302
37400
|
lines.push(`
|
|
37303
37401
|
### Routes Defined (${routes.length})`);
|
|
37304
|
-
for (const
|
|
37305
|
-
lines.push(`- ${
|
|
37402
|
+
for (const r3 of routes) {
|
|
37403
|
+
lines.push(`- ${r3.method} ${r3.path} \u2192 ${r3.function_name}`);
|
|
37306
37404
|
}
|
|
37307
37405
|
}
|
|
37308
37406
|
const models = dataDb.prepare(
|
|
@@ -37320,7 +37418,7 @@ function handlePyImpact(args2, dataDb) {
|
|
|
37320
37418
|
const placeholders = routeIds.map(() => "?").join(",");
|
|
37321
37419
|
const callers = dataDb.prepare(
|
|
37322
37420
|
`SELECT DISTINCT frontend_file FROM ${t("py_route_callers")} WHERE route_id IN (${placeholders}) LIMIT 100000`
|
|
37323
|
-
).all(...routeIds.map((
|
|
37421
|
+
).all(...routeIds.map((r3) => r3.id));
|
|
37324
37422
|
if (callers.length > 0) {
|
|
37325
37423
|
lines.push(`
|
|
37326
37424
|
### Frontend Callers (${callers.length} files)`);
|
|
@@ -37363,8 +37461,8 @@ function handlePyContext(args2, dataDb) {
|
|
|
37363
37461
|
const routes = dataDb.prepare(`SELECT method, path, function_name, line FROM ${t("py_routes")} WHERE file = ? LIMIT 10000`).all(file);
|
|
37364
37462
|
if (routes.length > 0) {
|
|
37365
37463
|
lines.push("\n### Routes");
|
|
37366
|
-
for (const
|
|
37367
|
-
lines.push(`- ${
|
|
37464
|
+
for (const r3 of routes) {
|
|
37465
|
+
lines.push(`- ${r3.method} ${r3.path} \u2192 ${r3.function_name} (L${r3.line})`);
|
|
37368
37466
|
}
|
|
37369
37467
|
}
|
|
37370
37468
|
const models = dataDb.prepare(`SELECT class_name, table_name, line FROM ${t("py_models")} WHERE file = ? LIMIT 10000`).all(file);
|
|
@@ -37375,8 +37473,8 @@ function handlePyContext(args2, dataDb) {
|
|
|
37375
37473
|
}
|
|
37376
37474
|
}
|
|
37377
37475
|
const rules = config.rules || [];
|
|
37378
|
-
const matchingRules = rules.filter((
|
|
37379
|
-
const pattern =
|
|
37476
|
+
const matchingRules = rules.filter((r3) => {
|
|
37477
|
+
const pattern = r3.pattern;
|
|
37380
37478
|
if (pattern === "**") return true;
|
|
37381
37479
|
if (pattern.includes("**/*.py")) return file.endsWith(".py");
|
|
37382
37480
|
return file.includes(pattern.replace(/\*\*/g, ""));
|
|
@@ -37384,8 +37482,8 @@ function handlePyContext(args2, dataDb) {
|
|
|
37384
37482
|
if (matchingRules.length > 0) {
|
|
37385
37483
|
lines.push("\n### Applicable Rules");
|
|
37386
37484
|
for (const rule of matchingRules) {
|
|
37387
|
-
for (const
|
|
37388
|
-
lines.push(`- ${
|
|
37485
|
+
for (const r3 of rule.rules) {
|
|
37486
|
+
lines.push(`- ${r3}`);
|
|
37389
37487
|
}
|
|
37390
37488
|
}
|
|
37391
37489
|
}
|
|
@@ -37791,8 +37889,8 @@ function handleContext(file, dataDb, codegraphDb) {
|
|
|
37791
37889
|
lines.push("## Applicable Rules");
|
|
37792
37890
|
for (const rule of rules) {
|
|
37793
37891
|
const severity = rule.severity ? `[${rule.severity}]` : "";
|
|
37794
|
-
for (const
|
|
37795
|
-
lines.push(`- ${severity} ${
|
|
37892
|
+
for (const r3 of rule.rules) {
|
|
37893
|
+
lines.push(`- ${severity} ${r3}`);
|
|
37796
37894
|
}
|
|
37797
37895
|
if (rule.patternFile) {
|
|
37798
37896
|
lines.push(` See: .claude/${rule.patternFile}`);
|
|
@@ -37880,7 +37978,7 @@ function handleContext(file, dataDb, codegraphDb) {
|
|
|
37880
37978
|
WHERE kf.content MATCH ? AND kc.chunk_type = 'rule'
|
|
37881
37979
|
LIMIT 8
|
|
37882
37980
|
`).all(ftsQuery);
|
|
37883
|
-
ftsRuleIds = ftsResults.map((
|
|
37981
|
+
ftsRuleIds = ftsResults.map((r3) => r3.rule_id).filter((id) => id.startsWith("CR-"));
|
|
37884
37982
|
} catch {
|
|
37885
37983
|
}
|
|
37886
37984
|
}
|
|
@@ -38234,7 +38332,7 @@ function handleDomains(args2, dataDb, codegraphDb) {
|
|
|
38234
38332
|
}
|
|
38235
38333
|
lines.push("");
|
|
38236
38334
|
lines.push(`### Routers (${files.routers.length})`);
|
|
38237
|
-
for (const
|
|
38335
|
+
for (const r3 of files.routers) lines.push(`- ${r3}`);
|
|
38238
38336
|
lines.push("");
|
|
38239
38337
|
lines.push(`### Pages (${files.pages.length})`);
|
|
38240
38338
|
for (const p19 of files.pages) lines.push(`- ${p19}`);
|
|
@@ -39324,8 +39422,8 @@ function winManagerDirs(env) {
|
|
|
39324
39422
|
function windowsAllowlistRoots(env) {
|
|
39325
39423
|
const d2 = winManagerDirs(env);
|
|
39326
39424
|
const roots = [];
|
|
39327
|
-
for (const
|
|
39328
|
-
if (
|
|
39425
|
+
for (const r3 of [d2.userprofile, d2.nvmHome, d2.nvmSymlink, d2.fnmDir, d2.voltaHome, d2.localappdata]) {
|
|
39426
|
+
if (r3) roots.push(r3);
|
|
39329
39427
|
}
|
|
39330
39428
|
roots.push("C:\\Program Files\\nodejs", "C:\\Program Files (x86)\\nodejs");
|
|
39331
39429
|
return roots;
|