@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.
@@ -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
- if (!relPath.includes(memoryDir) && !relPath.includes("memory/") && !relPath.includes(".claude/")) {
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
- 11 hooks monitoring this session. Type "${getConfig().toolPrefix ?? "massu"}_sync" to index your codebase.
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.7.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 (11 hooks)
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 | `auto-ingest-incident.sh` | strict | Edit\|Write -- auto-capture incident patterns |
66
- | position: 7 | `memory-auto-ingest.sh` | standard | Write -- auto-sync memory files to codegraph SQLite DB |
67
- | position: 8 | `validate-deliverables.sh` | strict | Bash\|Edit\|Write -- deliverable validation |
68
- | position: 9 | `pattern-scanner.sh --single-file` | strict | Edit\|Write -- per-file pattern scan |
69
- | position: 10 | `mcp-usage-tracker.sh` | strict | MCP tools -- append-only MCP audit log |
70
- | position: 11 | `compaction-advisor.sh` | standard | Bash\|Edit\|Write\|Read\|Grep\|Glob -- context tracking, widest matcher |
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
- - `memory-auto-ingest.sh` runs after incident capture -- memory sync is data-writing, before validation
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 (7 hooks)
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 | Uncommitted changes warning | standard (inline) | Alert user about unstaged work |
115
- | position: 3 | `memory-auto-extract.sh` | standard | Auto-extract memories from DB observations |
116
- | position: 4 | `auto-review-on-stop.sh` | strict | Automated code review of session changes |
117
- | position: 5 | `surface-review-findings.sh` | strict | Display review findings to user |
118
- | position: 6 | `validate-deliverables.sh` | strict | Final deliverable validation |
119
- | position: 7 | `pattern-extractor.sh` | advisory | Extract new patterns from session |
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)
@@ -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 compiled hook files exist
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
@@ -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 11 hooks in .claude/settings.local.json
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|@MainActor|asyncio\.timeout|X-Service-Token|\.save\(|return False)/gm) || []).length;
77
- const removedBroken = (fullDiff.match(/^-.*(bug|broken|crash|\.store\(|= nil|error)/gm) || []).length;
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
  }