@massu/core 2.5.0 → 2.5.2
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 +231 -0
- package/commands/massu-bearings.md +1 -1
- package/commands/massu-recap.md +16 -5
- package/dist/cli.js +716 -258
- package/dist/hooks/auto-learning-pipeline.js +297 -56
- package/dist/hooks/classify-failure.js +323 -25
- package/dist/hooks/cost-tracker.js +47 -10
- package/dist/hooks/fix-detector.js +94 -10
- package/dist/hooks/incident-pipeline.js +77 -10
- package/dist/hooks/intent-suggester.js +77 -10
- package/dist/hooks/memory-recall.js +77 -10
- package/dist/hooks/post-edit-context.js +125 -11
- package/dist/hooks/post-tool-use.js +44 -7
- package/dist/hooks/pre-compact.js +43 -7
- package/dist/hooks/pre-delete-check.js +125 -13
- package/dist/hooks/pre-tool-use-gate.js +135 -17
- package/dist/hooks/quality-event.js +47 -10
- package/dist/hooks/rule-enforcement-pipeline.js +77 -10
- package/dist/hooks/security-gate.js +75 -10
- package/dist/hooks/session-end.js +43 -7
- package/dist/hooks/session-start.js +144 -38
- package/dist/hooks/user-prompt.js +43 -7
- package/package.json +1 -1
|
@@ -703,6 +703,13 @@ var init_config = __esm({
|
|
|
703
703
|
});
|
|
704
704
|
LanguageFrameworkEntrySchema = z2.object({
|
|
705
705
|
framework: z2.string().optional(),
|
|
706
|
+
// `source_dirs` is emitted by `init` (commands/init.ts:417) and existence-checked
|
|
707
|
+
// by config validation (init.ts:868-876), but until 2026-08-14 it survived only via
|
|
708
|
+
// `.passthrough()` — declared in the file, untyped in the schema. `lib/source-layout.ts`
|
|
709
|
+
// derives the index builders' candidate set from it, so it is typed here rather than
|
|
710
|
+
// read as an unknown: a consumer that has to shape-check its own SoT is one branch away
|
|
711
|
+
// from silently contributing `undefined` to a path predicate.
|
|
712
|
+
source_dirs: z2.array(z2.string()).optional(),
|
|
706
713
|
test_framework: z2.string().optional(),
|
|
707
714
|
test: z2.string().optional(),
|
|
708
715
|
runtime: z2.string().optional(),
|
|
@@ -1345,6 +1352,16 @@ var init_memory_vector = __esm({
|
|
|
1345
1352
|
}
|
|
1346
1353
|
});
|
|
1347
1354
|
|
|
1355
|
+
// src/lib/timestamps.ts
|
|
1356
|
+
function nowIso() {
|
|
1357
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1358
|
+
}
|
|
1359
|
+
var init_timestamps = __esm({
|
|
1360
|
+
"src/lib/timestamps.ts"() {
|
|
1361
|
+
"use strict";
|
|
1362
|
+
}
|
|
1363
|
+
});
|
|
1364
|
+
|
|
1348
1365
|
// src/db-backup.ts
|
|
1349
1366
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync, statSync, unlinkSync, copyFileSync, rmSync as rmSync4 } from "fs";
|
|
1350
1367
|
import { resolve as resolve3, join as join3, basename, dirname as dirname4 } from "path";
|
|
@@ -3605,8 +3622,8 @@ function linkSessionToTask(db, sessionId, taskId) {
|
|
|
3605
3622
|
}
|
|
3606
3623
|
function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantResponse, toolCallsJson, toolCallCount, promptTokens, responseTokens) {
|
|
3607
3624
|
const result = db.prepare(`
|
|
3608
|
-
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens)
|
|
3609
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3625
|
+
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens, created_at)
|
|
3626
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3610
3627
|
`).run(
|
|
3611
3628
|
sessionId,
|
|
3612
3629
|
turnNumber,
|
|
@@ -3615,14 +3632,15 @@ function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantRes
|
|
|
3615
3632
|
toolCallsJson,
|
|
3616
3633
|
toolCallCount,
|
|
3617
3634
|
promptTokens,
|
|
3618
|
-
responseTokens
|
|
3635
|
+
responseTokens,
|
|
3636
|
+
nowIso()
|
|
3619
3637
|
);
|
|
3620
3638
|
return Number(result.lastInsertRowid);
|
|
3621
3639
|
}
|
|
3622
3640
|
function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, inputSize, outputSize, success, filesInvolved) {
|
|
3623
3641
|
db.prepare(`
|
|
3624
|
-
INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved)
|
|
3625
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3642
|
+
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)
|
|
3643
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3626
3644
|
`).run(
|
|
3627
3645
|
sessionId,
|
|
3628
3646
|
turnNumber,
|
|
@@ -3631,7 +3649,8 @@ function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, in
|
|
|
3631
3649
|
inputSize,
|
|
3632
3650
|
outputSize,
|
|
3633
3651
|
success ? 1 : 0,
|
|
3634
|
-
filesInvolved ? JSON.stringify(filesInvolved) : null
|
|
3652
|
+
filesInvolved ? JSON.stringify(filesInvolved) : null,
|
|
3653
|
+
nowIso()
|
|
3635
3654
|
);
|
|
3636
3655
|
}
|
|
3637
3656
|
function getLastProcessedLine(db, sessionId) {
|
|
@@ -3890,6 +3909,7 @@ var init_memory_db = __esm({
|
|
|
3890
3909
|
init_db_driver();
|
|
3891
3910
|
init_config();
|
|
3892
3911
|
init_memory_vector();
|
|
3912
|
+
init_timestamps();
|
|
3893
3913
|
init_db_backup();
|
|
3894
3914
|
init_rule_delivery();
|
|
3895
3915
|
init_rule_candidate_store();
|
|
@@ -3986,12 +4006,65 @@ import { tmpdir as tmpdir2 } from "os";
|
|
|
3986
4006
|
import { join as join6 } from "path";
|
|
3987
4007
|
|
|
3988
4008
|
// src/hooks/lib/write-hook-message.ts
|
|
3989
|
-
|
|
3990
|
-
|
|
4009
|
+
var HOOK_EVENTS = [
|
|
4010
|
+
"UserPromptSubmit",
|
|
4011
|
+
"UserPromptExpansion",
|
|
4012
|
+
"SessionStart",
|
|
4013
|
+
"PreToolUse",
|
|
4014
|
+
"PostToolUse",
|
|
4015
|
+
"Stop",
|
|
4016
|
+
"SubagentStop",
|
|
4017
|
+
"PreCompact"
|
|
4018
|
+
];
|
|
4019
|
+
var ADDITIONAL_CONTEXT_MAX_CHARS = 1e4;
|
|
4020
|
+
function isHookEvent(value) {
|
|
4021
|
+
return typeof value === "string" && HOOK_EVENTS.includes(value);
|
|
4022
|
+
}
|
|
4023
|
+
function writeHookContext(hookEventName, message) {
|
|
4024
|
+
if (!isHookEvent(hookEventName)) {
|
|
4025
|
+
process.stderr.write(
|
|
4026
|
+
`[massu] writeHookContext: refusing to emit \u2014 unrecognised hook event ${JSON.stringify(hookEventName)}. Known: ${HOOK_EVENTS.join(", ")}.
|
|
4027
|
+
`
|
|
4028
|
+
);
|
|
4029
|
+
return;
|
|
4030
|
+
}
|
|
4031
|
+
if (typeof message !== "string" || message.length === 0) return;
|
|
4032
|
+
let additionalContext = message;
|
|
4033
|
+
if (additionalContext.length > ADDITIONAL_CONTEXT_MAX_CHARS) {
|
|
4034
|
+
const marker = `
|
|
4035
|
+
[massu] \u2026truncated at ${ADDITIONAL_CONTEXT_MAX_CHARS} chars (spec cap).`;
|
|
4036
|
+
additionalContext = additionalContext.slice(0, ADDITIONAL_CONTEXT_MAX_CHARS - marker.length) + marker;
|
|
4037
|
+
}
|
|
4038
|
+
process.stdout.write(
|
|
4039
|
+
JSON.stringify({ hookSpecificOutput: { hookEventName, additionalContext } }) + "\n"
|
|
4040
|
+
);
|
|
3991
4041
|
}
|
|
3992
4042
|
|
|
3993
4043
|
// src/hooks/fix-detector.ts
|
|
3994
4044
|
init_hook_failure_signal();
|
|
4045
|
+
|
|
4046
|
+
// src/hooks/lib/is-direct-invocation.ts
|
|
4047
|
+
import { pathToFileURL } from "url";
|
|
4048
|
+
function isDirectInvocation(moduleUrl) {
|
|
4049
|
+
const entry = process.argv[1];
|
|
4050
|
+
if (!entry) return false;
|
|
4051
|
+
try {
|
|
4052
|
+
return moduleUrl === pathToFileURL(entry).href;
|
|
4053
|
+
} catch {
|
|
4054
|
+
return false;
|
|
4055
|
+
}
|
|
4056
|
+
}
|
|
4057
|
+
|
|
4058
|
+
// src/hooks/fix-detector.ts
|
|
4059
|
+
var HOOK_EVENT = "PostToolUse";
|
|
4060
|
+
var TEST_OR_FIXTURE_PATH_PATTERNS = [
|
|
4061
|
+
/(^|\/)(__tests__|__fixtures__|tests?|fixtures?|spec)\//,
|
|
4062
|
+
/\.(test|spec)\.[cm]?[jt]sx?$/,
|
|
4063
|
+
/(^|\/)(test_[^/]+|[^/]+_test)\.py$/
|
|
4064
|
+
];
|
|
4065
|
+
function isTestOrFixturePath(filePath) {
|
|
4066
|
+
return TEST_OR_FIXTURE_PATH_PATTERNS.some((re) => re.test(filePath));
|
|
4067
|
+
}
|
|
3995
4068
|
var FIX_HEURISTICS = [
|
|
3996
4069
|
{
|
|
3997
4070
|
name: "removed_broken_code",
|
|
@@ -4118,6 +4191,10 @@ async function main() {
|
|
|
4118
4191
|
process.exit(0);
|
|
4119
4192
|
return;
|
|
4120
4193
|
}
|
|
4194
|
+
if (isTestOrFixturePath(filePath)) {
|
|
4195
|
+
process.exit(0);
|
|
4196
|
+
return;
|
|
4197
|
+
}
|
|
4121
4198
|
if (config.autoLearning?.enabled === false || config.autoLearning?.fixDetection?.enabled === false) {
|
|
4122
4199
|
process.exit(0);
|
|
4123
4200
|
return;
|
|
@@ -4166,7 +4243,8 @@ async function main() {
|
|
|
4166
4243
|
appendFileSync3(flagPath, JSON.stringify(signal) + "\n");
|
|
4167
4244
|
const lines = readFileSync6(flagPath, "utf-8").split("\n").filter(Boolean);
|
|
4168
4245
|
if (lines.length === 1) {
|
|
4169
|
-
|
|
4246
|
+
writeHookContext(
|
|
4247
|
+
HOOK_EVENT,
|
|
4170
4248
|
`[Massu Auto-Learning] Bug fix detected in ${filePath} (signals: ${detected.join(", ")}). The auto-learning pipeline will prompt you at session end to create an incident report, derive a prevention rule, and add enforcement.`
|
|
4171
4249
|
);
|
|
4172
4250
|
}
|
|
@@ -4186,4 +4264,10 @@ function readStdin() {
|
|
|
4186
4264
|
setTimeout(() => resolve5(data), 3e3);
|
|
4187
4265
|
});
|
|
4188
4266
|
}
|
|
4189
|
-
|
|
4267
|
+
if (isDirectInvocation(import.meta.url)) {
|
|
4268
|
+
main();
|
|
4269
|
+
}
|
|
4270
|
+
export {
|
|
4271
|
+
TEST_OR_FIXTURE_PATH_PATTERNS,
|
|
4272
|
+
isTestOrFixturePath
|
|
4273
|
+
};
|
|
@@ -697,6 +697,13 @@ var init_config = __esm({
|
|
|
697
697
|
});
|
|
698
698
|
LanguageFrameworkEntrySchema = z2.object({
|
|
699
699
|
framework: z2.string().optional(),
|
|
700
|
+
// `source_dirs` is emitted by `init` (commands/init.ts:417) and existence-checked
|
|
701
|
+
// by config validation (init.ts:868-876), but until 2026-08-14 it survived only via
|
|
702
|
+
// `.passthrough()` — declared in the file, untyped in the schema. `lib/source-layout.ts`
|
|
703
|
+
// derives the index builders' candidate set from it, so it is typed here rather than
|
|
704
|
+
// read as an unknown: a consumer that has to shape-check its own SoT is one branch away
|
|
705
|
+
// from silently contributing `undefined` to a path predicate.
|
|
706
|
+
source_dirs: z2.array(z2.string()).optional(),
|
|
700
707
|
test_framework: z2.string().optional(),
|
|
701
708
|
test: z2.string().optional(),
|
|
702
709
|
runtime: z2.string().optional(),
|
|
@@ -1339,6 +1346,16 @@ var init_memory_vector = __esm({
|
|
|
1339
1346
|
}
|
|
1340
1347
|
});
|
|
1341
1348
|
|
|
1349
|
+
// src/lib/timestamps.ts
|
|
1350
|
+
function nowIso() {
|
|
1351
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1352
|
+
}
|
|
1353
|
+
var init_timestamps = __esm({
|
|
1354
|
+
"src/lib/timestamps.ts"() {
|
|
1355
|
+
"use strict";
|
|
1356
|
+
}
|
|
1357
|
+
});
|
|
1358
|
+
|
|
1342
1359
|
// src/db-backup.ts
|
|
1343
1360
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync, statSync, unlinkSync, copyFileSync, rmSync as rmSync4 } from "fs";
|
|
1344
1361
|
import { resolve as resolve3, join as join3, basename, dirname as dirname4 } from "path";
|
|
@@ -3670,8 +3687,8 @@ function linkSessionToTask(db, sessionId, taskId) {
|
|
|
3670
3687
|
}
|
|
3671
3688
|
function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantResponse, toolCallsJson, toolCallCount, promptTokens, responseTokens) {
|
|
3672
3689
|
const result = db.prepare(`
|
|
3673
|
-
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens)
|
|
3674
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3690
|
+
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens, created_at)
|
|
3691
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3675
3692
|
`).run(
|
|
3676
3693
|
sessionId,
|
|
3677
3694
|
turnNumber,
|
|
@@ -3680,14 +3697,15 @@ function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantRes
|
|
|
3680
3697
|
toolCallsJson,
|
|
3681
3698
|
toolCallCount,
|
|
3682
3699
|
promptTokens,
|
|
3683
|
-
responseTokens
|
|
3700
|
+
responseTokens,
|
|
3701
|
+
nowIso()
|
|
3684
3702
|
);
|
|
3685
3703
|
return Number(result.lastInsertRowid);
|
|
3686
3704
|
}
|
|
3687
3705
|
function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, inputSize, outputSize, success, filesInvolved) {
|
|
3688
3706
|
db.prepare(`
|
|
3689
|
-
INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved)
|
|
3690
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3707
|
+
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)
|
|
3708
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3691
3709
|
`).run(
|
|
3692
3710
|
sessionId,
|
|
3693
3711
|
turnNumber,
|
|
@@ -3696,7 +3714,8 @@ function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, in
|
|
|
3696
3714
|
inputSize,
|
|
3697
3715
|
outputSize,
|
|
3698
3716
|
success ? 1 : 0,
|
|
3699
|
-
filesInvolved ? JSON.stringify(filesInvolved) : null
|
|
3717
|
+
filesInvolved ? JSON.stringify(filesInvolved) : null,
|
|
3718
|
+
nowIso()
|
|
3700
3719
|
);
|
|
3701
3720
|
}
|
|
3702
3721
|
function getLastProcessedLine(db, sessionId) {
|
|
@@ -3955,6 +3974,7 @@ var init_memory_db = __esm({
|
|
|
3955
3974
|
init_db_driver();
|
|
3956
3975
|
init_config();
|
|
3957
3976
|
init_memory_vector();
|
|
3977
|
+
init_timestamps();
|
|
3958
3978
|
init_db_backup();
|
|
3959
3979
|
init_rule_delivery();
|
|
3960
3980
|
init_rule_candidate_store();
|
|
@@ -3979,12 +3999,57 @@ import { existsSync as existsSync9, readFileSync as readFileSync6, readdirSync a
|
|
|
3979
3999
|
import { basename as basename3, resolve as resolve5 } from "path";
|
|
3980
4000
|
|
|
3981
4001
|
// src/hooks/lib/write-hook-message.ts
|
|
3982
|
-
|
|
3983
|
-
|
|
4002
|
+
var HOOK_EVENTS = [
|
|
4003
|
+
"UserPromptSubmit",
|
|
4004
|
+
"UserPromptExpansion",
|
|
4005
|
+
"SessionStart",
|
|
4006
|
+
"PreToolUse",
|
|
4007
|
+
"PostToolUse",
|
|
4008
|
+
"Stop",
|
|
4009
|
+
"SubagentStop",
|
|
4010
|
+
"PreCompact"
|
|
4011
|
+
];
|
|
4012
|
+
var ADDITIONAL_CONTEXT_MAX_CHARS = 1e4;
|
|
4013
|
+
function isHookEvent(value) {
|
|
4014
|
+
return typeof value === "string" && HOOK_EVENTS.includes(value);
|
|
4015
|
+
}
|
|
4016
|
+
function writeHookContext(hookEventName, message) {
|
|
4017
|
+
if (!isHookEvent(hookEventName)) {
|
|
4018
|
+
process.stderr.write(
|
|
4019
|
+
`[massu] writeHookContext: refusing to emit \u2014 unrecognised hook event ${JSON.stringify(hookEventName)}. Known: ${HOOK_EVENTS.join(", ")}.
|
|
4020
|
+
`
|
|
4021
|
+
);
|
|
4022
|
+
return;
|
|
4023
|
+
}
|
|
4024
|
+
if (typeof message !== "string" || message.length === 0) return;
|
|
4025
|
+
let additionalContext = message;
|
|
4026
|
+
if (additionalContext.length > ADDITIONAL_CONTEXT_MAX_CHARS) {
|
|
4027
|
+
const marker = `
|
|
4028
|
+
[massu] \u2026truncated at ${ADDITIONAL_CONTEXT_MAX_CHARS} chars (spec cap).`;
|
|
4029
|
+
additionalContext = additionalContext.slice(0, ADDITIONAL_CONTEXT_MAX_CHARS - marker.length) + marker;
|
|
4030
|
+
}
|
|
4031
|
+
process.stdout.write(
|
|
4032
|
+
JSON.stringify({ hookSpecificOutput: { hookEventName, additionalContext } }) + "\n"
|
|
4033
|
+
);
|
|
3984
4034
|
}
|
|
3985
4035
|
|
|
3986
4036
|
// src/hooks/incident-pipeline.ts
|
|
3987
4037
|
init_hook_failure_signal();
|
|
4038
|
+
|
|
4039
|
+
// src/hooks/lib/is-direct-invocation.ts
|
|
4040
|
+
import { pathToFileURL } from "url";
|
|
4041
|
+
function isDirectInvocation(moduleUrl) {
|
|
4042
|
+
const entry = process.argv[1];
|
|
4043
|
+
if (!entry) return false;
|
|
4044
|
+
try {
|
|
4045
|
+
return moduleUrl === pathToFileURL(entry).href;
|
|
4046
|
+
} catch {
|
|
4047
|
+
return false;
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
4050
|
+
|
|
4051
|
+
// src/hooks/incident-pipeline.ts
|
|
4052
|
+
var HOOK_EVENT = "PostToolUse";
|
|
3988
4053
|
async function main() {
|
|
3989
4054
|
try {
|
|
3990
4055
|
const input = await readStdin();
|
|
@@ -4067,7 +4132,7 @@ async function main() {
|
|
|
4067
4132
|
lines.push(" This step is MANDATORY per the auto-learning pipeline.");
|
|
4068
4133
|
lines.push("============================================================================");
|
|
4069
4134
|
lines.push("");
|
|
4070
|
-
|
|
4135
|
+
writeHookContext(HOOK_EVENT, lines.join("\n"));
|
|
4071
4136
|
try {
|
|
4072
4137
|
const taxonomyConfig = config.autoLearning?.failureClassification;
|
|
4073
4138
|
if (taxonomyConfig?.enabled !== false) {
|
|
@@ -4111,4 +4176,6 @@ function readStdin() {
|
|
|
4111
4176
|
setTimeout(() => resolve6(data), 3e3);
|
|
4112
4177
|
});
|
|
4113
4178
|
}
|
|
4114
|
-
|
|
4179
|
+
if (isDirectInvocation(import.meta.url)) {
|
|
4180
|
+
main();
|
|
4181
|
+
}
|
|
@@ -1167,6 +1167,13 @@ var init_config = __esm({
|
|
|
1167
1167
|
});
|
|
1168
1168
|
LanguageFrameworkEntrySchema = z2.object({
|
|
1169
1169
|
framework: z2.string().optional(),
|
|
1170
|
+
// `source_dirs` is emitted by `init` (commands/init.ts:417) and existence-checked
|
|
1171
|
+
// by config validation (init.ts:868-876), but until 2026-08-14 it survived only via
|
|
1172
|
+
// `.passthrough()` — declared in the file, untyped in the schema. `lib/source-layout.ts`
|
|
1173
|
+
// derives the index builders' candidate set from it, so it is typed here rather than
|
|
1174
|
+
// read as an unknown: a consumer that has to shape-check its own SoT is one branch away
|
|
1175
|
+
// from silently contributing `undefined` to a path predicate.
|
|
1176
|
+
source_dirs: z2.array(z2.string()).optional(),
|
|
1170
1177
|
test_framework: z2.string().optional(),
|
|
1171
1178
|
test: z2.string().optional(),
|
|
1172
1179
|
runtime: z2.string().optional(),
|
|
@@ -1339,6 +1346,16 @@ var init_memory_vector = __esm({
|
|
|
1339
1346
|
}
|
|
1340
1347
|
});
|
|
1341
1348
|
|
|
1349
|
+
// src/lib/timestamps.ts
|
|
1350
|
+
function nowIso() {
|
|
1351
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1352
|
+
}
|
|
1353
|
+
var init_timestamps = __esm({
|
|
1354
|
+
"src/lib/timestamps.ts"() {
|
|
1355
|
+
"use strict";
|
|
1356
|
+
}
|
|
1357
|
+
});
|
|
1358
|
+
|
|
1342
1359
|
// src/db-backup.ts
|
|
1343
1360
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync, statSync, unlinkSync, copyFileSync, rmSync as rmSync4 } from "fs";
|
|
1344
1361
|
import { resolve as resolve3, join as join3, basename, dirname as dirname4 } from "path";
|
|
@@ -3599,8 +3616,8 @@ function linkSessionToTask(db, sessionId, taskId) {
|
|
|
3599
3616
|
}
|
|
3600
3617
|
function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantResponse, toolCallsJson, toolCallCount, promptTokens, responseTokens) {
|
|
3601
3618
|
const result = db.prepare(`
|
|
3602
|
-
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens)
|
|
3603
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3619
|
+
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens, created_at)
|
|
3620
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3604
3621
|
`).run(
|
|
3605
3622
|
sessionId,
|
|
3606
3623
|
turnNumber,
|
|
@@ -3609,14 +3626,15 @@ function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantRes
|
|
|
3609
3626
|
toolCallsJson,
|
|
3610
3627
|
toolCallCount,
|
|
3611
3628
|
promptTokens,
|
|
3612
|
-
responseTokens
|
|
3629
|
+
responseTokens,
|
|
3630
|
+
nowIso()
|
|
3613
3631
|
);
|
|
3614
3632
|
return Number(result.lastInsertRowid);
|
|
3615
3633
|
}
|
|
3616
3634
|
function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, inputSize, outputSize, success, filesInvolved) {
|
|
3617
3635
|
db.prepare(`
|
|
3618
|
-
INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved)
|
|
3619
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3636
|
+
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)
|
|
3637
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3620
3638
|
`).run(
|
|
3621
3639
|
sessionId,
|
|
3622
3640
|
turnNumber,
|
|
@@ -3625,7 +3643,8 @@ function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, in
|
|
|
3625
3643
|
inputSize,
|
|
3626
3644
|
outputSize,
|
|
3627
3645
|
success ? 1 : 0,
|
|
3628
|
-
filesInvolved ? JSON.stringify(filesInvolved) : null
|
|
3646
|
+
filesInvolved ? JSON.stringify(filesInvolved) : null,
|
|
3647
|
+
nowIso()
|
|
3629
3648
|
);
|
|
3630
3649
|
}
|
|
3631
3650
|
function getLastProcessedLine(db, sessionId) {
|
|
@@ -3884,6 +3903,7 @@ var init_memory_db = __esm({
|
|
|
3884
3903
|
init_db_driver();
|
|
3885
3904
|
init_config();
|
|
3886
3905
|
init_memory_vector();
|
|
3906
|
+
init_timestamps();
|
|
3887
3907
|
init_db_backup();
|
|
3888
3908
|
init_rule_delivery();
|
|
3889
3909
|
init_rule_candidate_store();
|
|
@@ -3973,12 +3993,57 @@ var init_hook_failure_signal = __esm({
|
|
|
3973
3993
|
});
|
|
3974
3994
|
|
|
3975
3995
|
// src/hooks/lib/write-hook-message.ts
|
|
3976
|
-
|
|
3977
|
-
|
|
3996
|
+
var HOOK_EVENTS = [
|
|
3997
|
+
"UserPromptSubmit",
|
|
3998
|
+
"UserPromptExpansion",
|
|
3999
|
+
"SessionStart",
|
|
4000
|
+
"PreToolUse",
|
|
4001
|
+
"PostToolUse",
|
|
4002
|
+
"Stop",
|
|
4003
|
+
"SubagentStop",
|
|
4004
|
+
"PreCompact"
|
|
4005
|
+
];
|
|
4006
|
+
var ADDITIONAL_CONTEXT_MAX_CHARS = 1e4;
|
|
4007
|
+
function isHookEvent(value) {
|
|
4008
|
+
return typeof value === "string" && HOOK_EVENTS.includes(value);
|
|
4009
|
+
}
|
|
4010
|
+
function writeHookContext(hookEventName, message) {
|
|
4011
|
+
if (!isHookEvent(hookEventName)) {
|
|
4012
|
+
process.stderr.write(
|
|
4013
|
+
`[massu] writeHookContext: refusing to emit \u2014 unrecognised hook event ${JSON.stringify(hookEventName)}. Known: ${HOOK_EVENTS.join(", ")}.
|
|
4014
|
+
`
|
|
4015
|
+
);
|
|
4016
|
+
return;
|
|
4017
|
+
}
|
|
4018
|
+
if (typeof message !== "string" || message.length === 0) return;
|
|
4019
|
+
let additionalContext = message;
|
|
4020
|
+
if (additionalContext.length > ADDITIONAL_CONTEXT_MAX_CHARS) {
|
|
4021
|
+
const marker = `
|
|
4022
|
+
[massu] \u2026truncated at ${ADDITIONAL_CONTEXT_MAX_CHARS} chars (spec cap).`;
|
|
4023
|
+
additionalContext = additionalContext.slice(0, ADDITIONAL_CONTEXT_MAX_CHARS - marker.length) + marker;
|
|
4024
|
+
}
|
|
4025
|
+
process.stdout.write(
|
|
4026
|
+
JSON.stringify({ hookSpecificOutput: { hookEventName, additionalContext } }) + "\n"
|
|
4027
|
+
);
|
|
3978
4028
|
}
|
|
3979
4029
|
|
|
3980
4030
|
// src/hooks/intent-suggester.ts
|
|
3981
4031
|
init_hook_failure_signal();
|
|
4032
|
+
|
|
4033
|
+
// src/hooks/lib/is-direct-invocation.ts
|
|
4034
|
+
import { pathToFileURL } from "url";
|
|
4035
|
+
function isDirectInvocation(moduleUrl) {
|
|
4036
|
+
const entry = process.argv[1];
|
|
4037
|
+
if (!entry) return false;
|
|
4038
|
+
try {
|
|
4039
|
+
return moduleUrl === pathToFileURL(entry).href;
|
|
4040
|
+
} catch {
|
|
4041
|
+
return false;
|
|
4042
|
+
}
|
|
4043
|
+
}
|
|
4044
|
+
|
|
4045
|
+
// src/hooks/intent-suggester.ts
|
|
4046
|
+
var HOOK_EVENT = "UserPromptSubmit";
|
|
3982
4047
|
var COMMAND_MAPPINGS = [
|
|
3983
4048
|
{
|
|
3984
4049
|
keywords: ["test", "failing"],
|
|
@@ -4056,7 +4121,7 @@ async function main() {
|
|
|
4056
4121
|
process.exit(0);
|
|
4057
4122
|
return;
|
|
4058
4123
|
}
|
|
4059
|
-
|
|
4124
|
+
writeHookContext(HOOK_EVENT, `Tip: Use ${match.command} to ${match.description}.`);
|
|
4060
4125
|
} catch (err) {
|
|
4061
4126
|
recordHookFailure("intent-suggester", err);
|
|
4062
4127
|
}
|
|
@@ -4073,4 +4138,6 @@ function readStdin() {
|
|
|
4073
4138
|
setTimeout(() => resolve5(data), 3e3);
|
|
4074
4139
|
});
|
|
4075
4140
|
}
|
|
4076
|
-
|
|
4141
|
+
if (isDirectInvocation(import.meta.url)) {
|
|
4142
|
+
main();
|
|
4143
|
+
}
|
|
@@ -1167,6 +1167,13 @@ var init_config = __esm({
|
|
|
1167
1167
|
});
|
|
1168
1168
|
LanguageFrameworkEntrySchema = z2.object({
|
|
1169
1169
|
framework: z2.string().optional(),
|
|
1170
|
+
// `source_dirs` is emitted by `init` (commands/init.ts:417) and existence-checked
|
|
1171
|
+
// by config validation (init.ts:868-876), but until 2026-08-14 it survived only via
|
|
1172
|
+
// `.passthrough()` — declared in the file, untyped in the schema. `lib/source-layout.ts`
|
|
1173
|
+
// derives the index builders' candidate set from it, so it is typed here rather than
|
|
1174
|
+
// read as an unknown: a consumer that has to shape-check its own SoT is one branch away
|
|
1175
|
+
// from silently contributing `undefined` to a path predicate.
|
|
1176
|
+
source_dirs: z2.array(z2.string()).optional(),
|
|
1170
1177
|
test_framework: z2.string().optional(),
|
|
1171
1178
|
test: z2.string().optional(),
|
|
1172
1179
|
runtime: z2.string().optional(),
|
|
@@ -1360,6 +1367,16 @@ var init_memory_vector = __esm({
|
|
|
1360
1367
|
}
|
|
1361
1368
|
});
|
|
1362
1369
|
|
|
1370
|
+
// src/lib/timestamps.ts
|
|
1371
|
+
function nowIso() {
|
|
1372
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1373
|
+
}
|
|
1374
|
+
var init_timestamps = __esm({
|
|
1375
|
+
"src/lib/timestamps.ts"() {
|
|
1376
|
+
"use strict";
|
|
1377
|
+
}
|
|
1378
|
+
});
|
|
1379
|
+
|
|
1363
1380
|
// src/db-backup.ts
|
|
1364
1381
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync, statSync, unlinkSync, copyFileSync, rmSync as rmSync4 } from "fs";
|
|
1365
1382
|
import { resolve as resolve3, join as join3, basename, dirname as dirname4 } from "path";
|
|
@@ -3706,8 +3723,8 @@ function linkSessionToTask(db, sessionId, taskId) {
|
|
|
3706
3723
|
}
|
|
3707
3724
|
function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantResponse, toolCallsJson, toolCallCount, promptTokens, responseTokens) {
|
|
3708
3725
|
const result = db.prepare(`
|
|
3709
|
-
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens)
|
|
3710
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3726
|
+
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens, created_at)
|
|
3727
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3711
3728
|
`).run(
|
|
3712
3729
|
sessionId,
|
|
3713
3730
|
turnNumber,
|
|
@@ -3716,14 +3733,15 @@ function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantRes
|
|
|
3716
3733
|
toolCallsJson,
|
|
3717
3734
|
toolCallCount,
|
|
3718
3735
|
promptTokens,
|
|
3719
|
-
responseTokens
|
|
3736
|
+
responseTokens,
|
|
3737
|
+
nowIso()
|
|
3720
3738
|
);
|
|
3721
3739
|
return Number(result.lastInsertRowid);
|
|
3722
3740
|
}
|
|
3723
3741
|
function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, inputSize, outputSize, success, filesInvolved) {
|
|
3724
3742
|
db.prepare(`
|
|
3725
|
-
INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved)
|
|
3726
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3743
|
+
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)
|
|
3744
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3727
3745
|
`).run(
|
|
3728
3746
|
sessionId,
|
|
3729
3747
|
turnNumber,
|
|
@@ -3732,7 +3750,8 @@ function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, in
|
|
|
3732
3750
|
inputSize,
|
|
3733
3751
|
outputSize,
|
|
3734
3752
|
success ? 1 : 0,
|
|
3735
|
-
filesInvolved ? JSON.stringify(filesInvolved) : null
|
|
3753
|
+
filesInvolved ? JSON.stringify(filesInvolved) : null,
|
|
3754
|
+
nowIso()
|
|
3736
3755
|
);
|
|
3737
3756
|
}
|
|
3738
3757
|
function getLastProcessedLine(db, sessionId) {
|
|
@@ -3991,6 +4010,7 @@ var init_memory_db = __esm({
|
|
|
3991
4010
|
init_db_driver();
|
|
3992
4011
|
init_config();
|
|
3993
4012
|
init_memory_vector();
|
|
4013
|
+
init_timestamps();
|
|
3994
4014
|
init_db_backup();
|
|
3995
4015
|
init_rule_delivery();
|
|
3996
4016
|
init_rule_candidate_store();
|
|
@@ -4520,14 +4540,59 @@ function pendingPointer(db) {
|
|
|
4520
4540
|
init_memory_embedder();
|
|
4521
4541
|
|
|
4522
4542
|
// src/hooks/lib/write-hook-message.ts
|
|
4523
|
-
|
|
4524
|
-
|
|
4543
|
+
var HOOK_EVENTS = [
|
|
4544
|
+
"UserPromptSubmit",
|
|
4545
|
+
"UserPromptExpansion",
|
|
4546
|
+
"SessionStart",
|
|
4547
|
+
"PreToolUse",
|
|
4548
|
+
"PostToolUse",
|
|
4549
|
+
"Stop",
|
|
4550
|
+
"SubagentStop",
|
|
4551
|
+
"PreCompact"
|
|
4552
|
+
];
|
|
4553
|
+
var ADDITIONAL_CONTEXT_MAX_CHARS = 1e4;
|
|
4554
|
+
function isHookEvent(value) {
|
|
4555
|
+
return typeof value === "string" && HOOK_EVENTS.includes(value);
|
|
4556
|
+
}
|
|
4557
|
+
function writeHookContext(hookEventName, message) {
|
|
4558
|
+
if (!isHookEvent(hookEventName)) {
|
|
4559
|
+
process.stderr.write(
|
|
4560
|
+
`[massu] writeHookContext: refusing to emit \u2014 unrecognised hook event ${JSON.stringify(hookEventName)}. Known: ${HOOK_EVENTS.join(", ")}.
|
|
4561
|
+
`
|
|
4562
|
+
);
|
|
4563
|
+
return;
|
|
4564
|
+
}
|
|
4565
|
+
if (typeof message !== "string" || message.length === 0) return;
|
|
4566
|
+
let additionalContext = message;
|
|
4567
|
+
if (additionalContext.length > ADDITIONAL_CONTEXT_MAX_CHARS) {
|
|
4568
|
+
const marker = `
|
|
4569
|
+
[massu] \u2026truncated at ${ADDITIONAL_CONTEXT_MAX_CHARS} chars (spec cap).`;
|
|
4570
|
+
additionalContext = additionalContext.slice(0, ADDITIONAL_CONTEXT_MAX_CHARS - marker.length) + marker;
|
|
4571
|
+
}
|
|
4572
|
+
process.stdout.write(
|
|
4573
|
+
JSON.stringify({ hookSpecificOutput: { hookEventName, additionalContext } }) + "\n"
|
|
4574
|
+
);
|
|
4525
4575
|
}
|
|
4526
4576
|
|
|
4527
4577
|
// src/hooks/memory-recall.ts
|
|
4528
4578
|
init_db_driver();
|
|
4529
4579
|
init_hook_failure_signal();
|
|
4530
4580
|
import { existsSync as existsSync9 } from "fs";
|
|
4581
|
+
|
|
4582
|
+
// src/hooks/lib/is-direct-invocation.ts
|
|
4583
|
+
import { pathToFileURL } from "url";
|
|
4584
|
+
function isDirectInvocation(moduleUrl) {
|
|
4585
|
+
const entry = process.argv[1];
|
|
4586
|
+
if (!entry) return false;
|
|
4587
|
+
try {
|
|
4588
|
+
return moduleUrl === pathToFileURL(entry).href;
|
|
4589
|
+
} catch {
|
|
4590
|
+
return false;
|
|
4591
|
+
}
|
|
4592
|
+
}
|
|
4593
|
+
|
|
4594
|
+
// src/hooks/memory-recall.ts
|
|
4595
|
+
var HOOK_EVENT = "UserPromptSubmit";
|
|
4531
4596
|
var DEFAULTS = {
|
|
4532
4597
|
enabled: true,
|
|
4533
4598
|
maxTokens: 1200,
|
|
@@ -4581,7 +4646,7 @@ async function main() {
|
|
|
4581
4646
|
cfg.timeoutMs
|
|
4582
4647
|
);
|
|
4583
4648
|
if (block && block.trim()) {
|
|
4584
|
-
|
|
4649
|
+
writeHookContext(HOOK_EVENT, block);
|
|
4585
4650
|
}
|
|
4586
4651
|
} catch {
|
|
4587
4652
|
} finally {
|
|
@@ -4679,4 +4744,6 @@ function readStdin() {
|
|
|
4679
4744
|
setTimeout(() => resolve5(data), 3e3);
|
|
4680
4745
|
});
|
|
4681
4746
|
}
|
|
4682
|
-
|
|
4747
|
+
if (isDirectInvocation(import.meta.url)) {
|
|
4748
|
+
main();
|
|
4749
|
+
}
|