@massu/core 0.6.2 → 0.7.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.
@@ -0,0 +1,440 @@
1
+ #!/usr/bin/env node
2
+ import{createRequire as __cr}from"module";const require=__cr(import.meta.url);
3
+
4
+ // src/hooks/rule-enforcement-pipeline.ts
5
+ import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync } from "fs";
6
+ import { basename, resolve as resolve2 } from "path";
7
+
8
+ // src/config.ts
9
+ import { resolve, dirname } from "path";
10
+ import { existsSync, readFileSync } from "fs";
11
+ import { parse as parseYaml } from "yaml";
12
+ import { z } from "zod";
13
+ var DomainConfigSchema = z.object({
14
+ name: z.string().default("Unknown"),
15
+ routers: z.array(z.string()).default([]),
16
+ pages: z.array(z.string()).default([]),
17
+ tables: z.array(z.string()).default([]),
18
+ allowedImportsFrom: z.array(z.string()).default([])
19
+ });
20
+ var PatternRuleConfigSchema = z.object({
21
+ pattern: z.string().default("**"),
22
+ rules: z.array(z.string()).default([])
23
+ });
24
+ var CostModelSchema = z.object({
25
+ input_per_million: z.number(),
26
+ output_per_million: z.number(),
27
+ cache_read_per_million: z.number().optional(),
28
+ cache_write_per_million: z.number().optional()
29
+ });
30
+ var AnalyticsConfigSchema = z.object({
31
+ quality: z.object({
32
+ weights: z.record(z.string(), z.number()).default({
33
+ bug_found: -5,
34
+ vr_failure: -10,
35
+ incident: -20,
36
+ cr_violation: -3,
37
+ vr_pass: 2,
38
+ clean_commit: 5,
39
+ successful_verification: 3
40
+ }),
41
+ categories: z.array(z.string()).default(["security", "architecture", "coupling", "tests", "rule_compliance"])
42
+ }).optional(),
43
+ cost: z.object({
44
+ models: z.record(z.string(), CostModelSchema).default({}),
45
+ currency: z.string().default("USD")
46
+ }).optional(),
47
+ prompts: z.object({
48
+ success_indicators: z.array(z.string()).default(["committed", "approved", "looks good", "perfect", "great", "thanks"]),
49
+ failure_indicators: z.array(z.string()).default(["revert", "wrong", "that's not", "undo", "incorrect"]),
50
+ max_turns_for_success: z.number().default(2)
51
+ }).optional()
52
+ }).optional();
53
+ var CustomPatternSchema = z.object({
54
+ pattern: z.string(),
55
+ severity: z.string(),
56
+ message: z.string()
57
+ });
58
+ var GovernanceConfigSchema = z.object({
59
+ audit: z.object({
60
+ formats: z.array(z.string()).default(["summary", "detailed", "soc2"]),
61
+ retention_days: z.number().default(365),
62
+ auto_log: z.record(z.string(), z.boolean()).default({
63
+ code_changes: true,
64
+ rule_enforcement: true,
65
+ approvals: true,
66
+ commits: true
67
+ })
68
+ }).optional(),
69
+ validation: z.object({
70
+ realtime: z.boolean().default(true),
71
+ checks: z.record(z.string(), z.boolean()).default({
72
+ rule_compliance: true,
73
+ import_existence: true,
74
+ naming_conventions: true
75
+ }),
76
+ custom_patterns: z.array(CustomPatternSchema).default([])
77
+ }).optional(),
78
+ adr: z.object({
79
+ detection_phrases: z.array(z.string()).default(["chose", "decided", "switching to", "moving from", "going with"]),
80
+ template: z.string().default("default"),
81
+ storage: z.string().default("database"),
82
+ output_dir: z.string().default("docs/adr")
83
+ }).optional()
84
+ }).optional();
85
+ var SecurityPatternSchema = z.object({
86
+ pattern: z.string(),
87
+ severity: z.string(),
88
+ category: z.string(),
89
+ description: z.string()
90
+ });
91
+ var SecurityConfigSchema = z.object({
92
+ patterns: z.array(SecurityPatternSchema).default([]),
93
+ auto_score_on_edit: z.boolean().default(true),
94
+ score_threshold_alert: z.number().default(50),
95
+ severity_weights: z.record(z.string(), z.number()).optional(),
96
+ restrictive_licenses: z.array(z.string()).optional(),
97
+ dep_alternatives: z.record(z.string(), z.array(z.string())).optional(),
98
+ dependencies: z.object({
99
+ package_manager: z.string().default("npm"),
100
+ blocked_packages: z.array(z.string()).default([]),
101
+ preferred_packages: z.record(z.string(), z.string()).default({}),
102
+ max_bundle_size_kb: z.number().default(500)
103
+ }).optional()
104
+ }).optional();
105
+ var TeamConfigSchema = z.object({
106
+ enabled: z.boolean().default(false),
107
+ sync_backend: z.string().default("local"),
108
+ developer_id: z.string().default("auto"),
109
+ share_by_default: z.boolean().default(false),
110
+ expertise_weights: z.object({
111
+ session: z.number().default(20),
112
+ observation: z.number().default(10)
113
+ }).optional(),
114
+ privacy: z.object({
115
+ share_file_paths: z.boolean().default(true),
116
+ share_code_snippets: z.boolean().default(false),
117
+ share_observations: z.boolean().default(true)
118
+ }).optional()
119
+ }).optional();
120
+ var RegressionConfigSchema = z.object({
121
+ test_patterns: z.array(z.string()).default([
122
+ "{dir}/__tests__/{name}.test.{ext}",
123
+ "{dir}/{name}.spec.{ext}",
124
+ "tests/{path}.test.{ext}"
125
+ ]),
126
+ test_runner: z.string().default("npm test"),
127
+ health_thresholds: z.object({
128
+ healthy: z.number().default(80),
129
+ warning: z.number().default(50)
130
+ }).optional()
131
+ }).optional();
132
+ var AutoLearningConfigSchema = z.object({
133
+ enabled: z.boolean().default(true),
134
+ incidentDir: z.string().default("docs/incidents"),
135
+ memoryDir: z.string().default("memory"),
136
+ memoryIndexFile: z.string().default("MEMORY.md"),
137
+ enforcementHooksDir: z.string().default("scripts/hooks"),
138
+ fixDetection: z.object({
139
+ enabled: z.boolean().default(true),
140
+ lookbackDays: z.number().default(7),
141
+ signals: z.array(z.string()).default([
142
+ "removed_broken_code",
143
+ "added_error_handling",
144
+ "method_name_correction",
145
+ "auth_fix",
146
+ "nil_handling_fix",
147
+ "concurrency_fix",
148
+ "async_pattern_fix",
149
+ "added_missing_import"
150
+ ])
151
+ }).default({}),
152
+ pipeline: z.object({
153
+ requireIncidentReport: z.boolean().default(true),
154
+ requirePreventionRule: z.boolean().default(true),
155
+ requireEnforcement: z.boolean().default(true)
156
+ }).default({})
157
+ }).optional();
158
+ var CloudConfigSchema = z.object({
159
+ enabled: z.boolean().default(false),
160
+ apiKey: z.string().optional(),
161
+ endpoint: z.string().optional(),
162
+ sync: z.object({
163
+ memory: z.boolean().default(true),
164
+ analytics: z.boolean().default(true),
165
+ audit: z.boolean().default(true)
166
+ }).default({ memory: true, analytics: true, audit: true })
167
+ }).optional();
168
+ var ConventionsConfigSchema = z.object({
169
+ claudeDirName: z.string().default(".claude").refine(
170
+ (s) => !s.includes("..") && !s.startsWith("/"),
171
+ { message: 'claudeDirName must not contain ".." or start with "/"' }
172
+ ),
173
+ sessionStatePath: z.string().default(".claude/session-state/CURRENT.md").refine(
174
+ (s) => !s.includes("..") && !s.startsWith("/"),
175
+ { message: 'sessionStatePath must not contain ".." or start with "/"' }
176
+ ),
177
+ sessionArchivePath: z.string().default(".claude/session-state/archive").refine(
178
+ (s) => !s.includes("..") && !s.startsWith("/"),
179
+ { message: 'sessionArchivePath must not contain ".." or start with "/"' }
180
+ ),
181
+ knowledgeCategories: z.array(z.string()).default([
182
+ "patterns",
183
+ "commands",
184
+ "incidents",
185
+ "reference",
186
+ "protocols",
187
+ "checklists",
188
+ "playbooks",
189
+ "critical",
190
+ "scripts",
191
+ "status",
192
+ "templates",
193
+ "loop-state",
194
+ "session-state",
195
+ "agents"
196
+ ]),
197
+ knowledgeSourceFiles: z.array(z.string()).default(["CLAUDE.md", "MEMORY.md", "corrections.md"]),
198
+ excludePatterns: z.array(z.string()).default(["/ARCHIVE/", "/SESSION-HISTORY/"])
199
+ }).optional();
200
+ var PythonDomainConfigSchema = z.object({
201
+ name: z.string(),
202
+ packages: z.array(z.string()),
203
+ allowed_imports_from: z.array(z.string()).default([])
204
+ });
205
+ var PythonConfigSchema = z.object({
206
+ root: z.string(),
207
+ alembic_dir: z.string().optional(),
208
+ domains: z.array(PythonDomainConfigSchema).default([]),
209
+ exclude_dirs: z.array(z.string()).default(["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"])
210
+ }).optional();
211
+ var PathsConfigSchema = z.object({
212
+ source: z.string().default("src"),
213
+ aliases: z.record(z.string(), z.string()).default({ "@": "src" }),
214
+ routers: z.string().optional(),
215
+ routerRoot: z.string().optional(),
216
+ pages: z.string().optional(),
217
+ middleware: z.string().optional(),
218
+ schema: z.string().optional(),
219
+ components: z.string().optional(),
220
+ hooks: z.string().optional()
221
+ });
222
+ var RawConfigSchema = z.object({
223
+ project: z.object({
224
+ name: z.string().default("my-project"),
225
+ root: z.string().default("auto")
226
+ }).default({ name: "my-project", root: "auto" }),
227
+ framework: z.object({
228
+ type: z.string().default("typescript"),
229
+ router: z.string().default("none"),
230
+ orm: z.string().default("none"),
231
+ ui: z.string().default("none")
232
+ }).default({ type: "typescript", router: "none", orm: "none", ui: "none" }),
233
+ paths: PathsConfigSchema.default({ source: "src", aliases: { "@": "src" } }),
234
+ toolPrefix: z.string().default("massu"),
235
+ dbAccessPattern: z.string().optional(),
236
+ knownMismatches: z.record(z.string(), z.record(z.string(), z.string())).optional(),
237
+ accessScopes: z.array(z.string()).optional(),
238
+ domains: z.array(DomainConfigSchema).default([]),
239
+ rules: z.array(PatternRuleConfigSchema).default([]),
240
+ analytics: AnalyticsConfigSchema,
241
+ governance: GovernanceConfigSchema,
242
+ security: SecurityConfigSchema,
243
+ team: TeamConfigSchema,
244
+ regression: RegressionConfigSchema,
245
+ cloud: CloudConfigSchema,
246
+ conventions: ConventionsConfigSchema,
247
+ python: PythonConfigSchema,
248
+ autoLearning: AutoLearningConfigSchema
249
+ }).passthrough();
250
+ var _config = null;
251
+ var _projectRoot = null;
252
+ function findProjectRoot() {
253
+ const cwd = process.cwd();
254
+ let dir = cwd;
255
+ while (true) {
256
+ if (existsSync(resolve(dir, "massu.config.yaml"))) {
257
+ return dir;
258
+ }
259
+ const parent = dirname(dir);
260
+ if (parent === dir) break;
261
+ dir = parent;
262
+ }
263
+ dir = cwd;
264
+ while (true) {
265
+ if (existsSync(resolve(dir, "package.json"))) {
266
+ return dir;
267
+ }
268
+ if (existsSync(resolve(dir, ".git"))) {
269
+ return dir;
270
+ }
271
+ const parent = dirname(dir);
272
+ if (parent === dir) break;
273
+ dir = parent;
274
+ }
275
+ return cwd;
276
+ }
277
+ function getProjectRoot() {
278
+ if (!_projectRoot) {
279
+ _projectRoot = findProjectRoot();
280
+ }
281
+ return _projectRoot;
282
+ }
283
+ function getConfig() {
284
+ if (_config) return _config;
285
+ const root = getProjectRoot();
286
+ const configPath = resolve(root, "massu.config.yaml");
287
+ let rawYaml = {};
288
+ if (existsSync(configPath)) {
289
+ const content = readFileSync(configPath, "utf-8");
290
+ rawYaml = parseYaml(content) ?? {};
291
+ }
292
+ const parsed = RawConfigSchema.parse(rawYaml);
293
+ const projectRoot = parsed.project.root === "auto" || !parsed.project.root ? root : resolve(root, parsed.project.root);
294
+ _config = {
295
+ project: {
296
+ name: parsed.project.name,
297
+ root: projectRoot
298
+ },
299
+ framework: parsed.framework,
300
+ paths: parsed.paths,
301
+ toolPrefix: parsed.toolPrefix,
302
+ dbAccessPattern: parsed.dbAccessPattern,
303
+ knownMismatches: parsed.knownMismatches,
304
+ accessScopes: parsed.accessScopes,
305
+ domains: parsed.domains,
306
+ rules: parsed.rules,
307
+ analytics: parsed.analytics,
308
+ governance: parsed.governance,
309
+ security: parsed.security,
310
+ team: parsed.team,
311
+ regression: parsed.regression,
312
+ cloud: parsed.cloud,
313
+ conventions: parsed.conventions,
314
+ python: parsed.python
315
+ };
316
+ if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
317
+ _config.cloud = {
318
+ enabled: true,
319
+ sync: { memory: true, analytics: true, audit: true },
320
+ ..._config.cloud,
321
+ apiKey: process.env.MASSU_API_KEY
322
+ };
323
+ }
324
+ return _config;
325
+ }
326
+
327
+ // src/hooks/rule-enforcement-pipeline.ts
328
+ async function main() {
329
+ try {
330
+ const input = await readStdin();
331
+ const hookInput = JSON.parse(input);
332
+ const filePath = hookInput.tool_input?.file_path;
333
+ if (!filePath) {
334
+ process.exit(0);
335
+ return;
336
+ }
337
+ const config = getConfig();
338
+ if (config.autoLearning?.enabled === false) {
339
+ process.exit(0);
340
+ return;
341
+ }
342
+ const root = getProjectRoot();
343
+ const memoryDir = config.autoLearning?.memoryDir ?? "memory";
344
+ const enforcementDir = config.autoLearning?.enforcementHooksDir ?? "scripts/hooks";
345
+ const relPath = filePath.startsWith(root + "/") ? filePath.slice(root.length + 1) : filePath;
346
+ const fileName = basename(filePath);
347
+ if (!fileName.startsWith("feedback_") || !fileName.endsWith(".md")) {
348
+ process.exit(0);
349
+ return;
350
+ }
351
+ if (!relPath.includes(memoryDir) && !relPath.includes("memory/") && !relPath.includes(".claude/")) {
352
+ process.exit(0);
353
+ return;
354
+ }
355
+ if (!existsSync2(filePath)) {
356
+ process.exit(0);
357
+ return;
358
+ }
359
+ if (config.autoLearning?.pipeline?.requireEnforcement === false) {
360
+ process.exit(0);
361
+ return;
362
+ }
363
+ const content = readFileSync2(filePath, "utf-8");
364
+ const nameMatch = content.match(/^name:\s*(.+)/m);
365
+ const descMatch = content.match(/^description:\s*(.+)/m);
366
+ const ruleName = nameMatch?.[1]?.trim() ?? fileName;
367
+ const ruleDesc = descMatch?.[1]?.trim() ?? "";
368
+ const enforcementDirAbs = resolve2(root, enforcementDir);
369
+ let hasEnforcement = false;
370
+ if (existsSync2(enforcementDirAbs)) {
371
+ const hookFiles = readdirSync(enforcementDirAbs).filter((f) => f.endsWith(".sh") || f.endsWith(".ts") || f.endsWith(".js"));
372
+ for (const hookFile of hookFiles) {
373
+ try {
374
+ const hookContent = readFileSync2(resolve2(enforcementDirAbs, hookFile), "utf-8");
375
+ if (hookContent.includes(fileName)) {
376
+ hasEnforcement = true;
377
+ break;
378
+ }
379
+ } catch {
380
+ }
381
+ }
382
+ }
383
+ if (hasEnforcement) {
384
+ process.exit(0);
385
+ return;
386
+ }
387
+ const lines = [];
388
+ lines.push("");
389
+ lines.push("============================================================================");
390
+ lines.push(" AUTO-LEARNING: New Rule Created \u2014 Enforcement Placement Required");
391
+ lines.push("============================================================================");
392
+ lines.push("");
393
+ lines.push(` Rule: ${ruleName}`);
394
+ lines.push(` File: ${filePath}`);
395
+ if (ruleDesc) {
396
+ lines.push(` Description: ${ruleDesc}`);
397
+ }
398
+ lines.push("");
399
+ lines.push(" This rule has NO automated enforcement yet. Add it now.");
400
+ lines.push("");
401
+ lines.push(" ANALYZE the rule and determine enforcement layer(s):");
402
+ lines.push("");
403
+ lines.push(" 1. STATICALLY DETECTABLE? (grep/regex can find violations in code)");
404
+ lines.push(` \u2192 Add check to: ${enforcementDir}/pattern-feedback hook`);
405
+ lines.push(` \u2192 Also add to pre-commit hook if critical`);
406
+ lines.push("");
407
+ lines.push(" 2. ABOUT EDITING CERTAIN FILES? (auth, infra, routers, etc.)");
408
+ lines.push(` \u2192 Add warning to: ${enforcementDir}/blast-radius hook`);
409
+ lines.push("");
410
+ lines.push(" 3. ABOUT DANGEROUS COMMANDS? (kill, rm, destructive ops)");
411
+ lines.push(` \u2192 Add block to: ${enforcementDir}/dangerous-command hook`);
412
+ lines.push("");
413
+ lines.push(" 4. NEEDS RUNTIME MONITORING? (can only be detected at runtime)");
414
+ lines.push(" \u2192 Create a monitoring/audit producer");
415
+ lines.push("");
416
+ lines.push(" 5. AI-GUIDANCE ONLY? (philosophy, process, judgment calls)");
417
+ lines.push(" \u2192 Memory rule is sufficient (already created)");
418
+ lines.push("");
419
+ lines.push(" AFTER adding enforcement, test the hook to verify it detects violations.");
420
+ lines.push("");
421
+ lines.push(" This step is MANDATORY per the auto-learning pipeline.");
422
+ lines.push("============================================================================");
423
+ lines.push("");
424
+ console.log(lines.join("\n"));
425
+ } catch {
426
+ }
427
+ process.exit(0);
428
+ }
429
+ function readStdin() {
430
+ return new Promise((resolve3) => {
431
+ let data = "";
432
+ process.stdin.setEncoding("utf-8");
433
+ process.stdin.on("data", (chunk) => {
434
+ data += chunk;
435
+ });
436
+ process.stdin.on("end", () => resolve3(data));
437
+ setTimeout(() => resolve3(data), 3e3);
438
+ });
439
+ }
440
+ main();
@@ -131,6 +131,32 @@ var RegressionConfigSchema = z.object({
131
131
  warning: z.number().default(50)
132
132
  }).optional()
