@massu/core 2.4.0 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +126 -0
- package/commands/massu-bearings.md +1 -1
- package/commands/massu-recap.md +16 -5
- package/dist/cli.js +291 -80
- package/dist/hooks/auto-learning-pipeline.js +53 -9
- package/dist/hooks/classify-failure.js +55 -11
- package/dist/hooks/cost-tracker.js +23 -9
- package/dist/hooks/fix-detector.js +54 -9
- package/dist/hooks/incident-pipeline.js +53 -9
- package/dist/hooks/intent-suggester.js +53 -9
- package/dist/hooks/memory-recall.js +53 -9
- package/dist/hooks/post-edit-context.js +53 -9
- package/dist/hooks/post-tool-use.js +19 -6
- package/dist/hooks/pre-compact.js +19 -6
- package/dist/hooks/pre-delete-check.js +53 -9
- package/dist/hooks/pre-tool-use-gate.js +57 -11
- package/dist/hooks/quality-event.js +23 -9
- package/dist/hooks/rule-enforcement-pipeline.js +53 -9
- package/dist/hooks/security-gate.js +53 -9
- package/dist/hooks/session-end.js +19 -6
- package/dist/hooks/session-start.js +120 -37
- package/dist/hooks/user-prompt.js +19 -6
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1548,6 +1548,16 @@ var init_memory_vector = __esm({
|
|
|
1548
1548
|
}
|
|
1549
1549
|
});
|
|
1550
1550
|
|
|
1551
|
+
// src/lib/timestamps.ts
|
|
1552
|
+
function nowIso() {
|
|
1553
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1554
|
+
}
|
|
1555
|
+
var init_timestamps = __esm({
|
|
1556
|
+
"src/lib/timestamps.ts"() {
|
|
1557
|
+
"use strict";
|
|
1558
|
+
}
|
|
1559
|
+
});
|
|
1560
|
+
|
|
1551
1561
|
// src/db-backup.ts
|
|
1552
1562
|
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readdirSync as readdirSync2, statSync as statSync2, unlinkSync, copyFileSync, rmSync as rmSync4 } from "fs";
|
|
1553
1563
|
import { resolve as resolve3, join as join3, basename, dirname as dirname4 } from "path";
|
|
@@ -3972,8 +3982,8 @@ function linkSessionToTask(db, sessionId, taskId) {
|
|
|
3972
3982
|
}
|
|
3973
3983
|
function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantResponse, toolCallsJson, toolCallCount, promptTokens, responseTokens) {
|
|
3974
3984
|
const result = db.prepare(`
|
|
3975
|
-
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens)
|
|
3976
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3985
|
+
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens, created_at)
|
|
3986
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3977
3987
|
`).run(
|
|
3978
3988
|
sessionId,
|
|
3979
3989
|
turnNumber,
|
|
@@ -3982,14 +3992,15 @@ function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantRes
|
|
|
3982
3992
|
toolCallsJson,
|
|
3983
3993
|
toolCallCount,
|
|
3984
3994
|
promptTokens,
|
|
3985
|
-
responseTokens
|
|
3995
|
+
responseTokens,
|
|
3996
|
+
nowIso()
|
|
3986
3997
|
);
|
|
3987
3998
|
return Number(result.lastInsertRowid);
|
|
3988
3999
|
}
|
|
3989
4000
|
function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, inputSize, outputSize, success, filesInvolved) {
|
|
3990
4001
|
db.prepare(`
|
|
3991
|
-
INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved)
|
|
3992
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
4002
|
+
INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved, created_at)
|
|
4003
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3993
4004
|
`).run(
|
|
3994
4005
|
sessionId,
|
|
3995
4006
|
turnNumber,
|
|
@@ -3998,7 +4009,8 @@ function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, in
|
|
|
3998
4009
|
inputSize,
|
|
3999
4010
|
outputSize,
|
|
4000
4011
|
success ? 1 : 0,
|
|
4001
|
-
filesInvolved ? JSON.stringify(filesInvolved) : null
|
|
4012
|
+
filesInvolved ? JSON.stringify(filesInvolved) : null,
|
|
4013
|
+
nowIso()
|
|
4002
4014
|
);
|
|
4003
4015
|
}
|
|
4004
4016
|
function getLastProcessedLine(db, sessionId) {
|
|
@@ -4257,6 +4269,7 @@ var init_memory_db = __esm({
|
|
|
4257
4269
|
init_db_driver();
|
|
4258
4270
|
init_config();
|
|
4259
4271
|
init_memory_vector();
|
|
4272
|
+
init_timestamps();
|
|
4260
4273
|
init_db_backup();
|
|
4261
4274
|
init_rule_delivery();
|
|
4262
4275
|
init_rule_candidate_store();
|
|
@@ -15980,14 +15993,23 @@ __export(init_exports, {
|
|
|
15980
15993
|
getInstallerVersion: () => getInstallerVersion,
|
|
15981
15994
|
initMemoryDir: () => initMemoryDir,
|
|
15982
15995
|
installHooks: () => installHooks,
|
|
15996
|
+
installMassuShim: () => installMassuShim,
|
|
15983
15997
|
isTemplateName: () => isTemplateName,
|
|
15984
15998
|
listTemplates: () => listTemplates,
|
|
15999
|
+
massuHookIdentity: () => massuHookIdentity,
|
|
16000
|
+
massuRuntimeCliPath: () => massuRuntimeCliPath,
|
|
16001
|
+
massuRuntimeDir: () => massuRuntimeDir,
|
|
16002
|
+
massuShimBody: () => massuShimBody,
|
|
16003
|
+
massuShimPath: () => massuShimPath,
|
|
16004
|
+
materializeMassuRuntime: () => materializeMassuRuntime,
|
|
15985
16005
|
mergeHooksConfig: () => mergeHooksConfig,
|
|
15986
16006
|
parseInitArgs: () => parseInitArgs,
|
|
15987
16007
|
printInitHelp: () => printInitHelp,
|
|
15988
16008
|
registerMcpServer: () => registerMcpServer,
|
|
15989
16009
|
renderConfigYaml: () => renderConfigYaml,
|
|
15990
16010
|
resolveHooksDir: () => resolveHooksDir,
|
|
16011
|
+
resolveMassuRuntimeCli: () => resolveMassuRuntimeCli,
|
|
16012
|
+
resolveMassuShim: () => resolveMassuShim,
|
|
15991
16013
|
resolveTemplatesDir: () => resolveTemplatesDir,
|
|
15992
16014
|
runInit: () => runInit,
|
|
15993
16015
|
validateWrittenConfig: () => validateWrittenConfig,
|
|
@@ -15997,6 +16019,7 @@ import { closeSync as closeSync4, existsSync as existsSync19, fsyncSync as fsync
|
|
|
15997
16019
|
import { resolve as resolve9, basename as basename6, dirname as dirname12 } from "path";
|
|
15998
16020
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
15999
16021
|
import { homedir as homedir7 } from "os";
|
|
16022
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
16000
16023
|
import { stringify as yamlStringify, parse as yamlParse } from "yaml";
|
|
16001
16024
|
function detectFramework(projectRoot) {
|
|
16002
16025
|
const result = {
|
|
@@ -16570,11 +16593,8 @@ function registerMcpServer(projectRoot) {
|
|
|
16570
16593
|
return false;
|
|
16571
16594
|
}
|
|
16572
16595
|
const version = getInstallerVersion();
|
|
16573
|
-
|
|
16574
|
-
|
|
16575
|
-
command: "npx",
|
|
16576
|
-
args: ["-y", `@massu/core@${version}`]
|
|
16577
|
-
};
|
|
16596
|
+
const shim = resolveMassuShim();
|
|
16597
|
+
servers.massu = shim !== null ? { type: "stdio", command: shim, args: [version] } : { type: "stdio", command: "npx", args: ["-y", `@massu/core@${version}`] };
|
|
16578
16598
|
existing.mcpServers = servers;
|
|
16579
16599
|
writeFileSync5(mcpPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
|
|
16580
16600
|
return true;
|
|
@@ -16591,7 +16611,98 @@ function resolveHooksDir() {
|
|
|
16591
16611
|
}
|
|
16592
16612
|
return "node_modules/@massu/core/dist/hooks";
|
|
16593
16613
|
}
|
|
16594
|
-
function
|
|
16614
|
+
function massuRuntimeDir(version) {
|
|
16615
|
+
return resolve9(homedir7(), ".massu", "runtime", version);
|
|
16616
|
+
}
|
|
16617
|
+
function massuRuntimeCliPath(version) {
|
|
16618
|
+
return resolve9(massuRuntimeDir(version), "node_modules", "@massu", "core", "dist", "cli.js");
|
|
16619
|
+
}
|
|
16620
|
+
function resolveMassuRuntimeCli(version) {
|
|
16621
|
+
if (process.env.MASSU_NO_NODE_DIRECT === "1") return null;
|
|
16622
|
+
const cli = massuRuntimeCliPath(version);
|
|
16623
|
+
if (!existsSync19(cli)) return null;
|
|
16624
|
+
try {
|
|
16625
|
+
execFileSync2(process.execPath, [cli, "--version"], { stdio: "ignore", timeout: 2e4 });
|
|
16626
|
+
return cli;
|
|
16627
|
+
} catch {
|
|
16628
|
+
return null;
|
|
16629
|
+
}
|
|
16630
|
+
}
|
|
16631
|
+
function materializeMassuRuntime(version) {
|
|
16632
|
+
if (process.env.MASSU_NO_NODE_DIRECT === "1") return false;
|
|
16633
|
+
if (resolveMassuRuntimeCli(version) !== null) return true;
|
|
16634
|
+
const dir = massuRuntimeDir(version);
|
|
16635
|
+
try {
|
|
16636
|
+
mkdirSync10(dir, { recursive: true });
|
|
16637
|
+
execFileSync2(
|
|
16638
|
+
"npm",
|
|
16639
|
+
["install", "--prefix", dir, `@massu/core@${version}`, "--no-audit", "--no-fund", "--loglevel=error"],
|
|
16640
|
+
{ stdio: "ignore", timeout: 3e5 }
|
|
16641
|
+
);
|
|
16642
|
+
} catch {
|
|
16643
|
+
return false;
|
|
16644
|
+
}
|
|
16645
|
+
return resolveMassuRuntimeCli(version) !== null;
|
|
16646
|
+
}
|
|
16647
|
+
function massuShimPath() {
|
|
16648
|
+
return resolve9(homedir7(), ".massu", "bin", "massu-hook");
|
|
16649
|
+
}
|
|
16650
|
+
function massuShimBody() {
|
|
16651
|
+
return `#!/bin/sh
|
|
16652
|
+
# massu launcher shim \u2014 GENERATED, do not edit. Recreated by \`massu install-hooks\`.
|
|
16653
|
+
#
|
|
16654
|
+
# usage: massu-hook <version> <subcommand...>
|
|
16655
|
+
#
|
|
16656
|
+
# Resolves the massu runtime AT FIRE TIME so a deleted/moved runtime degrades to npx
|
|
16657
|
+
# instead of silently 404-ing the hook (the P-003 failure class). exec preserves stdin,
|
|
16658
|
+
# stdout, stderr and the exit code.
|
|
16659
|
+
set -u
|
|
16660
|
+
if [ "$#" -lt 1 ]; then
|
|
16661
|
+
echo "massu-hook: missing <version> argument" >&2
|
|
16662
|
+
exit 2
|
|
16663
|
+
fi
|
|
16664
|
+
MASSU_VERSION="$1"
|
|
16665
|
+
shift
|
|
16666
|
+
MASSU_CLI="\${HOME}/.massu/runtime/\${MASSU_VERSION}/node_modules/@massu/core/dist/cli.js"
|
|
16667
|
+
if [ -f "$MASSU_CLI" ]; then
|
|
16668
|
+
exec node "$MASSU_CLI" "$@"
|
|
16669
|
+
fi
|
|
16670
|
+
exec npx -y "@massu/core@\${MASSU_VERSION}" "$@"
|
|
16671
|
+
`;
|
|
16672
|
+
}
|
|
16673
|
+
function installMassuShim() {
|
|
16674
|
+
if (process.env.MASSU_NO_NODE_DIRECT === "1") return false;
|
|
16675
|
+
if (process.platform === "win32") return false;
|
|
16676
|
+
const shim = massuShimPath();
|
|
16677
|
+
try {
|
|
16678
|
+
mkdirSync10(dirname12(shim), { recursive: true });
|
|
16679
|
+
writeFileSync5(shim, massuShimBody(), "utf-8");
|
|
16680
|
+
chmodSync5(shim, 493);
|
|
16681
|
+
} catch {
|
|
16682
|
+
return false;
|
|
16683
|
+
}
|
|
16684
|
+
try {
|
|
16685
|
+
execFileSync2(shim, [], { stdio: "ignore", timeout: 1e4 });
|
|
16686
|
+
return false;
|
|
16687
|
+
} catch (e2) {
|
|
16688
|
+
const status = e2.status;
|
|
16689
|
+
return status === 2;
|
|
16690
|
+
}
|
|
16691
|
+
}
|
|
16692
|
+
function resolveMassuShim() {
|
|
16693
|
+
if (process.env.MASSU_NO_NODE_DIRECT === "1") return null;
|
|
16694
|
+
if (process.platform === "win32") return null;
|
|
16695
|
+
const shim = massuShimPath();
|
|
16696
|
+
if (!existsSync19(shim)) return null;
|
|
16697
|
+
try {
|
|
16698
|
+
execFileSync2(shim, [], { stdio: "ignore", timeout: 1e4 });
|
|
16699
|
+
return null;
|
|
16700
|
+
} catch (e2) {
|
|
16701
|
+
return e2.status === 2 ? shim : null;
|
|
16702
|
+
}
|
|
16703
|
+
}
|
|
16704
|
+
function hookCmd(version, hookName, shim) {
|
|
16705
|
+
if (shim !== null) return `"${shim}" ${version} hook-runner ${hookName}`;
|
|
16595
16706
|
return `npx -y @massu/core@${version} hook-runner ${hookName}`;
|
|
16596
16707
|
}
|
|
16597
16708
|
function getHookTimeout(name) {
|
|
@@ -16599,11 +16710,12 @@ function getHookTimeout(name) {
|
|
|
16599
16710
|
}
|
|
16600
16711
|
function buildHooksConfig(_hooksDir) {
|
|
16601
16712
|
const version = getInstallerVersion();
|
|
16713
|
+
const shim = resolveMassuShim();
|
|
16602
16714
|
return {
|
|
16603
16715
|
SessionStart: [
|
|
16604
16716
|
{
|
|
16605
16717
|
hooks: [
|
|
16606
|
-
{ type: "command", command: hookCmd(version, "session-start"), timeout: getHookTimeout("session-start") }
|
|
16718
|
+
{ type: "command", command: hookCmd(version, "session-start", shim), timeout: getHookTimeout("session-start") }
|
|
16607
16719
|
]
|
|
16608
16720
|
}
|
|
16609
16721
|
],
|
|
@@ -16618,26 +16730,26 @@ function buildHooksConfig(_hooksDir) {
|
|
|
16618
16730
|
{
|
|
16619
16731
|
matcher: "Bash|Write|Edit",
|
|
16620
16732
|
hooks: [
|
|
16621
|
-
{ type: "command", command: hookCmd(version, "pre-tool-use-gate"), timeout: getHookTimeout("pre-tool-use-gate") }
|
|
16733
|
+
{ type: "command", command: hookCmd(version, "pre-tool-use-gate", shim), timeout: getHookTimeout("pre-tool-use-gate") }
|
|
16622
16734
|
]
|
|
16623
16735
|
}
|
|
16624
16736
|
],
|
|
16625
16737
|
PostToolUse: [
|
|
16626
16738
|
{
|
|
16627
16739
|
hooks: [
|
|
16628
|
-
{ type: "command", command: hookCmd(version, "post-tool-use"), timeout: getHookTimeout("post-tool-use") },
|
|
16629
|
-
{ type: "command", command: hookCmd(version, "quality-event"), timeout: getHookTimeout("quality-event") },
|
|
16630
|
-
{ type: "command", command: hookCmd(version, "cost-tracker"), timeout: getHookTimeout("cost-tracker") }
|
|
16740
|
+
{ type: "command", command: hookCmd(version, "post-tool-use", shim), timeout: getHookTimeout("post-tool-use") },
|
|
16741
|
+
{ type: "command", command: hookCmd(version, "quality-event", shim), timeout: getHookTimeout("quality-event") },
|
|
16742
|
+
{ type: "command", command: hookCmd(version, "cost-tracker", shim), timeout: getHookTimeout("cost-tracker") }
|
|
16631
16743
|
]
|
|
16632
16744
|
},
|
|
16633
16745
|
{
|
|
16634
16746
|
matcher: "Edit|Write",
|
|
16635
16747
|
hooks: [
|
|
16636
|
-
{ type: "command", command: hookCmd(version, "post-edit-context"), timeout: getHookTimeout("post-edit-context") },
|
|
16748
|
+
{ type: "command", command: hookCmd(version, "post-edit-context", shim), timeout: getHookTimeout("post-edit-context") },
|
|
16637
16749
|
// Auto-learning pipeline — classifies failures and detects fixes on
|
|
16638
16750
|
// file changes. See Phase 5-6 of the autodetect plan.
|
|
16639
|
-
{ type: "command", command: hookCmd(version, "fix-detector"), timeout: getHookTimeout("fix-detector") },
|
|
16640
|
-
{ type: "command", command: hookCmd(version, "classify-failure"), timeout: getHookTimeout("classify-failure") }
|
|
16751
|
+
{ type: "command", command: hookCmd(version, "fix-detector", shim), timeout: getHookTimeout("fix-detector") },
|
|
16752
|
+
{ type: "command", command: hookCmd(version, "classify-failure", shim), timeout: getHookTimeout("classify-failure") }
|
|
16641
16753
|
]
|
|
16642
16754
|
},
|
|
16643
16755
|
{
|
|
@@ -16645,35 +16757,35 @@ function buildHooksConfig(_hooksDir) {
|
|
|
16645
16757
|
hooks: [
|
|
16646
16758
|
// Incident + rule enforcement pipelines fire on Write-only (incidents
|
|
16647
16759
|
// are authored as .md files; rules are enforced after new-file drops).
|
|
16648
|
-
{ type: "command", command: hookCmd(version, "incident-pipeline"), timeout: getHookTimeout("incident-pipeline") },
|
|
16649
|
-
{ type: "command", command: hookCmd(version, "rule-enforcement-pipeline"), timeout: getHookTimeout("rule-enforcement-pipeline") }
|
|
16760
|
+
{ type: "command", command: hookCmd(version, "incident-pipeline", shim), timeout: getHookTimeout("incident-pipeline") },
|
|
16761
|
+
{ type: "command", command: hookCmd(version, "rule-enforcement-pipeline", shim), timeout: getHookTimeout("rule-enforcement-pipeline") }
|
|
16650
16762
|
]
|
|
16651
16763
|
}
|
|
16652
16764
|
],
|
|
16653
16765
|
Stop: [
|
|
16654
16766
|
{
|
|
16655
16767
|
hooks: [
|
|
16656
|
-
{ type: "command", command: hookCmd(version, "session-end"), timeout: getHookTimeout("session-end") },
|
|
16768
|
+
{ type: "command", command: hookCmd(version, "session-end", shim), timeout: getHookTimeout("session-end") },
|
|
16657
16769
|
// Session-end auto-learning aggregation (failure-class roll-up).
|
|
16658
|
-
{ type: "command", command: hookCmd(version, "auto-learning-pipeline"), timeout: getHookTimeout("auto-learning-pipeline") }
|
|
16770
|
+
{ type: "command", command: hookCmd(version, "auto-learning-pipeline", shim), timeout: getHookTimeout("auto-learning-pipeline") }
|
|
16659
16771
|
]
|
|
16660
16772
|
}
|
|
16661
16773
|
],
|
|
16662
16774
|
PreCompact: [
|
|
16663
16775
|
{
|
|
16664
16776
|
hooks: [
|
|
16665
|
-
{ type: "command", command: hookCmd(version, "pre-compact"), timeout: getHookTimeout("pre-compact") }
|
|
16777
|
+
{ type: "command", command: hookCmd(version, "pre-compact", shim), timeout: getHookTimeout("pre-compact") }
|
|
16666
16778
|
]
|
|
16667
16779
|
}
|
|
16668
16780
|
],
|
|
16669
16781
|
UserPromptSubmit: [
|
|
16670
16782
|
{
|
|
16671
16783
|
hooks: [
|
|
16672
|
-
{ type: "command", command: hookCmd(version, "user-prompt"), timeout: getHookTimeout("user-prompt") },
|
|
16673
|
-
{ type: "command", command: hookCmd(version, "intent-suggester"), timeout: getHookTimeout("intent-suggester") },
|
|
16784
|
+
{ type: "command", command: hookCmd(version, "user-prompt", shim), timeout: getHookTimeout("user-prompt") },
|
|
16785
|
+
{ type: "command", command: hookCmd(version, "intent-suggester", shim), timeout: getHookTimeout("intent-suggester") },
|
|
16674
16786
|
// plan-living-memory-slice-1 P3-002: automatic relevant-recall.
|
|
16675
16787
|
// Injects a compact "🧠 Relevant memory" block; fails open (empty).
|
|
16676
|
-
{ type: "command", command: hookCmd(version, "memory-recall"), timeout: getHookTimeout("memory-recall") }
|
|
16788
|
+
{ type: "command", command: hookCmd(version, "memory-recall", shim), timeout: getHookTimeout("memory-recall") }
|
|
16677
16789
|
]
|
|
16678
16790
|
}
|
|
16679
16791
|
]
|
|
@@ -16711,21 +16823,31 @@ function mergeHooksConfig(existing, additions) {
|
|
|
16711
16823
|
}
|
|
16712
16824
|
return merged;
|
|
16713
16825
|
}
|
|
16826
|
+
function massuHookIdentity(command) {
|
|
16827
|
+
if (!/@massu\/core|[/\\]\.?massu[/\\]/.test(command)) return null;
|
|
16828
|
+
const m3 = /\bhook-runner\s+([A-Za-z0-9._-]+)/.exec(command);
|
|
16829
|
+
return m3 ? m3[1] : null;
|
|
16830
|
+
}
|
|
16714
16831
|
function mergeHookEntries(existing, additions) {
|
|
16715
|
-
const
|
|
16832
|
+
const seenCommands = /* @__PURE__ */ new Set();
|
|
16833
|
+
const seenIdentities = /* @__PURE__ */ new Set();
|
|
16716
16834
|
const result = [];
|
|
16717
|
-
|
|
16718
|
-
if (!entry || typeof entry.command !== "string")
|
|
16719
|
-
|
|
16720
|
-
|
|
16721
|
-
|
|
16722
|
-
|
|
16723
|
-
|
|
16724
|
-
|
|
16725
|
-
|
|
16726
|
-
|
|
16835
|
+
const take = (entry) => {
|
|
16836
|
+
if (!entry || typeof entry.command !== "string") return;
|
|
16837
|
+
const identity = massuHookIdentity(entry.command);
|
|
16838
|
+
if (identity !== null) {
|
|
16839
|
+
if (seenIdentities.has(identity)) return;
|
|
16840
|
+
seenIdentities.add(identity);
|
|
16841
|
+
seenCommands.add(entry.command);
|
|
16842
|
+
result.push(entry);
|
|
16843
|
+
return;
|
|
16844
|
+
}
|
|
16845
|
+
if (seenCommands.has(entry.command)) return;
|
|
16846
|
+
seenCommands.add(entry.command);
|
|
16727
16847
|
result.push(entry);
|
|
16728
|
-
}
|
|
16848
|
+
};
|
|
16849
|
+
for (const entry of additions ?? []) take(entry);
|
|
16850
|
+
for (const entry of existing ?? []) take(entry);
|
|
16729
16851
|
return result;
|
|
16730
16852
|
}
|
|
16731
16853
|
function installHooks(projectRoot) {
|
|
@@ -18716,6 +18838,16 @@ async function runInstallHooks() {
|
|
|
18716
18838
|
console.log("Massu AI - Hook Installation");
|
|
18717
18839
|
console.log("============================");
|
|
18718
18840
|
console.log("");
|
|
18841
|
+
const version = getInstallerVersion();
|
|
18842
|
+
const shimOk = installMassuShim();
|
|
18843
|
+
const runtimeOk = materializeMassuRuntime(version);
|
|
18844
|
+
if (shimOk && runtimeOk) {
|
|
18845
|
+
console.log(" Launcher: shim + runtime ready (fast path, ~9x cheaper per hook)");
|
|
18846
|
+
} else if (shimOk) {
|
|
18847
|
+
console.log(" Launcher: shim ready, runtime unavailable \u2014 shim will fall back to npx at fire time");
|
|
18848
|
+
} else {
|
|
18849
|
+
console.log(" Launcher: npx (shim unavailable \u2014 works, ~0.9s slower per hook)");
|
|
18850
|
+
}
|
|
18719
18851
|
const { count } = installHooks(projectRoot);
|
|
18720
18852
|
console.log(` Installed ${count} hooks in .claude/settings.local.json`);
|
|
18721
18853
|
console.log("");
|
|
@@ -20454,7 +20586,7 @@ import { createHash as createHash8 } from "crypto";
|
|
|
20454
20586
|
function renderMemoryFiles(db, candidates, opts) {
|
|
20455
20587
|
const config = opts.config ?? resolveMemoryFilesConfig();
|
|
20456
20588
|
if (!config.renderEnabled) {
|
|
20457
|
-
return EMPTY("render_disabled");
|
|
20589
|
+
return EMPTY("render_disabled", candidates.length);
|
|
20458
20590
|
}
|
|
20459
20591
|
const dryRun = opts.dryRun === true;
|
|
20460
20592
|
const now = opts.now ?? Date.now();
|
|
@@ -20463,7 +20595,7 @@ function renderMemoryFiles(db, candidates, opts) {
|
|
|
20463
20595
|
});
|
|
20464
20596
|
const { memoryDir } = opts;
|
|
20465
20597
|
if (!existsSync33(memoryDir)) {
|
|
20466
|
-
return EMPTY("no_memory_dir");
|
|
20598
|
+
return EMPTY("no_memory_dir", candidates.length);
|
|
20467
20599
|
}
|
|
20468
20600
|
try {
|
|
20469
20601
|
return withMemoryIndexLock(
|
|
@@ -20472,10 +20604,10 @@ function renderMemoryFiles(db, candidates, opts) {
|
|
|
20472
20604
|
);
|
|
20473
20605
|
} catch (err) {
|
|
20474
20606
|
if (err instanceof MemoryIndexLockBusy) {
|
|
20475
|
-
return EMPTY("lock_busy");
|
|
20607
|
+
return EMPTY("lock_busy", candidates.length);
|
|
20476
20608
|
}
|
|
20477
20609
|
audit("memory_file_render_refused", { reason: "unexpected_error", error: String(err) });
|
|
20478
|
-
return EMPTY("error");
|
|
20610
|
+
return EMPTY("error", candidates.length);
|
|
20479
20611
|
}
|
|
20480
20612
|
}
|
|
20481
20613
|
function renderLocked(db, candidates, o) {
|
|
@@ -20485,11 +20617,14 @@ function renderLocked(db, candidates, o) {
|
|
|
20485
20617
|
try {
|
|
20486
20618
|
tombstones = readTombstones(memoryDir);
|
|
20487
20619
|
} catch {
|
|
20488
|
-
return EMPTY("tombstone_ledger_unreadable");
|
|
20620
|
+
return EMPTY("tombstone_ledger_unreadable", candidates.length);
|
|
20489
20621
|
}
|
|
20490
20622
|
const ownerOf = relPathOwnerLookup(db);
|
|
20491
20623
|
const planned = [];
|
|
20492
|
-
|
|
20624
|
+
let unchanged = 0;
|
|
20625
|
+
let capped = 0;
|
|
20626
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
20627
|
+
const c2 = candidates[i];
|
|
20493
20628
|
if (!isLocalOrigin(c2.origin)) {
|
|
20494
20629
|
refusals.push({ name: c2.name, reason: "non_local_origin", detail: c2.origin || "unknown" });
|
|
20495
20630
|
audit("memory_file_render_refused", { name: c2.name, reason: "non_local_origin" });
|
|
@@ -20545,7 +20680,10 @@ function renderLocked(db, candidates, o) {
|
|
|
20545
20680
|
continue;
|
|
20546
20681
|
}
|
|
20547
20682
|
const next = composeFile(c2, home);
|
|
20548
|
-
if (next !== null && next === existing)
|
|
20683
|
+
if (next !== null && next === existing) {
|
|
20684
|
+
unchanged++;
|
|
20685
|
+
continue;
|
|
20686
|
+
}
|
|
20549
20687
|
}
|
|
20550
20688
|
const content = composeFile(c2, home);
|
|
20551
20689
|
if (content === null) {
|
|
@@ -20553,9 +20691,24 @@ function renderLocked(db, candidates, o) {
|
|
|
20553
20691
|
continue;
|
|
20554
20692
|
}
|
|
20555
20693
|
planned.push({ c: c2, relPath, absPath, content });
|
|
20556
|
-
if (planned.length >= config.renderMaxFilesPerSession)
|
|
20694
|
+
if (planned.length >= config.renderMaxFilesPerSession) {
|
|
20695
|
+
capped = candidates.length - (i + 1);
|
|
20696
|
+
break;
|
|
20697
|
+
}
|
|
20698
|
+
}
|
|
20699
|
+
const accounted = planned.length + refusals.length + unchanged + capped;
|
|
20700
|
+
if (accounted !== candidates.length) {
|
|
20701
|
+
audit("memory_file_render_refused", {
|
|
20702
|
+
reason: "accounting_leak",
|
|
20703
|
+
considered: candidates.length,
|
|
20704
|
+
accounted
|
|
20705
|
+
});
|
|
20706
|
+
throw new RenderAccountingLeak(
|
|
20707
|
+
`render did not account for every candidate: considered=${candidates.length} but accounted=${accounted} (planned=${planned.length} refused=${refusals.length} unchanged=${unchanged} capped=${capped}). A candidate was dropped without a bucket.`
|
|
20708
|
+
);
|
|
20557
20709
|
}
|
|
20558
20710
|
const indexLines = buildIndexLines(db, memoryDir, planned, config);
|
|
20711
|
+
const books = { considered: candidates.length, unchanged, capped };
|
|
20559
20712
|
if (dryRun) {
|
|
20560
20713
|
return {
|
|
20561
20714
|
enabled: true,
|
|
@@ -20563,11 +20716,20 @@ function renderLocked(db, candidates, o) {
|
|
|
20563
20716
|
written: planned.map((p19) => p19.relPath),
|
|
20564
20717
|
refusals,
|
|
20565
20718
|
indexLines,
|
|
20566
|
-
bytesWritten: 0
|
|
20719
|
+
bytesWritten: 0,
|
|
20720
|
+
...books
|
|
20567
20721
|
};
|
|
20568
20722
|
}
|
|
20569
20723
|
if (planned.length === 0 && indexLines.length === 0) {
|
|
20570
|
-
return {
|
|
20724
|
+
return {
|
|
20725
|
+
enabled: true,
|
|
20726
|
+
dryRun: false,
|
|
20727
|
+
written: [],
|
|
20728
|
+
refusals,
|
|
20729
|
+
indexLines: [],
|
|
20730
|
+
bytesWritten: 0,
|
|
20731
|
+
...books
|
|
20732
|
+
};
|
|
20571
20733
|
}
|
|
20572
20734
|
if (!hasFreshBackup(memoryDir, home)) {
|
|
20573
20735
|
try {
|
|
@@ -20577,7 +20739,7 @@ function renderLocked(db, candidates, o) {
|
|
|
20577
20739
|
reason: "backup_failed",
|
|
20578
20740
|
error: err instanceof BackupError ? err.message : String(err)
|
|
20579
20741
|
});
|
|
20580
|
-
return EMPTY("backup_failed");
|
|
20742
|
+
return EMPTY("backup_failed", candidates.length);
|
|
20581
20743
|
}
|
|
20582
20744
|
}
|
|
20583
20745
|
const indexPath = o.indexPath ?? join19(memoryDir, "MEMORY.md");
|
|
@@ -20645,7 +20807,10 @@ function renderLocked(db, candidates, o) {
|
|
|
20645
20807
|
errors: restored.errors
|
|
20646
20808
|
});
|
|
20647
20809
|
return {
|
|
20648
|
-
...EMPTY("rollback_incomplete"),
|
|
20810
|
+
...EMPTY("rollback_incomplete", candidates.length),
|
|
20811
|
+
// The books were balanced before the write began; keep them rather than zeroing.
|
|
20812
|
+
unchanged,
|
|
20813
|
+
capped,
|
|
20649
20814
|
enabled: true,
|
|
20650
20815
|
refusals: [
|
|
20651
20816
|
...refusals,
|
|
@@ -20654,9 +20819,9 @@ function renderLocked(db, candidates, o) {
|
|
|
20654
20819
|
};
|
|
20655
20820
|
}
|
|
20656
20821
|
audit("memory_file_render_refused", { reason: "write_failed", error: String(err) });
|
|
20657
|
-
return { ...EMPTY("write_failed"), enabled: true, refusals };
|
|
20822
|
+
return { ...EMPTY("write_failed", candidates.length), enabled: true, refusals, unchanged, capped };
|
|
20658
20823
|
}
|
|
20659
|
-
return { enabled: true, dryRun: false, written, refusals, indexLines, bytesWritten };
|
|
20824
|
+
return { enabled: true, dryRun: false, written, refusals, indexLines, bytesWritten, ...books };
|
|
20660
20825
|
}
|
|
20661
20826
|
function sha2562(s) {
|
|
20662
20827
|
return createHash8("sha256").update(s, "utf8").digest("hex");
|
|
@@ -20725,7 +20890,7 @@ function buildIndexLines(db, memoryDir, planned, config) {
|
|
|
20725
20890
|
function oneLine(s) {
|
|
20726
20891
|
return s.replace(/[\r\n]+/g, " ").slice(0, 120);
|
|
20727
20892
|
}
|
|
20728
|
-
var EMPTY;
|
|
20893
|
+
var RenderAccountingLeak, EMPTY;
|
|
20729
20894
|
var init_memory_renderer = __esm({
|
|
20730
20895
|
"src/memory-renderer.ts"() {
|
|
20731
20896
|
"use strict";
|
|
@@ -20740,14 +20905,22 @@ var init_memory_renderer = __esm({
|
|
|
20740
20905
|
init_memory_index_region();
|
|
20741
20906
|
init_memory_files_config();
|
|
20742
20907
|
init_memory_file_ingest();
|
|
20743
|
-
|
|
20908
|
+
RenderAccountingLeak = class extends Error {
|
|
20909
|
+
};
|
|
20910
|
+
EMPTY = (reason, considered = 0) => ({
|
|
20744
20911
|
enabled: false,
|
|
20745
20912
|
dryRun: false,
|
|
20746
20913
|
written: [],
|
|
20747
20914
|
refusals: [],
|
|
20748
20915
|
indexLines: [],
|
|
20749
20916
|
bytesWritten: 0,
|
|
20750
|
-
skippedReason: reason
|
|
20917
|
+
skippedReason: reason,
|
|
20918
|
+
// An early gate refused, so every candidate was dropped — by that ONE named reason.
|
|
20919
|
+
// Reporting `considered` here is what distinguishes "nothing to do" from "walked away
|
|
20920
|
+
// from 59 rows because the lock was busy".
|
|
20921
|
+
considered,
|
|
20922
|
+
unchanged: 0,
|
|
20923
|
+
capped: 0
|
|
20751
20924
|
});
|
|
20752
20925
|
}
|
|
20753
20926
|
});
|
|
@@ -20938,25 +21111,46 @@ var init_memory_render_cli = __esm({
|
|
|
20938
21111
|
function loadRenderCandidates(db) {
|
|
20939
21112
|
const cfg = resolveMemoryFilesConfig();
|
|
20940
21113
|
const typePlaceholders = RENDERABLE_MEMORY_TYPES.map(() => "?").join(", ");
|
|
21114
|
+
const WHERE = `WHERE importance >= ?
|
|
21115
|
+
AND COALESCE(expired_at_epoch, 0) = 0
|
|
21116
|
+
AND type IN (${typePlaceholders})`;
|
|
21117
|
+
const { population } = db.prepare(`SELECT COUNT(*) AS population FROM observations ${WHERE}`).get(cfg.renderMinImportance, ...RENDERABLE_MEMORY_TYPES);
|
|
20941
21118
|
const rows = db.prepare(
|
|
20942
21119
|
`SELECT id, title, detail, importance, COALESCE(origin, 'local') AS origin
|
|
20943
21120
|
FROM observations
|
|
20944
|
-
|
|
20945
|
-
AND COALESCE(expired_at_epoch, 0) = 0
|
|
20946
|
-
AND type IN (${typePlaceholders})
|
|
21121
|
+
${WHERE}
|
|
20947
21122
|
ORDER BY importance DESC, created_at_epoch DESC
|
|
20948
|
-
LIMIT
|
|
21123
|
+
LIMIT ${CANDIDATE_WINDOW}`
|
|
20949
21124
|
).all(cfg.renderMinImportance, ...RENDERABLE_MEMORY_TYPES);
|
|
20950
|
-
|
|
20951
|
-
|
|
20952
|
-
|
|
20953
|
-
|
|
20954
|
-
|
|
20955
|
-
|
|
20956
|
-
|
|
20957
|
-
|
|
21125
|
+
const reingest = rows.filter((r2) => r2.title.startsWith("[memory-file]"));
|
|
21126
|
+
const kept = rows.filter((r2) => !r2.title.startsWith("[memory-file]"));
|
|
21127
|
+
const excluded = reingest.length ? [{ reason: EXCLUSION_MEMORY_FILE_REINGEST, count: reingest.length }] : [];
|
|
21128
|
+
const ledger = {
|
|
21129
|
+
population,
|
|
21130
|
+
windowed: rows.length,
|
|
21131
|
+
truncatedByWindow: Math.max(0, population - rows.length),
|
|
21132
|
+
excluded,
|
|
21133
|
+
returned: kept.length
|
|
21134
|
+
};
|
|
21135
|
+
const accounted = ledger.returned + ledger.truncatedByWindow + excluded.reduce((n, e2) => n + e2.count, 0);
|
|
21136
|
+
if (accounted !== population) {
|
|
21137
|
+
throw new CandidateAccountingLeak(
|
|
21138
|
+
`candidate ledger does not balance: population=${population} but accounted=${accounted} (returned=${ledger.returned} truncated=${ledger.truncatedByWindow} excluded=${JSON.stringify(excluded)}). A row was dropped without a bucket.`
|
|
21139
|
+
);
|
|
21140
|
+
}
|
|
21141
|
+
return {
|
|
21142
|
+
ledger,
|
|
21143
|
+
candidates: kept.map((r2) => ({
|
|
21144
|
+
observationId: r2.id,
|
|
21145
|
+
name: r2.title,
|
|
21146
|
+
title: r2.title,
|
|
21147
|
+
body: r2.detail ?? "",
|
|
21148
|
+
importance: r2.importance,
|
|
21149
|
+
origin: r2.origin
|
|
21150
|
+
}))
|
|
21151
|
+
};
|
|
20958
21152
|
}
|
|
20959
|
-
var RENDERABLE_MEMORY_TYPES;
|
|
21153
|
+
var RENDERABLE_MEMORY_TYPES, CANDIDATE_WINDOW, EXCLUSION_MEMORY_FILE_REINGEST, CandidateAccountingLeak;
|
|
20960
21154
|
var init_memory_render_candidates = __esm({
|
|
20961
21155
|
"src/memory-render-candidates.ts"() {
|
|
20962
21156
|
"use strict";
|
|
@@ -20967,6 +21161,10 @@ var init_memory_render_candidates = __esm({
|
|
|
20967
21161
|
"incident_near_miss",
|
|
20968
21162
|
"cr_violation"
|
|
20969
21163
|
];
|
|
21164
|
+
CANDIDATE_WINDOW = 50;
|
|
21165
|
+
EXCLUSION_MEMORY_FILE_REINGEST = "memory_file_reingest";
|
|
21166
|
+
CandidateAccountingLeak = class extends Error {
|
|
21167
|
+
};
|
|
20970
21168
|
}
|
|
20971
21169
|
});
|
|
20972
21170
|
|
|
@@ -21046,13 +21244,13 @@ function pruneNoiseObservations(db, opts = {}) {
|
|
|
21046
21244
|
const counts = countNoise(db);
|
|
21047
21245
|
if (dryRun || counts.total === 0) return { counts, expired: 0, dryRun };
|
|
21048
21246
|
const nowSec = opts.now ?? Math.floor(Date.now() / 1e3);
|
|
21049
|
-
const
|
|
21247
|
+
const nowIso2 = new Date(nowSec * 1e3).toISOString();
|
|
21050
21248
|
const res = db.prepare(
|
|
21051
21249
|
`UPDATE observations
|
|
21052
21250
|
SET expired_at = ?, expired_at_epoch = ?, valid_to = ?, valid_to_epoch = ?
|
|
21053
21251
|
WHERE COALESCE(expired_at_epoch, 0) = 0
|
|
21054
21252
|
AND ((${DA_PREDICATE}) OR (${FT_DECISION_PREDICATE}) OR (${DB_PREDICATE}))`
|
|
21055
|
-
).run(
|
|
21253
|
+
).run(nowIso2, nowSec, nowIso2, nowSec);
|
|
21056
21254
|
return { counts, expired: res.changes, dryRun: false };
|
|
21057
21255
|
}
|
|
21058
21256
|
var DA_PREDICATE, FT_DECISION_PREDICATE, DB_PREDICATE;
|
|
@@ -21097,7 +21295,7 @@ async function runMemoryRenderCli(sub, args2) {
|
|
|
21097
21295
|
};
|
|
21098
21296
|
}
|
|
21099
21297
|
const cfg = resolveMemoryFilesConfig();
|
|
21100
|
-
const candidates = loadRenderCandidates(db);
|
|
21298
|
+
const { candidates, ledger } = loadRenderCandidates(db);
|
|
21101
21299
|
const result = renderMemoryFiles(db, candidates, {
|
|
21102
21300
|
memoryDir,
|
|
21103
21301
|
dryRun: true,
|
|
@@ -21112,6 +21310,19 @@ async function runMemoryRenderCli(sub, args2) {
|
|
|
21112
21310
|
}
|
|
21113
21311
|
lines.push(`Massu would write ${result.written.length} file(s):`);
|
|
21114
21312
|
for (const f2 of result.written) lines.push(` ${f2}`);
|
|
21313
|
+
lines.push("", "Where every row went:");
|
|
21314
|
+
lines.push(` candidate population (type+importance+expiry) : ${ledger.population}`);
|
|
21315
|
+
if (ledger.truncatedByWindow > 0) {
|
|
21316
|
+
lines.push(` truncated by the row window : ${ledger.truncatedByWindow}`);
|
|
21317
|
+
}
|
|
21318
|
+
for (const e2 of ledger.excluded) {
|
|
21319
|
+
lines.push(` excluded: ${e2.reason.padEnd(30)} : ${e2.count}`);
|
|
21320
|
+
}
|
|
21321
|
+
lines.push(` -> reached the renderer : ${ledger.returned}`);
|
|
21322
|
+
lines.push(
|
|
21323
|
+
` renderer: considered ${result.considered} = written ${result.written.length} + refused ${result.refusals.length} + unchanged ${result.unchanged} + capped ${result.capped}`
|
|
21324
|
+
);
|
|
21325
|
+
lines.push(` skipped: ${result.skippedReason ?? "(none)"}`);
|
|
21115
21326
|
if (result.indexLines.length > 0) {
|
|
21116
21327
|
lines.push("", "MEMORY.md \u2014 inside the massu:learned region only:");
|
|
21117
21328
|
for (const l2 of result.indexLines) lines.push(` ${l2}`);
|
|
@@ -23270,7 +23481,7 @@ async function pullInstalledPackRules(db, opts = {}) {
|
|
|
23270
23481
|
}
|
|
23271
23482
|
const packs = parsePacks(envelope.packs_json);
|
|
23272
23483
|
const result = { ...ZERO };
|
|
23273
|
-
const
|
|
23484
|
+
const nowIso2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
23274
23485
|
for (const pack of packs) {
|
|
23275
23486
|
if (!isValidInstalledPack(pack)) continue;
|
|
23276
23487
|
for (const rawRule of pack.rules) {
|
|
@@ -23287,7 +23498,7 @@ async function pullInstalledPackRules(db, opts = {}) {
|
|
|
23287
23498
|
result.skipped += 1;
|
|
23288
23499
|
continue;
|
|
23289
23500
|
}
|
|
23290
|
-
materializeCandidate(projectRoot, candidatePath, promptHash, rule, pack, signedOrgId,
|
|
23501
|
+
materializeCandidate(projectRoot, candidatePath, promptHash, rule, pack, signedOrgId, nowIso2, opts.home ?? homedir20());
|
|
23291
23502
|
result.materialized += 1;
|
|
23292
23503
|
}
|
|
23293
23504
|
}
|
|
@@ -23334,7 +23545,7 @@ function alreadyApplied(db, promptHash) {
|
|
|
23334
23545
|
return false;
|
|
23335
23546
|
}
|
|
23336
23547
|
}
|
|
23337
|
-
function materializeCandidate(projectRoot, candidatePath, promptHash, rule, pack, orgId,
|
|
23548
|
+
function materializeCandidate(projectRoot, candidatePath, promptHash, rule, pack, orgId, nowIso2, home) {
|
|
23338
23549
|
const promptText = `${rule.title}: ${rule.description}`.replace(/\n+/g, " ").slice(0, 200) || `pack rule ${promptHash}`;
|
|
23339
23550
|
const draftText = rule.pattern ?? rule.check ?? rule.description;
|
|
23340
23551
|
const hardened = isExecutableDestination(rule.destination);
|
|
@@ -23350,7 +23561,7 @@ function materializeCandidate(projectRoot, candidatePath, promptHash, rule, pack
|
|
|
23350
23561
|
score: 0,
|
|
23351
23562
|
signals: [],
|
|
23352
23563
|
prior_turn_files: [],
|
|
23353
|
-
timestamp:
|
|
23564
|
+
timestamp: nowIso2,
|
|
23354
23565
|
session_id: `pack:${pack.slug}`,
|
|
23355
23566
|
// Provenance (P2-002/P2-003): the applier's team-origin / hardened apply-gate
|
|
23356
23567
|
// keys on this. `origin:'pack'` rides the SAME gate as a team origin
|
|
@@ -23362,7 +23573,7 @@ function materializeCandidate(projectRoot, candidatePath, promptHash, rule, pack
|
|
|
23362
23573
|
origin: "pack",
|
|
23363
23574
|
org_id: orgId,
|
|
23364
23575
|
promoted_by: `pack:${pack.slug}`,
|
|
23365
|
-
promoted_at:
|
|
23576
|
+
promoted_at: nowIso2,
|
|
23366
23577
|
signature_verified: true,
|
|
23367
23578
|
...applyMac ? { apply_mac: applyMac } : {},
|
|
23368
23579
|
pack_slug: pack.slug,
|
|
@@ -34562,7 +34773,7 @@ function indexAllKnowledge(db) {
|
|
|
34562
34773
|
files.push(...docsFiles);
|
|
34563
34774
|
}
|
|
34564
34775
|
const now = /* @__PURE__ */ new Date();
|
|
34565
|
-
const
|
|
34776
|
+
const nowIso2 = now.toISOString();
|
|
34566
34777
|
const nowEpoch = now.getTime();
|
|
34567
34778
|
const transaction = db.transaction(() => {
|
|
34568
34779
|
db.exec("DROP TRIGGER IF EXISTS kc_fts_delete");
|
|
@@ -34606,7 +34817,7 @@ function indexAllKnowledge(db) {
|
|
|
34606
34817
|
const category = categorizeFile(filePath);
|
|
34607
34818
|
const title = extractTitle(content, filePath);
|
|
34608
34819
|
const description = extractDescription2(content);
|
|
34609
|
-
const result = insertDoc.run(relPath, category, title, description, hash,
|
|
34820
|
+
const result = insertDoc.run(relPath, category, title, description, hash, nowIso2, nowEpoch);
|
|
34610
34821
|
const docId = result.lastInsertRowid;
|
|
34611
34822
|
stats.filesIndexed++;
|
|
34612
34823
|
const sections = parseSections(content, filePath);
|