@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.
- package/README.md +40 -0
- package/dist/cli.js +471 -83
- package/dist/hooks/auto-learning-pipeline.js +469 -0
- package/dist/hooks/cost-tracker.js +28 -1
- package/dist/hooks/fix-detector.js +462 -0
- package/dist/hooks/incident-pipeline.js +426 -0
- package/dist/hooks/post-edit-context.js +28 -1
- package/dist/hooks/post-tool-use.js +28 -1
- package/dist/hooks/pre-compact.js +28 -1
- package/dist/hooks/pre-delete-check.js +28 -1
- package/dist/hooks/quality-event.js +28 -1
- package/dist/hooks/rule-enforcement-pipeline.js +440 -0
- package/dist/hooks/session-end.js +28 -1
- package/dist/hooks/session-start.js +28 -1
- package/dist/hooks/user-prompt.js +28 -1
- package/package.json +2 -2
- package/src/config.ts +31 -0
- package/src/hooks/auto-learning-pipeline.ts +195 -0
- package/src/hooks/fix-detector.ts +186 -0
- package/src/hooks/incident-pipeline.ts +148 -0
- package/src/hooks/rule-enforcement-pipeline.ts +158 -0
- package/src/mcp-bridge-tools.ts +459 -0
- package/src/tools.ts +8 -0
|
@@ -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.
|
|
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,
|
|
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;
|