133
133
  }).optional();
134
+ var AutoLearningConfigSchema = z.object({
135
+ enabled: z.boolean().default(true),
136
+ incidentDir: z.string().default("docs/incidents"),
137
+ memoryDir: z.string().default("memory"),
138
+ memoryIndexFile: z.string().default("MEMORY.md"),
139
+ enforcementHooksDir: z.string().default("scripts/hooks"),
140
+ fixDetection: z.object({
141
+ enabled: z.boolean().default(true),
142
+ lookbackDays: z.number().default(7),
143
+ signals: z.array(z.string()).default([
144
+ "removed_broken_code",
145
+ "added_error_handling",
146
+ "method_name_correction",
147
+ "auth_fix",
148
+ "nil_handling_fix",
149
+ "concurrency_fix",
150
+ "async_pattern_fix",
151
+ "added_missing_import"
152
+ ])
153
+ }).default({}),
154
+ pipeline: z.object({
155
+ requireIncidentReport: z.boolean().default(true),
156
+ requirePreventionRule: z.boolean().default(true),
157
+ requireEnforcement: z.boolean().default(true)
158
+ }).default({})
159
+ }).optional();
134
160
  var CloudConfigSchema = z.object({
135
161
  enabled: z.boolean().default(false),
136
162
  apiKey: z.string().optional(),
@@ -220,7 +246,8 @@ var RawConfigSchema = z.object({
220
246
  regression: RegressionConfigSchema,
221
247
  cloud: CloudConfigSchema,
222
248
  conventions: ConventionsConfigSchema,
223
- python: PythonConfigSchema
249
+ python: PythonConfigSchema,
250
+ autoLearning: AutoLearningConfigSchema
224
251
  }).passthrough();
225
252
  var _config = null;
226
253
  var _projectRoot = null;
@@ -131,6 +131,32 @@ var RegressionConfigSchema = z.object({
131
131
  warning: z.number().default(50)
132
132
  }).optional()
133
133
  }).optional();
