@massu/core 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/cli.js +151 -3
- package/dist/hooks/auto-learning-pipeline.js +14 -2
- package/dist/hooks/classify-failure.js +1146 -0
- package/dist/hooks/cost-tracker.js +31 -0
- package/dist/hooks/fix-detector.js +12 -0
- package/dist/hooks/incident-pipeline.js +698 -10
- package/dist/hooks/post-edit-context.js +12 -0
- package/dist/hooks/post-tool-use.js +31 -0
- package/dist/hooks/pre-compact.js +31 -0
- package/dist/hooks/pre-delete-check.js +12 -0
- package/dist/hooks/quality-event.js +31 -0
- package/dist/hooks/rule-enforcement-pipeline.js +14 -1
- package/dist/hooks/session-end.js +31 -0
- package/dist/hooks/session-start.js +32 -1
- package/dist/hooks/user-prompt.js +63 -1
- package/package.json +1 -1
- package/reference/hook-execution-order.md +25 -17
- package/src/commands/doctor.ts +1 -1
- package/src/commands/init.ts +11 -1
- package/src/config.ts +12 -0
- package/src/hooks/auto-learning-pipeline.ts +3 -3
- package/src/hooks/classify-failure.ts +259 -0
- package/src/hooks/incident-pipeline.ts +42 -0
- package/src/hooks/rule-enforcement-pipeline.ts +2 -1
- package/src/hooks/session-start.ts +1 -1
- package/src/hooks/user-prompt.ts +21 -1
- package/src/license.ts +2 -1
- package/src/mcp-bridge-tools.ts +1 -1
- package/src/memory-db.ts +201 -0
|
@@ -149,6 +149,18 @@ var AutoLearningConfigSchema = z.object({
|
|
|
149
149
|
"added_missing_import"
|
|
150
150
|
])
|
|
151
151
|
}).default({}),
|
|
152
|
+
failureClassification: z.object({
|
|
153
|
+
enabled: z.boolean().default(true),
|
|
154
|
+
thresholds: z.object({
|
|
155
|
+
known: z.number().default(5),
|
|
156
|
+
similar: z.number().default(3)
|
|
157
|
+
}).default({}),
|
|
158
|
+
scoring: z.object({
|
|
159
|
+
diffPatternWeight: z.number().default(3),
|
|
160
|
+
filePatternWeight: z.number().default(2),
|
|
161
|
+
promptKeywordWeight: z.number().default(2)
|
|
162
|
+
}).default({})
|
|
163
|
+
}).default({}),
|
|
152
164
|
pipeline: z.object({
|
|
153
165
|
requireIncidentReport: z.boolean().default(true),
|
|
154
166
|
requirePreventionRule: z.boolean().default(true),
|
|
@@ -151,6 +151,18 @@ var AutoLearningConfigSchema = z.object({
|
|
|
151
151
|
"added_missing_import"
|
|
152
152
|
])
|
|
153
153
|
}).default({}),
|
|
154
|
+
failureClassification: z.object({
|
|
155
|
+
enabled: z.boolean().default(true),
|
|
156
|
+
thresholds: z.object({
|
|
157
|
+
known: z.number().default(5),
|
|
158
|
+
similar: z.number().default(3)
|
|
159
|
+
}).default({}),
|
|
160
|
+
scoring: z.object({
|
|
161
|
+
diffPatternWeight: z.number().default(3),
|
|
162
|
+
filePatternWeight: z.number().default(2),
|
|
163
|
+
promptKeywordWeight: z.number().default(2)
|
|
164
|
+
}).default({})
|
|
165
|
+
}).default({}),
|
|
154
166
|
pipeline: z.object({
|
|
155
167
|
requireIncidentReport: z.boolean().default(true),
|
|
156
168
|
requirePreventionRule: z.boolean().default(true),
|
|
@@ -848,6 +860,25 @@ function initMemorySchema(db) {
|
|
|
848
860
|
features TEXT DEFAULT '[]'
|
|
849
861
|
);
|
|
850
862
|
`);
|
|
863
|
+
db.exec(`
|
|
864
|
+
CREATE TABLE IF NOT EXISTS failure_classes (
|
|
865
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
866
|
+
name TEXT NOT NULL UNIQUE,
|
|
867
|
+
description TEXT NOT NULL,
|
|
868
|
+
diff_patterns TEXT NOT NULL DEFAULT '[]',
|
|
869
|
+
file_patterns TEXT NOT NULL DEFAULT '[]',
|
|
870
|
+
prompt_keywords TEXT NOT NULL DEFAULT '[]',
|
|
871
|
+
incidents TEXT NOT NULL DEFAULT '[]',
|
|
872
|
+
rules TEXT NOT NULL DEFAULT '[]',
|
|
873
|
+
scanner_checks TEXT NOT NULL DEFAULT '[]',
|
|
874
|
+
known_message TEXT NOT NULL DEFAULT '',
|
|
875
|
+
needs_review INTEGER NOT NULL DEFAULT 0,
|
|
876
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
877
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
878
|
+
);
|
|
879
|
+
CREATE INDEX IF NOT EXISTS idx_fc_name ON failure_classes(name);
|
|
880
|
+
CREATE INDEX IF NOT EXISTS idx_fc_needs_review ON failure_classes(needs_review);
|
|
881
|
+
`);
|
|
851
882
|
}
|
|
852
883
|
function assignImportance(type, vrResult) {
|
|
853
884
|
switch (type) {
|
|
@@ -151,6 +151,18 @@ var AutoLearningConfigSchema = z.object({
|
|
|
151
151
|
"added_missing_import"
|
|
152
152
|
])
|
|
153
153
|
}).default({}),
|
|
154
|
+
failureClassification: z.object({
|
|
155
|
+
enabled: z.boolean().default(true),
|
|
156
|
+
thresholds: z.object({
|
|
157
|
+
known: z.number().default(5),
|
|
158
|
+
similar: z.number().default(3)
|
|
159
|
+
}).default({}),
|
|
160
|
+
scoring: z.object({
|
|
161
|
+
diffPatternWeight: z.number().default(3),
|
|
162
|
+
filePatternWeight: z.number().default(2),
|
|
163
|
+
promptKeywordWeight: z.number().default(2)
|
|
164
|
+
}).default({})
|
|
165
|
+
}).default({}),
|
|
154
166
|
pipeline: z.object({
|
|
155
167
|
requireIncidentReport: z.boolean().default(true),
|
|
156
168
|
requirePreventionRule: z.boolean().default(true),
|
|
@@ -848,6 +860,25 @@ function initMemorySchema(db) {
|
|
|
848
860
|
features TEXT DEFAULT '[]'
|
|
849
861
|
);
|
|
850
862
|
`);
|
|
863
|
+
db.exec(`
|
|
864
|
+
CREATE TABLE IF NOT EXISTS failure_classes (
|
|
865
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
866
|
+
name TEXT NOT NULL UNIQUE,
|
|
867
|
+
description TEXT NOT NULL,
|
|
868
|
+
diff_patterns TEXT NOT NULL DEFAULT '[]',
|
|
869
|
+
file_patterns TEXT NOT NULL DEFAULT '[]',
|
|
870
|
+
prompt_keywords TEXT NOT NULL DEFAULT '[]',
|
|
871
|
+
incidents TEXT NOT NULL DEFAULT '[]',
|
|
872
|
+
rules TEXT NOT NULL DEFAULT '[]',
|
|
873
|
+
scanner_checks TEXT NOT NULL DEFAULT '[]',
|
|
874
|
+
known_message TEXT NOT NULL DEFAULT '',
|
|
875
|
+
needs_review INTEGER NOT NULL DEFAULT 0,
|
|
876
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
877
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
878
|
+
);
|
|
879
|
+
CREATE INDEX IF NOT EXISTS idx_fc_name ON failure_classes(name);
|
|
880
|
+
CREATE INDEX IF NOT EXISTS idx_fc_needs_review ON failure_classes(needs_review);
|
|
881
|
+
`);
|
|
851
882
|
}
|
|
852
883
|
function assignImportance(type, vrResult) {
|
|
853
884
|
switch (type) {
|
|
@@ -150,6 +150,18 @@ var AutoLearningConfigSchema = z.object({
|
|
|
150
150
|
"added_missing_import"
|
|
151
151
|
])
|
|
152
152
|
}).default({}),
|
|
153
|
+
failureClassification: z.object({
|
|
154
|
+
enabled: z.boolean().default(true),
|
|
155
|
+
thresholds: z.object({
|
|
156
|
+
known: z.number().default(5),
|
|
157
|
+
similar: z.number().default(3)
|
|
158
|
+
}).default({}),
|
|
159
|
+
scoring: z.object({
|
|
160
|
+
diffPatternWeight: z.number().default(3),
|
|
161
|
+
filePatternWeight: z.number().default(2),
|
|
162
|
+
promptKeywordWeight: z.number().default(2)
|
|
163
|
+
}).default({})
|
|
164
|
+
}).default({}),
|
|
153
165
|
pipeline: z.object({
|
|
154
166
|
requireIncidentReport: z.boolean().default(true),
|
|
155
167
|
requirePreventionRule: z.boolean().default(true),
|
|
@@ -151,6 +151,18 @@ var AutoLearningConfigSchema = z.object({
|
|
|
151
151
|
"added_missing_import"
|
|
152
152
|
])
|
|
153
153
|
}).default({}),
|
|
154
|
+
failureClassification: z.object({
|
|
155
|
+
enabled: z.boolean().default(true),
|
|
156
|
+
thresholds: z.object({
|
|
157
|
+
known: z.number().default(5),
|
|
158
|
+
similar: z.number().default(3)
|
|
159
|
+
}).default({}),
|
|
160
|
+
scoring: z.object({
|
|
161
|
+
diffPatternWeight: z.number().default(3),
|
|
162
|
+
filePatternWeight: z.number().default(2),
|
|
163
|
+
promptKeywordWeight: z.number().default(2)
|
|
164
|
+
}).default({})
|
|
165
|
+
}).default({}),
|
|
154
166
|
pipeline: z.object({
|
|
155
167
|
requireIncidentReport: z.boolean().default(true),
|
|
156
168
|
requirePreventionRule: z.boolean().default(true),
|
|
@@ -848,6 +860,25 @@ function initMemorySchema(db) {
|
|
|
848
860
|
features TEXT DEFAULT '[]'
|
|
849
861
|
);
|
|
850
862
|
`);
|
|
863
|
+
db.exec(`
|
|
864
|
+
CREATE TABLE IF NOT EXISTS failure_classes (
|
|
865
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
866
|
+
name TEXT NOT NULL UNIQUE,
|
|
867
|
+
description TEXT NOT NULL,
|
|
868
|
+
diff_patterns TEXT NOT NULL DEFAULT '[]',
|
|
869
|
+
file_patterns TEXT NOT NULL DEFAULT '[]',
|
|
870
|
+
prompt_keywords TEXT NOT NULL DEFAULT '[]',
|
|
871
|
+
incidents TEXT NOT NULL DEFAULT '[]',
|
|
872
|
+
rules TEXT NOT NULL DEFAULT '[]',
|
|
873
|
+
scanner_checks TEXT NOT NULL DEFAULT '[]',
|
|
874
|
+
known_message TEXT NOT NULL DEFAULT '',
|
|
875
|
+
needs_review INTEGER NOT NULL DEFAULT 0,
|
|
876
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
877
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
878
|
+
);
|
|
879
|
+
CREATE INDEX IF NOT EXISTS idx_fc_name ON failure_classes(name);
|
|
880
|
+
CREATE INDEX IF NOT EXISTS idx_fc_needs_review ON failure_classes(needs_review);
|
|
881
|
+
`);
|
|
851
882
|
}
|
|
852
883
|
|
|
853
884
|
// src/hooks/quality-event.ts
|
|
@@ -149,6 +149,18 @@ var AutoLearningConfigSchema = z.object({
|
|
|
149
149
|
"added_missing_import"
|
|
150
150
|
])
|
|
151
151
|
}).default({}),
|
|
152
|
+
failureClassification: z.object({
|
|
153
|
+
enabled: z.boolean().default(true),
|
|
154
|
+
thresholds: z.object({
|
|
155
|
+
known: z.number().default(5),
|
|
156
|
+
similar: z.number().default(3)
|
|
157
|
+
}).default({}),
|
|
158
|
+
scoring: z.object({
|
|
159
|
+
diffPatternWeight: z.number().default(3),
|
|
160
|
+
filePatternWeight: z.number().default(2),
|
|
161
|
+
promptKeywordWeight: z.number().default(2)
|
|
162
|
+
}).default({})
|
|
163
|
+
}).default({}),
|
|
152
164
|
pipeline: z.object({
|
|
153
165
|
requireIncidentReport: z.boolean().default(true),
|
|
154
166
|
requirePreventionRule: z.boolean().default(true),
|
|
@@ -348,7 +360,8 @@ async function main() {
|
|
|
348
360
|
process.exit(0);
|
|
349
361
|
return;
|
|
350
362
|
}
|
|
351
|
-
|
|
363
|
+
const claudeDir = config.conventions?.claudeDirName ?? ".claude";
|
|
364
|
+
if (!relPath.includes(memoryDir) && !relPath.includes("memory/") && !relPath.includes(claudeDir + "/")) {
|
|
352
365
|
process.exit(0);
|
|
353
366
|
return;
|
|
354
367
|
}
|
|
@@ -151,6 +151,18 @@ var AutoLearningConfigSchema = z.object({
|
|
|
151
151
|
"added_missing_import"
|
|
152
152
|
])
|
|
153
153
|
}).default({}),
|
|
154
|
+
failureClassification: z.object({
|
|
155
|
+
enabled: z.boolean().default(true),
|
|
156
|
+
thresholds: z.object({
|
|
157
|
+
known: z.number().default(5),
|
|
158
|
+
similar: z.number().default(3)
|
|
159
|
+
}).default({}),
|
|
160
|
+
scoring: z.object({
|
|
161
|
+
diffPatternWeight: z.number().default(3),
|
|
162
|
+
filePatternWeight: z.number().default(2),
|
|
163
|
+
promptKeywordWeight: z.number().default(2)
|
|
164
|
+
}).default({})
|
|
165
|
+
}).default({}),
|
|
154
166
|
pipeline: z.object({
|
|
155
167
|
requireIncidentReport: z.boolean().default(true),
|
|
156
168
|
requirePreventionRule: z.boolean().default(true),
|
|
@@ -848,6 +860,25 @@ function initMemorySchema(db) {
|
|
|
848
860
|
features TEXT DEFAULT '[]'
|
|
849
861
|
);
|
|
850
862
|
`);
|
|
863
|
+
db.exec(`
|
|
864
|
+
CREATE TABLE IF NOT EXISTS failure_classes (
|
|
865
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
866
|
+
name TEXT NOT NULL UNIQUE,
|
|
867
|
+
description TEXT NOT NULL,
|
|
868
|
+
diff_patterns TEXT NOT NULL DEFAULT '[]',
|
|
869
|
+
file_patterns TEXT NOT NULL DEFAULT '[]',
|
|
870
|
+
prompt_keywords TEXT NOT NULL DEFAULT '[]',
|
|
871
|
+
incidents TEXT NOT NULL DEFAULT '[]',
|
|
872
|
+
rules TEXT NOT NULL DEFAULT '[]',
|
|
873
|
+
scanner_checks TEXT NOT NULL DEFAULT '[]',
|
|
874
|
+
known_message TEXT NOT NULL DEFAULT '',
|
|
875
|
+
needs_review INTEGER NOT NULL DEFAULT 0,
|
|
876
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
877
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
878
|
+
);
|
|
879
|
+
CREATE INDEX IF NOT EXISTS idx_fc_name ON failure_classes(name);
|
|
880
|
+
CREATE INDEX IF NOT EXISTS idx_fc_needs_review ON failure_classes(needs_review);
|
|
881
|
+
`);
|
|
851
882
|
}
|
|
852
883
|
function enqueueSyncPayload(db, payload) {
|
|
853
884
|
db.prepare("INSERT INTO pending_sync (payload) VALUES (?)").run(payload);
|
|
@@ -151,6 +151,18 @@ var AutoLearningConfigSchema = z.object({
|
|
|
151
151
|
"added_missing_import"
|
|
152
152
|
])
|
|
153
153
|
}).default({}),
|
|
154
|
+
failureClassification: z.object({
|
|
155
|
+
enabled: z.boolean().default(true),
|
|
156
|
+
thresholds: z.object({
|
|
157
|
+
known: z.number().default(5),
|
|
158
|
+
similar: z.number().default(3)
|
|
159
|
+
}).default({}),
|
|
160
|
+
scoring: z.object({
|
|
161
|
+
diffPatternWeight: z.number().default(3),
|
|
162
|
+
filePatternWeight: z.number().default(2),
|
|
163
|
+
promptKeywordWeight: z.number().default(2)
|
|
164
|
+
}).default({})
|
|
165
|
+
}).default({}),
|
|
154
166
|
pipeline: z.object({
|
|
155
167
|
requireIncidentReport: z.boolean().default(true),
|
|
156
168
|
requirePreventionRule: z.boolean().default(true),
|
|
@@ -854,6 +866,25 @@ function initMemorySchema(db) {
|
|
|
854
866
|
features TEXT DEFAULT '[]'
|
|
855
867
|
);
|
|
856
868
|
`);
|
|
869
|
+
db.exec(`
|
|
870
|
+
CREATE TABLE IF NOT EXISTS failure_classes (
|
|
871
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
872
|
+
name TEXT NOT NULL UNIQUE,
|
|
873
|
+
description TEXT NOT NULL,
|
|
874
|
+
diff_patterns TEXT NOT NULL DEFAULT '[]',
|
|
875
|
+
file_patterns TEXT NOT NULL DEFAULT '[]',
|
|
876
|
+
prompt_keywords TEXT NOT NULL DEFAULT '[]',
|
|
877
|
+
incidents TEXT NOT NULL DEFAULT '[]',
|
|
878
|
+
rules TEXT NOT NULL DEFAULT '[]',
|
|
879
|
+
scanner_checks TEXT NOT NULL DEFAULT '[]',
|
|
880
|
+
known_message TEXT NOT NULL DEFAULT '',
|
|
881
|
+
needs_review INTEGER NOT NULL DEFAULT 0,
|
|
882
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
883
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
884
|
+
);
|
|
885
|
+
CREATE INDEX IF NOT EXISTS idx_fc_name ON failure_classes(name);
|
|
886
|
+
CREATE INDEX IF NOT EXISTS idx_fc_needs_review ON failure_classes(needs_review);
|
|
887
|
+
`);
|
|
857
888
|
}
|
|
858
889
|
function autoDetectTaskId(planFile) {
|
|
859
890
|
if (!planFile) return null;
|
|
@@ -955,7 +986,7 @@ async function main() {
|
|
|
955
986
|
process.stdout.write(
|
|
956
987
|
`=== MASSU AI: Active ===
|
|
957
988
|
Session memory, code intelligence, and governance are now active.
|
|
958
|
-
|
|
989
|
+
15 hooks monitoring this session. Type "${getConfig().toolPrefix ?? "massu"}_sync" to index your codebase.
|
|
959
990
|
=== END MASSU ===
|
|
960
991
|
|
|
961
992
|
`
|
|
@@ -151,6 +151,18 @@ var AutoLearningConfigSchema = z.object({
|
|
|
151
151
|
"added_missing_import"
|
|
152
152
|
])
|
|
153
153
|
}).default({}),
|
|
154
|
+
failureClassification: z.object({
|
|
155
|
+
enabled: z.boolean().default(true),
|
|
156
|
+
thresholds: z.object({
|
|
157
|
+
known: z.number().default(5),
|
|
158
|
+
similar: z.number().default(3)
|
|
159
|
+
}).default({}),
|
|
160
|
+
scoring: z.object({
|
|
161
|
+
diffPatternWeight: z.number().default(3),
|
|
162
|
+
filePatternWeight: z.number().default(2),
|
|
163
|
+
promptKeywordWeight: z.number().default(2)
|
|
164
|
+
}).default({})
|
|
165
|
+
}).default({}),
|
|
154
166
|
pipeline: z.object({
|
|
155
167
|
requireIncidentReport: z.boolean().default(true),
|
|
156
168
|
requirePreventionRule: z.boolean().default(true),
|
|
@@ -848,6 +860,25 @@ function initMemorySchema(db) {
|
|
|
848
860
|
features TEXT DEFAULT '[]'
|
|
849
861
|
);
|
|
850
862
|
`);
|
|
863
|
+
db.exec(`
|
|
864
|
+
CREATE TABLE IF NOT EXISTS failure_classes (
|
|
865
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
866
|
+
name TEXT NOT NULL UNIQUE,
|
|
867
|
+
description TEXT NOT NULL,
|
|
868
|
+
diff_patterns TEXT NOT NULL DEFAULT '[]',
|
|
869
|
+
file_patterns TEXT NOT NULL DEFAULT '[]',
|
|
870
|
+
prompt_keywords TEXT NOT NULL DEFAULT '[]',
|
|
871
|
+
incidents TEXT NOT NULL DEFAULT '[]',
|
|
872
|
+
rules TEXT NOT NULL DEFAULT '[]',
|
|
873
|
+
scanner_checks TEXT NOT NULL DEFAULT '[]',
|
|
874
|
+
known_message TEXT NOT NULL DEFAULT '',
|
|
875
|
+
needs_review INTEGER NOT NULL DEFAULT 0,
|
|
876
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
877
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
878
|
+
);
|
|
879
|
+
CREATE INDEX IF NOT EXISTS idx_fc_name ON failure_classes(name);
|
|
880
|
+
CREATE INDEX IF NOT EXISTS idx_fc_needs_review ON failure_classes(needs_review);
|
|
881
|
+
`);
|
|
851
882
|
}
|
|
852
883
|
function assignImportance(type, vrResult) {
|
|
853
884
|
switch (type) {
|
|
@@ -921,7 +952,9 @@ function linkSessionToTask(db, sessionId, taskId) {
|
|
|
921
952
|
}
|
|
922
953
|
|
|
923
954
|
// src/hooks/user-prompt.ts
|
|
924
|
-
import { existsSync as existsSync3 } from "fs";
|
|
955
|
+
import { existsSync as existsSync3, writeFileSync } from "fs";
|
|
956
|
+
import { tmpdir } from "os";
|
|
957
|
+
import { join } from "path";
|
|
925
958
|
async function main() {
|
|
926
959
|
try {
|
|
927
960
|
const input = await readStdin();
|
|
@@ -994,6 +1027,35 @@ async function main() {
|
|
|
994
1027
|
}
|
|
995
1028
|
} catch (_memoryNagErr) {
|
|
996
1029
|
}
|
|
1030
|
+
try {
|
|
1031
|
+
const failureKeywords = [
|
|
1032
|
+
"bug",
|
|
1033
|
+
"broken",
|
|
1034
|
+
"crash",
|
|
1035
|
+
"error",
|
|
1036
|
+
"fail",
|
|
1037
|
+
"fix",
|
|
1038
|
+
"wrong",
|
|
1039
|
+
"missing",
|
|
1040
|
+
"undefined",
|
|
1041
|
+
"null",
|
|
1042
|
+
"exception",
|
|
1043
|
+
"stack trace",
|
|
1044
|
+
"regression",
|
|
1045
|
+
"revert",
|
|
1046
|
+
"doesn't work",
|
|
1047
|
+
"not working",
|
|
1048
|
+
"stopped working",
|
|
1049
|
+
"broke"
|
|
1050
|
+
];
|
|
1051
|
+
const promptLower = prompt.toLowerCase();
|
|
1052
|
+
const matched = failureKeywords.filter((kw) => promptLower.includes(kw));
|
|
1053
|
+
if (matched.length > 0) {
|
|
1054
|
+
const contextFile = join(tmpdir(), `massu-failure-context-${session_id.slice(0, 8)}-${Date.now()}`);
|
|
1055
|
+
writeFileSync(contextFile, matched.join(" "), "utf-8");
|
|
1056
|
+
}
|
|
1057
|
+
} catch {
|
|
1058
|
+
}
|
|
997
1059
|
} finally {
|
|
998
1060
|
db.close();
|
|
999
1061
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@massu/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AI Engineering Governance MCP Server - Session memory, knowledge system, feature registry, code intelligence, rule enforcement, auto-learning pipeline, tiered tooling (12 free / 72 total), 55+ workflow commands, 15 agents, 20+ patterns",
|
|
6
6
|
"main": "src/server.ts",
|
|
@@ -52,9 +52,9 @@ Security blocking -> advisory warnings -> matcher-specific -> observability.
|
|
|
52
52
|
|
|
53
53
|
---
|
|
54
54
|
|
|
55
|
-
## PostToolUse (
|
|
55
|
+
## PostToolUse (14 hooks)
|
|
56
56
|
|
|
57
|
-
Security scan -> immediate feedback -> context tracking -> incident capture -> memory sync -> observability.
|
|
57
|
+
Security scan -> immediate feedback -> context tracking -> fix detection -> incident capture -> pipeline triggers -> memory sync -> observability.
|
|
58
58
|
|
|
59
59
|
| position: 1 | CI monitor | standard | Bash(git push) -- immediate push feedback |
|
|
60
60
|
|---|---|---|---|
|
|
@@ -62,17 +62,23 @@ Security scan -> immediate feedback -> context tracking -> incident capture -> m
|
|
|
62
62
|
| position: 3 | `pattern-feedback.sh` | standard | Edit\|Write -- immediate pattern violation feedback |
|
|
63
63
|
| position: 4 | `post-edit-context.js` | strict | Edit\|Write -- detailed semantic analysis |
|
|
64
64
|
| position: 5 | `post-tool-use.js` | standard | Edit\|Write\|Bash -- structured context tracking |
|
|
65
|
-
| position: 6 | `
|
|
66
|
-
| position: 7 | `
|
|
67
|
-
| position: 8 | `
|
|
68
|
-
| position: 9 | `
|
|
69
|
-
| position: 10 | `
|
|
70
|
-
| position: 11 | `
|
|
65
|
+
| position: 6 | `fix-detector.js` | standard | Edit\|Write -- detect bug fixes via git diff heuristics |
|
|
66
|
+
| position: 7 | `auto-ingest-incident.sh` | strict | Edit\|Write -- auto-capture incident patterns |
|
|
67
|
+
| position: 8 | `incident-pipeline.js` | standard | Write -- trigger rule derivation on incident report writes |
|
|
68
|
+
| position: 9 | `rule-enforcement-pipeline.js` | standard | Write -- trigger enforcement on prevention rule writes |
|
|
69
|
+
| position: 10 | `memory-auto-ingest.sh` | standard | Write -- auto-sync memory files to codegraph SQLite DB |
|
|
70
|
+
| position: 11 | `validate-deliverables.sh` | strict | Bash\|Edit\|Write -- deliverable validation |
|
|
71
|
+
| position: 12 | `pattern-scanner.sh --single-file` | strict | Edit\|Write -- per-file pattern scan |
|
|
72
|
+
| position: 13 | `mcp-usage-tracker.sh` | strict | MCP tools -- append-only MCP audit log |
|
|
73
|
+
| position: 14 | `compaction-advisor.sh` | standard | Bash\|Edit\|Write\|Read\|Grep\|Glob -- context tracking, widest matcher |
|
|
71
74
|
|
|
72
75
|
**Dependencies**:
|
|
73
76
|
- `output-secret-filter.sh` MUST run before any feedback hooks -- security first
|
|
74
77
|
- `pattern-feedback.sh` before `post-tool-use.js` -- immediate feedback before tracking
|
|
75
|
-
- `
|
|
78
|
+
- `fix-detector.js` after `post-tool-use.js` -- needs structured tracking context
|
|
79
|
+
- `incident-pipeline.js` after `auto-ingest-incident.sh` -- incident must be captured first
|
|
80
|
+
- `rule-enforcement-pipeline.js` after `incident-pipeline.js` -- rule derivation before enforcement
|
|
81
|
+
- `memory-auto-ingest.sh` runs after pipeline hooks -- memory sync is data-writing, before validation
|
|
76
82
|
- `compaction-advisor.sh` MUST be last -- widest matcher, just counts tool calls
|
|
77
83
|
|
|
78
84
|
---
|
|
@@ -105,21 +111,23 @@ Quick state capture -> full DB snapshot.
|
|
|
105
111
|
|
|
106
112
|
---
|
|
107
113
|
|
|
108
|
-
## Stop (
|
|
114
|
+
## Stop (8 hooks)
|
|
109
115
|
|
|
110
|
-
Session summary -> warnings -> memory extraction -> review -> validation.
|
|
116
|
+
Session summary -> auto-learning check -> warnings -> memory extraction -> review -> validation.
|
|
111
117
|
|
|
112
118
|
| position: 1 | `session-end.js` | standard | Write session summary to memory DB |
|
|
113
119
|
|---|---|---|---|
|
|
114
|
-
| position: 2 |
|
|
115
|
-
| position: 3 |
|
|
116
|
-
| position: 4 | `auto-
|
|
117
|
-
| position: 5 | `
|
|
118
|
-
| position: 6 | `
|
|
119
|
-
| position: 7 | `
|
|
120
|
+
| position: 2 | `auto-learning-pipeline.js` | standard | Enforce fix→incident→rule→enforcement pipeline completion |
|
|
121
|
+
| position: 3 | Uncommitted changes warning | standard (inline) | Alert user about unstaged work |
|
|
122
|
+
| position: 4 | `memory-auto-extract.sh` | standard | Auto-extract memories from DB observations |
|
|
123
|
+
| position: 5 | `auto-review-on-stop.sh` | strict | Automated code review of session changes |
|
|
124
|
+
| position: 6 | `surface-review-findings.sh` | strict | Display review findings to user |
|
|
125
|
+
| position: 7 | `validate-deliverables.sh` | strict | Final deliverable validation |
|
|
126
|
+
| position: 8 | `pattern-extractor.sh` | advisory | Extract new patterns from session |
|
|
120
127
|
|
|
121
128
|
**Dependencies**:
|
|
122
129
|
- `session-end.js` MUST be position 1 -- writes DB data that `memory-auto-extract.sh` reads
|
|
130
|
+
- `auto-learning-pipeline.js` MUST run early -- needs to output mandatory instructions before session ends
|
|
123
131
|
- `memory-auto-extract.sh` MUST come after `session-end.js` -- depends on DB observations
|
|
124
132
|
- `surface-review-findings.sh` MUST come after `auto-review-on-stop.sh` -- displays its output
|
|
125
133
|
- `pattern-extractor.sh` runs last -- advisory tier (skipped in minimal/standard profiles)
|
package/src/commands/doctor.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* 1. massu.config.yaml exists and parses correctly
|
|
9
9
|
* 2. .mcp.json has massu entry
|
|
10
10
|
* 3. .claude/settings.local.json has hooks config
|
|
11
|
-
* 4. All
|
|
11
|
+
* 4. All 15 compiled hook files exist
|
|
12
12
|
* 5. Knowledge DB exists (.massu/memory.db)
|
|
13
13
|
* 6. Memory directory exists (~/.claude/projects/.../memory/)
|
|
14
14
|
* 7. Shell hooks wired in settings.local.json
|
package/src/commands/init.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* 1. Detects project framework (scans package.json)
|
|
8
8
|
* 2. Generates massu.config.yaml (or preserves existing)
|
|
9
9
|
* 3. Registers MCP server in .mcp.json (creates or merges)
|
|
10
|
-
* 4. Installs all
|
|
10
|
+
* 4. Installs all 15 hooks in .claude/settings.local.json
|
|
11
11
|
* 5. Installs slash commands into .claude/commands/
|
|
12
12
|
* 6. Initializes memory directory
|
|
13
13
|
* 7. Prints success summary
|
|
@@ -402,6 +402,15 @@ export function buildHooksConfig(hooksDir: string): HooksConfig {
|
|
|
402
402
|
matcher: 'Edit|Write',
|
|
403
403
|
hooks: [
|
|
404
404
|
{ type: 'command', command: hookCmd(hooksDir, 'post-edit-context.js'), timeout: 5 },
|
|
405
|
+
{ type: 'command', command: hookCmd(hooksDir, 'fix-detector.js'), timeout: 5 },
|
|
406
|
+
{ type: 'command', command: hookCmd(hooksDir, 'classify-failure.js'), timeout: 5 },
|
|
407
|
+
],
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
matcher: 'Write',
|
|
411
|
+
hooks: [
|
|
412
|
+
{ type: 'command', command: hookCmd(hooksDir, 'incident-pipeline.js'), timeout: 5 },
|
|
413
|
+
{ type: 'command', command: hookCmd(hooksDir, 'rule-enforcement-pipeline.js'), timeout: 5 },
|
|
405
414
|
],
|
|
406
415
|
},
|
|
407
416
|
],
|
|
@@ -409,6 +418,7 @@ export function buildHooksConfig(hooksDir: string): HooksConfig {
|
|
|
409
418
|
{
|
|
410
419
|
hooks: [
|
|
411
420
|
{ type: 'command', command: hookCmd(hooksDir, 'session-end.js'), timeout: 15 },
|
|
421
|
+
{ type: 'command', command: hookCmd(hooksDir, 'auto-learning-pipeline.js'), timeout: 10 },
|
|
412
422
|
],
|
|
413
423
|
},
|
|
414
424
|
],
|
package/src/config.ts
CHANGED
|
@@ -168,6 +168,18 @@ const AutoLearningConfigSchema = z.object({
|
|
|
168
168
|
'added_missing_import',
|
|
169
169
|
]),
|
|
170
170
|
}).default({}),
|
|
171
|
+
failureClassification: z.object({
|
|
172
|
+
enabled: z.boolean().default(true),
|
|
173
|
+
thresholds: z.object({
|
|
174
|
+
known: z.number().default(5),
|
|
175
|
+
similar: z.number().default(3),
|
|
176
|
+
}).default({}),
|
|
177
|
+
scoring: z.object({
|
|
178
|
+
diffPatternWeight: z.number().default(3),
|
|
179
|
+
filePatternWeight: z.number().default(2),
|
|
180
|
+
promptKeywordWeight: z.number().default(2),
|
|
181
|
+
}).default({}),
|
|
182
|
+
}).default({}),
|
|
171
183
|
pipeline: z.object({
|
|
172
184
|
requireIncidentReport: z.boolean().default(true),
|
|
173
185
|
requirePreventionRule: z.boolean().default(true),
|
|
@@ -67,14 +67,14 @@ async function main(): Promise<void> {
|
|
|
67
67
|
} catch { /* ignore parse errors */ }
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
// Source 2: Scan uncommitted git diff for fix patterns
|
|
70
|
+
// Source 2: Scan uncommitted git diff for fix patterns (language-agnostic)
|
|
71
71
|
let uncommittedFix = false;
|
|
72
72
|
try {
|
|
73
73
|
const diff = execSync('git diff --name-only', { cwd: root, timeout: 3000, encoding: 'utf-8' });
|
|
74
74
|
if (diff.trim()) {
|
|
75
75
|
const fullDiff = execSync('git diff', { cwd: root, timeout: 5000, encoding: 'utf-8' });
|
|
76
|
-
const fixPatterns = (fullDiff.match(/^\+.*(try|except|catch|guard
|
|
77
|
-
const removedBroken = (fullDiff.match(/^-.*(bug|broken|crash
|
|
76
|
+
const fixPatterns = (fullDiff.match(/^\+.*(try|except|catch|guard|throw|raise|assert|validate|if.*null|if.*nil|if.*None|if.*undefined)/gm) || []).length;
|
|
77
|
+
const removedBroken = (fullDiff.match(/^-.*(bug|broken|crash|wrong|incorrect|typo|fail|error|miss|stale)/gm) || []).length;
|
|
78
78
|
if (fixPatterns > 3 || removedBroken > 1) {
|
|
79
79
|
uncommittedFix = true;
|
|
80
80
|
}
|