134
+ var AutoLearningConfigSchema = z.object({
135
+ enabled: z.boolean().default(true),
136
+ incidentDir: z.string().default("docs/incidents"),
137
+ memoryDir: z.string().default("memory"),
138
+ memoryIndexFile: z.string().default("MEMORY.md"),
139
+ enforcementHooksDir: z.string().default("scripts/hooks"),
140
+ fixDetection: z.object({
141
+ enabled: z.boolean().default(true),
142
+ lookbackDays: z.number().default(7),
143
+ signals: z.array(z.string()).default([
144
+ "removed_broken_code",
145
+ "added_error_handling",
146
+ "method_name_correction",
147
+ "auth_fix",
148
+ "nil_handling_fix",
149
+ "concurrency_fix",
150
+ "async_pattern_fix",
151
+ "added_missing_import"
152
+ ])
153
+ }).default({}),
154
+ pipeline: z.object({
155
+ requireIncidentReport: z.boolean().default(true),
156
+ requirePreventionRule: z.boolean().default(true),
157
+ requireEnforcement: z.boolean().default(true)
158
+ }).default({})
159
+ }).optional();
134
160
  var CloudConfigSchema = z.object({
135
161
  enabled: z.boolean().default(false),
136
162
  apiKey: z.string().optional(),
@@ -220,7 +246,8 @@ var RawConfigSchema = z.object({
220
246
  regression: RegressionConfigSchema,
221
247
  cloud: CloudConfigSchema,
222
248
  conventions: ConventionsConfigSchema,
223
- python: PythonConfigSchema
249
+ python: PythonConfigSchema,
250
+ autoLearning: AutoLearningConfigSchema
224
251
  }).passthrough();
225
252
  var _config = null;
226
253
  var _projectRoot = null;
@@ -131,6 +131,32 @@ var RegressionConfigSchema = z.object({
131
131
  warning: z.number().default(50)
132
132
  }).optional()
133
133
  }).optional();
134
+ var AutoLearningConfigSchema = z.object({
135
+ enabled: z.boolean().default(true),
136
+ incidentDir: z.string().default("docs/incidents"),
137
+ memoryDir: z.string().default("memory"),
138
+ memoryIndexFile: z.string().default("MEMORY.md"),
139
+ enforcementHooksDir: z.string().default("scripts/hooks"),
140
+ fixDetection: z.object({
141
+ enabled: z.boolean().default(true),
142
+ lookbackDays: z.number().default(7),
143
+ signals: z.array(z.string()).default([
144
+ "removed_broken_code",
145
+ "added_error_handling",
146
+ "method_name_correction",
147
+ "auth_fix",
148
+ "nil_handling_fix",
149
+ "concurrency_fix",
150
+ "async_pattern_fix",
151
+ "added_missing_import"
152
+ ])
153
+ }).default({}),
154
+ pipeline: z.object({
155
+ requireIncidentReport: z.boolean().default(true),
156
+ requirePreventionRule: z.boolean().default(true),
157
+ requireEnforcement: z.boolean().default(true)
158
+ }).default({})
159
+ }).optional();
134
160
  var CloudConfigSchema = z.object({
135
161
  enabled: z.boolean().default(false),
136
162
  apiKey: z.string().optional(),
@@ -220,7 +246,8 @@ var RawConfigSchema = z.object({
220
246
  regression: RegressionConfigSchema,
221
247
  cloud: CloudConfigSchema,
222
248
  conventions: ConventionsConfigSchema,
223
- python: PythonConfigSchema
249
+ python: PythonConfigSchema,
250
+ autoLearning: AutoLearningConfigSchema
224
251
  }).passthrough();
225
252
  var _config = null;
226
253
  var _projectRoot = null;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@massu/core",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "type": "module",
5
- "description": "AI Engineering Governance MCP Server - Session memory, knowledge system, feature registry, code intelligence, rule enforcement, tiered tooling (12 free / 72 total), 55+ workflow commands, 11 agents, 20+ patterns",
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",
7
7
  "bin": {
8
8
  "massu": "./dist/cli.js"
package/src/config.ts CHANGED
@@ -147,6 +147,35 @@ const RegressionConfigSchema = z.object({
147
147
  }).optional();
148
148
  export type RegressionConfig = z.infer<typeof RegressionConfigSchema>;
149
149
 
150
+ // --- Auto-Learning Config ---
151
+ const AutoLearningConfigSchema = z.object({
152
+ enabled: z.boolean().default(true),
153
+ incidentDir: z.string().default('docs/incidents'),
154
+ memoryDir: z.string().default('memory'),
155
+ memoryIndexFile: z.string().default('MEMORY.md'),
156
+ enforcementHooksDir: z.string().default('scripts/hooks'),
157
+ fixDetection: z.object({
158
+ enabled: z.boolean().default(true),
159
+ lookbackDays: z.number().default(7),
160
+ signals: z.array(z.string()).default([
161
+ 'removed_broken_code',
162
+ 'added_error_handling',
163
+ 'method_name_correction',
164
+ 'auth_fix',
165
+ 'nil_handling_fix',
166
+ 'concurrency_fix',
167
+ 'async_pattern_fix',
168
+ 'added_missing_import',
169
+ ]),
170
+ }).default({}),
171
+ pipeline: z.object({
172
+ requireIncidentReport: z.boolean().default(true),
173
+ requirePreventionRule: z.boolean().default(true),
174
+ requireEnforcement: z.boolean().default(true),
175
+ }).default({}),
176
+ }).optional();
177
+ export type AutoLearningConfig = z.infer<typeof AutoLearningConfigSchema>;
178
+
150
179
  // --- Cloud Config ---
151
180
  const CloudConfigSchema = z.object({
152
181
  enabled: z.boolean().default(false),
@@ -240,6 +269,7 @@ const RawConfigSchema = z.object({
240
269
  cloud: CloudConfigSchema,
241
270
  conventions: ConventionsConfigSchema,
242
271
  python: PythonConfigSchema,
272
+ autoLearning: AutoLearningConfigSchema,
243
273
  }).passthrough();
244
274
 
245
275
  // --- Final Config interface (derived from Zod) ---
@@ -261,6 +291,7 @@ export interface Config {
261
291
  cloud?: CloudConfig;
262
292
  conventions?: ConventionsConfig;
263
293
  python?: PythonConfig;
294
+ autoLearning?: AutoLearningConfig;
264
295
  }
265
296
 
266
297
  let _config: Config | null = null;