@massu/core 0.9.2 → 1.0.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/dist/cli.js +10519 -1661
- package/dist/hooks/auto-learning-pipeline.js +99 -19
- package/dist/hooks/classify-failure.js +99 -19
- package/dist/hooks/cost-tracker.js +97 -11
- package/dist/hooks/fix-detector.js +99 -19
- package/dist/hooks/incident-pipeline.js +97 -11
- package/dist/hooks/post-edit-context.js +97 -11
- package/dist/hooks/post-tool-use.js +101 -20
- package/dist/hooks/pre-compact.js +97 -11
- package/dist/hooks/pre-delete-check.js +97 -11
- package/dist/hooks/quality-event.js +97 -11
- package/dist/hooks/rule-enforcement-pipeline.js +97 -11
- package/dist/hooks/session-end.js +97 -11
- package/dist/hooks/session-start.js +98 -12
- package/dist/hooks/user-prompt.js +98 -43
- package/package.json +13 -3
- package/reference/hook-execution-order.md +17 -25
- package/src/cli.ts +2 -1
- package/src/commands/doctor.ts +1 -29
- package/src/commands/init.ts +752 -216
- package/src/config.ts +168 -12
- package/src/detect/domain-inferrer.ts +142 -0
- package/src/detect/drift.ts +199 -0
- package/src/detect/framework-detector.ts +281 -0
- package/src/detect/index.ts +174 -0
- package/src/detect/migrate.ts +278 -0
- package/src/detect/monorepo-detector.ts +347 -0
- package/src/detect/package-detector.ts +728 -0
- package/src/detect/source-dir-detector.ts +264 -0
- package/src/detect/vr-command-map.ts +167 -0
- package/src/hooks/auto-learning-pipeline.ts +2 -2
- package/src/hooks/classify-failure.ts +2 -2
- package/src/hooks/fix-detector.ts +2 -2
- package/src/hooks/session-start.ts +1 -1
- package/src/hooks/user-prompt.ts +1 -21
- package/src/knowledge-indexer.ts +1 -1
- package/src/license.ts +1 -2
- package/src/memory-db.ts +0 -5
- package/src/memory-file-ingest.ts +6 -13
- package/src/tools.ts +0 -8
- package/templates/multi-runtime/massu.config.yaml +80 -0
- package/templates/python-django/massu.config.yaml +51 -0
- package/templates/python-fastapi/massu.config.yaml +50 -0
- package/templates/rust-actix/massu.config.yaml +38 -0
- package/templates/swift-ios/massu.config.yaml +37 -0
- package/templates/ts-nestjs/massu.config.yaml +43 -0
- package/templates/ts-nextjs/massu.config.yaml +43 -0
- package/README.md +0 -40
- package/src/claude-md-templates.ts +0 -342
- package/src/mcp-bridge-tools.ts +0 -458
|
@@ -21,7 +21,8 @@ var DomainConfigSchema = z.object({
|
|
|
21
21
|
});
|
|
22
22
|
var PatternRuleConfigSchema = z.object({
|
|
23
23
|
pattern: z.string().default("**"),
|
|
24
|
-
rules: z.array(z.string()).default([])
|
|
24
|
+
rules: z.array(z.string()).default([]),
|
|
25
|
+
language: z.string().optional()
|
|
25
26
|
});
|
|
26
27
|
var CostModelSchema = z.object({
|
|
27
28
|
input_per_million: z.number(),
|
|
@@ -233,17 +234,59 @@ var PathsConfigSchema = z.object({
|
|
|
233
234
|
components: z.string().optional(),
|
|
234
235
|
hooks: z.string().optional()
|
|
235
236
|
});
|
|
237
|
+
var LanguageFrameworkEntrySchema = z.object({
|
|
238
|
+
framework: z.string().optional(),
|
|
239
|
+
test_framework: z.string().optional(),
|
|
240
|
+
test: z.string().optional(),
|
|
241
|
+
runtime: z.string().optional(),
|
|
242
|
+
orm: z.string().optional(),
|
|
243
|
+
router: z.string().optional(),
|
|
244
|
+
ui: z.string().optional()
|
|
245
|
+
}).passthrough();
|
|
246
|
+
var FrameworkConfigSchema = z.object({
|
|
247
|
+
type: z.string().default("typescript"),
|
|
248
|
+
primary: z.string().optional(),
|
|
249
|
+
router: z.string().default("none"),
|
|
250
|
+
orm: z.string().default("none"),
|
|
251
|
+
ui: z.string().default("none"),
|
|
252
|
+
languages: z.record(z.string(), LanguageFrameworkEntrySchema).optional()
|
|
253
|
+
}).passthrough();
|
|
254
|
+
var VerificationEntrySchema = z.object({
|
|
255
|
+
type: z.string().optional(),
|
|
256
|
+
test: z.string().optional(),
|
|
257
|
+
syntax: z.string().optional(),
|
|
258
|
+
lint: z.string().optional(),
|
|
259
|
+
build: z.string().optional()
|
|
260
|
+
}).passthrough();
|
|
261
|
+
var VerificationConfigSchema = z.record(z.string(), VerificationEntrySchema).optional();
|
|
262
|
+
var CanonicalPathsSchema = z.record(z.string(), z.string()).optional();
|
|
263
|
+
var VerificationTypesSchema = z.record(z.string(), z.string()).optional();
|
|
264
|
+
var DetectionRuleEntrySchema = z.object({
|
|
265
|
+
signals: z.array(z.string()).default([]),
|
|
266
|
+
priority: z.number().optional()
|
|
267
|
+
}).passthrough();
|
|
268
|
+
var DetectionConfigSchema = z.object({
|
|
269
|
+
rules: z.record(
|
|
270
|
+
z.string(),
|
|
271
|
+
// language
|
|
272
|
+
z.record(z.string(), DetectionRuleEntrySchema)
|
|
273
|
+
// framework -> rule entry
|
|
274
|
+
).optional(),
|
|
275
|
+
signal_weights: z.record(z.string(), z.number()).optional(),
|
|
276
|
+
disable_builtin: z.boolean().optional()
|
|
277
|
+
}).passthrough().optional();
|
|
236
278
|
var RawConfigSchema = z.object({
|
|
279
|
+
schema_version: z.union([z.literal(1), z.literal(2)]).default(1),
|
|
237
280
|
project: z.object({
|
|
238
281
|
name: z.string().default("my-project"),
|
|
239
282
|
root: z.string().default("auto")
|
|
240
283
|
}).default({ name: "my-project", root: "auto" }),
|
|
241
|
-
framework:
|
|
242
|
-
type:
|
|
243
|
-
router:
|
|
244
|
-
orm:
|
|
245
|
-
ui:
|
|
246
|
-
})
|
|
284
|
+
framework: FrameworkConfigSchema.default({
|
|
285
|
+
type: "typescript",
|
|
286
|
+
router: "none",
|
|
287
|
+
orm: "none",
|
|
288
|
+
ui: "none"
|
|
289
|
+
}),
|
|
247
290
|
paths: PathsConfigSchema.default({ source: "src", aliases: { "@": "src" } }),
|
|
248
291
|
toolPrefix: z.string().default("massu"),
|
|
249
292
|
dbAccessPattern: z.string().optional(),
|
|
@@ -258,8 +301,13 @@ var RawConfigSchema = z.object({
|
|
|
258
301
|
regression: RegressionConfigSchema,
|
|
259
302
|
cloud: CloudConfigSchema,
|
|
260
303
|
conventions: ConventionsConfigSchema,
|
|
304
|
+
autoLearning: AutoLearningConfigSchema,
|
|
261
305
|
python: PythonConfigSchema,
|
|
262
|
-
|
|
306
|
+
// P2-004 / P2-005 / P2-006 / P2-008: v2 extensions (all optional)
|
|
307
|
+
verification: VerificationConfigSchema,
|
|
308
|
+
canonical_paths: CanonicalPathsSchema,
|
|
309
|
+
verification_types: VerificationTypesSchema,
|
|
310
|
+
detection: DetectionConfigSchema
|
|
263
311
|
}).passthrough();
|
|
264
312
|
var _config = null;
|
|
265
313
|
var _projectRoot = null;
|
|
@@ -303,14 +351,47 @@ function getConfig() {
|
|
|
303
351
|
const content = readFileSync(configPath, "utf-8");
|
|
304
352
|
rawYaml = parseYaml(content) ?? {};
|
|
305
353
|
}
|
|
306
|
-
const
|
|
354
|
+
const result = RawConfigSchema.safeParse(rawYaml);
|
|
355
|
+
if (!result.success) {
|
|
356
|
+
const issues = result.error.issues.map((i) => {
|
|
357
|
+
const path = i.path.length > 0 ? i.path.join(".") : "(root)";
|
|
358
|
+
const received = "received" in i && i.received !== void 0 ? ` (received ${JSON.stringify(i.received)})` : "";
|
|
359
|
+
return ` - ${path}: ${i.message}${received}`;
|
|
360
|
+
}).join("\n");
|
|
361
|
+
throw new Error(
|
|
362
|
+
`Invalid massu.config.yaml at ${configPath}:
|
|
363
|
+
${issues}
|
|
364
|
+
Hint: run \`massu config refresh\` to regenerate a valid config or fix the listed fields manually.`
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
const parsed = result.data;
|
|
307
368
|
const projectRoot = parsed.project.root === "auto" || !parsed.project.root ? root : resolve(root, parsed.project.root);
|
|
369
|
+
const fw = parsed.framework;
|
|
370
|
+
let router = fw.router;
|
|
371
|
+
let orm = fw.orm;
|
|
372
|
+
let ui = fw.ui;
|
|
373
|
+
if (fw.type === "multi" && fw.primary && fw.languages) {
|
|
374
|
+
const primaryEntry = fw.languages[fw.primary];
|
|
375
|
+
if (primaryEntry) {
|
|
376
|
+
if (router === "none" && primaryEntry.router) router = primaryEntry.router;
|
|
377
|
+
if (orm === "none" && primaryEntry.orm) orm = primaryEntry.orm;
|
|
378
|
+
if (ui === "none" && primaryEntry.ui) ui = primaryEntry.ui;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
308
381
|
_config = {
|
|
382
|
+
schema_version: parsed.schema_version,
|
|
309
383
|
project: {
|
|
310
384
|
name: parsed.project.name,
|
|
311
385
|
root: projectRoot
|
|
312
386
|
},
|
|
313
|
-
framework:
|
|
387
|
+
framework: {
|
|
388
|
+
type: fw.type,
|
|
389
|
+
router,
|
|
390
|
+
orm,
|
|
391
|
+
ui,
|
|
392
|
+
primary: fw.primary,
|
|
393
|
+
languages: fw.languages
|
|
394
|
+
},
|
|
314
395
|
paths: parsed.paths,
|
|
315
396
|
toolPrefix: parsed.toolPrefix,
|
|
316
397
|
dbAccessPattern: parsed.dbAccessPattern,
|
|
@@ -325,7 +406,12 @@ function getConfig() {
|
|
|
325
406
|
regression: parsed.regression,
|
|
326
407
|
cloud: parsed.cloud,
|
|
327
408
|
conventions: parsed.conventions,
|
|
328
|
-
|
|
409
|
+
autoLearning: parsed.autoLearning,
|
|
410
|
+
python: parsed.python,
|
|
411
|
+
verification: parsed.verification,
|
|
412
|
+
canonical_paths: parsed.canonical_paths,
|
|
413
|
+
verification_types: parsed.verification_types,
|
|
414
|
+
detection: parsed.detection
|
|
329
415
|
};
|
|
330
416
|
if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
|
|
331
417
|
_config.cloud = {
|
|
@@ -986,7 +1072,7 @@ async function main() {
|
|
|
986
1072
|
process.stdout.write(
|
|
987
1073
|
`=== MASSU AI: Active ===
|
|
988
1074
|
Session memory, code intelligence, and governance are now active.
|
|
989
|
-
|
|
1075
|
+
11 hooks monitoring this session. Type "${getConfig().toolPrefix ?? "massu"}_sync" to index your codebase.
|
|
990
1076
|
=== END MASSU ===
|
|
991
1077
|
|
|
992
1078
|
`
|
|
@@ -21,7 +21,8 @@ var DomainConfigSchema = z.object({
|
|
|
21
21
|
});
|
|
22
22
|
var PatternRuleConfigSchema = z.object({
|
|
23
23
|
pattern: z.string().default("**"),
|
|
24
|
-
rules: z.array(z.string()).default([])
|
|
24
|
+
rules: z.array(z.string()).default([]),
|
|
25
|
+
language: z.string().optional()
|
|
25
26
|
});
|
|
26
27
|
var CostModelSchema = z.object({
|
|
27
28
|
input_per_million: z.number(),
|
|
@@ -233,17 +234,59 @@ var PathsConfigSchema = z.object({
|
|
|
233
234
|
components: z.string().optional(),
|
|
234
235
|
hooks: z.string().optional()
|
|
235
236
|
});
|
|
237
|
+
var LanguageFrameworkEntrySchema = z.object({
|
|
238
|
+
framework: z.string().optional(),
|
|
239
|
+
test_framework: z.string().optional(),
|
|
240
|
+
test: z.string().optional(),
|
|
241
|
+
runtime: z.string().optional(),
|
|
242
|
+
orm: z.string().optional(),
|
|
243
|
+
router: z.string().optional(),
|
|
244
|
+
ui: z.string().optional()
|
|
245
|
+
}).passthrough();
|
|
246
|
+
var FrameworkConfigSchema = z.object({
|
|
247
|
+
type: z.string().default("typescript"),
|
|
248
|
+
primary: z.string().optional(),
|
|
249
|
+
router: z.string().default("none"),
|
|
250
|
+
orm: z.string().default("none"),
|
|
251
|
+
ui: z.string().default("none"),
|
|
252
|
+
languages: z.record(z.string(), LanguageFrameworkEntrySchema).optional()
|
|
253
|
+
}).passthrough();
|
|
254
|
+
var VerificationEntrySchema = z.object({
|
|
255
|
+
type: z.string().optional(),
|
|
256
|
+
test: z.string().optional(),
|
|
257
|
+
syntax: z.string().optional(),
|
|
258
|
+
lint: z.string().optional(),
|
|
259
|
+
build: z.string().optional()
|
|
260
|
+
}).passthrough();
|
|
261
|
+
var VerificationConfigSchema = z.record(z.string(), VerificationEntrySchema).optional();
|
|
262
|
+
var CanonicalPathsSchema = z.record(z.string(), z.string()).optional();
|
|
263
|
+
var VerificationTypesSchema = z.record(z.string(), z.string()).optional();
|
|
264
|
+
var DetectionRuleEntrySchema = z.object({
|
|
265
|
+
signals: z.array(z.string()).default([]),
|
|
266
|
+
priority: z.number().optional()
|
|
267
|
+
}).passthrough();
|
|
268
|
+
var DetectionConfigSchema = z.object({
|
|
269
|
+
rules: z.record(
|
|
270
|
+
z.string(),
|
|
271
|
+
// language
|
|
272
|
+
z.record(z.string(), DetectionRuleEntrySchema)
|
|
273
|
+
// framework -> rule entry
|
|
274
|
+
).optional(),
|
|
275
|
+
signal_weights: z.record(z.string(), z.number()).optional(),
|
|
276
|
+
disable_builtin: z.boolean().optional()
|
|
277
|
+
}).passthrough().optional();
|
|
236
278
|
var RawConfigSchema = z.object({
|
|
279
|
+
schema_version: z.union([z.literal(1), z.literal(2)]).default(1),
|
|
237
280
|
project: z.object({
|
|
238
281
|
name: z.string().default("my-project"),
|
|
239
282
|
root: z.string().default("auto")
|
|
240
283
|
}).default({ name: "my-project", root: "auto" }),
|
|
241
|
-
framework:
|
|
242
|
-
type:
|
|
243
|
-
router:
|
|
244
|
-
orm:
|
|
245
|
-
ui:
|
|
246
|
-
})
|
|
284
|
+
framework: FrameworkConfigSchema.default({
|
|
285
|
+
type: "typescript",
|
|
286
|
+
router: "none",
|
|
287
|
+
orm: "none",
|
|
288
|
+
ui: "none"
|
|
289
|
+
}),
|
|
247
290
|
paths: PathsConfigSchema.default({ source: "src", aliases: { "@": "src" } }),
|
|
248
291
|
toolPrefix: z.string().default("massu"),
|
|
249
292
|
dbAccessPattern: z.string().optional(),
|
|
@@ -258,8 +301,13 @@ var RawConfigSchema = z.object({
|
|
|
258
301
|
regression: RegressionConfigSchema,
|
|
259
302
|
cloud: CloudConfigSchema,
|
|
260
303
|
conventions: ConventionsConfigSchema,
|
|
304
|
+
autoLearning: AutoLearningConfigSchema,
|
|
261
305
|
python: PythonConfigSchema,
|
|
262
|
-
|
|
306
|
+
// P2-004 / P2-005 / P2-006 / P2-008: v2 extensions (all optional)
|
|
307
|
+
verification: VerificationConfigSchema,
|
|
308
|
+
canonical_paths: CanonicalPathsSchema,
|
|
309
|
+
verification_types: VerificationTypesSchema,
|
|
310
|
+
detection: DetectionConfigSchema
|
|
263
311
|
}).passthrough();
|
|
264
312
|
var _config = null;
|
|
265
313
|
var _projectRoot = null;
|
|
@@ -303,14 +351,47 @@ function getConfig() {
|
|
|
303
351
|
const content = readFileSync(configPath, "utf-8");
|
|
304
352
|
rawYaml = parseYaml(content) ?? {};
|
|
305
353
|
}
|
|
306
|
-
const
|
|
354
|
+
const result = RawConfigSchema.safeParse(rawYaml);
|
|
355
|
+
if (!result.success) {
|
|
356
|
+
const issues = result.error.issues.map((i) => {
|
|
357
|
+
const path = i.path.length > 0 ? i.path.join(".") : "(root)";
|
|
358
|
+
const received = "received" in i && i.received !== void 0 ? ` (received ${JSON.stringify(i.received)})` : "";
|
|
359
|
+
return ` - ${path}: ${i.message}${received}`;
|
|
360
|
+
}).join("\n");
|
|
361
|
+
throw new Error(
|
|
362
|
+
`Invalid massu.config.yaml at ${configPath}:
|
|
363
|
+
${issues}
|
|
364
|
+
Hint: run \`massu config refresh\` to regenerate a valid config or fix the listed fields manually.`
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
const parsed = result.data;
|
|
307
368
|
const projectRoot = parsed.project.root === "auto" || !parsed.project.root ? root : resolve(root, parsed.project.root);
|
|
369
|
+
const fw = parsed.framework;
|
|
370
|
+
let router = fw.router;
|
|
371
|
+
let orm = fw.orm;
|
|
372
|
+
let ui = fw.ui;
|
|
373
|
+
if (fw.type === "multi" && fw.primary && fw.languages) {
|
|
374
|
+
const primaryEntry = fw.languages[fw.primary];
|
|
375
|
+
if (primaryEntry) {
|
|
376
|
+
if (router === "none" && primaryEntry.router) router = primaryEntry.router;
|
|
377
|
+
if (orm === "none" && primaryEntry.orm) orm = primaryEntry.orm;
|
|
378
|
+
if (ui === "none" && primaryEntry.ui) ui = primaryEntry.ui;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
308
381
|
_config = {
|
|
382
|
+
schema_version: parsed.schema_version,
|
|
309
383
|
project: {
|
|
310
384
|
name: parsed.project.name,
|
|
311
385
|
root: projectRoot
|
|
312
386
|
},
|
|
313
|
-
framework:
|
|
387
|
+
framework: {
|
|
388
|
+
type: fw.type,
|
|
389
|
+
router,
|
|
390
|
+
orm,
|
|
391
|
+
ui,
|
|
392
|
+
primary: fw.primary,
|
|
393
|
+
languages: fw.languages
|
|
394
|
+
},
|
|
314
395
|
paths: parsed.paths,
|
|
315
396
|
toolPrefix: parsed.toolPrefix,
|
|
316
397
|
dbAccessPattern: parsed.dbAccessPattern,
|
|
@@ -325,7 +406,12 @@ function getConfig() {
|
|
|
325
406
|
regression: parsed.regression,
|
|
326
407
|
cloud: parsed.cloud,
|
|
327
408
|
conventions: parsed.conventions,
|
|
328
|
-
|
|
409
|
+
autoLearning: parsed.autoLearning,
|
|
410
|
+
python: parsed.python,
|
|
411
|
+
verification: parsed.verification,
|
|
412
|
+
canonical_paths: parsed.canonical_paths,
|
|
413
|
+
verification_types: parsed.verification_types,
|
|
414
|
+
detection: parsed.detection
|
|
329
415
|
};
|
|
330
416
|
if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
|
|
331
417
|
_config.cloud = {
|
|
@@ -952,9 +1038,7 @@ function linkSessionToTask(db, sessionId, taskId) {
|
|
|
952
1038
|
}
|
|
953
1039
|
|
|
954
1040
|
// src/hooks/user-prompt.ts
|
|
955
|
-
import { existsSync as existsSync3
|
|
956
|
-
import { tmpdir } from "os";
|
|
957
|
-
import { join } from "path";
|
|
1041
|
+
import { existsSync as existsSync3 } from "fs";
|
|
958
1042
|
async function main() {
|
|
959
1043
|
try {
|
|
960
1044
|
const input = await readStdin();
|
|
@@ -1027,35 +1111,6 @@ async function main() {
|
|
|
1027
1111
|
}
|
|
1028
1112
|
} catch (_memoryNagErr) {
|
|
1029
1113
|
}
|
|
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
|
-
}
|
|
1059
1114
|
} finally {
|
|
1060
1115
|
db.close();
|
|
1061
1116
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@massu/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "AI Engineering Governance MCP Server - Session memory, knowledge system, feature registry, code intelligence, rule enforcement,
|
|
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",
|
|
6
6
|
"main": "src/server.ts",
|
|
7
7
|
"bin": {
|
|
8
8
|
"massu": "./dist/cli.js"
|
|
@@ -16,7 +16,10 @@
|
|
|
16
16
|
"prepublishOnly": "bash ../../scripts/prepublish-check.sh && npm run build"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
+
"@clack/prompts": "^0.9.1",
|
|
19
20
|
"better-sqlite3": "^12.6.2",
|
|
21
|
+
"fast-glob": "^3.3.0",
|
|
22
|
+
"smol-toml": "^1.3.0",
|
|
20
23
|
"yaml": "^2.4.0",
|
|
21
24
|
"zod": "^3.23.0"
|
|
22
25
|
},
|
|
@@ -29,13 +32,20 @@
|
|
|
29
32
|
},
|
|
30
33
|
"files": [
|
|
31
34
|
"src/**/*",
|
|
32
|
-
"!src
|
|
35
|
+
"!src/**/__tests__/**",
|
|
36
|
+
"!src/**/*.test.ts",
|
|
37
|
+
"!src/**/*.spec.ts",
|
|
38
|
+
"!**/.build/**",
|
|
39
|
+
"!**/.swiftpm/**",
|
|
40
|
+
"!**/node_modules/**",
|
|
41
|
+
"!**/__pycache__/**",
|
|
33
42
|
"dist/**/*",
|
|
34
43
|
"commands/**/*",
|
|
35
44
|
"agents/**/*",
|
|
36
45
|
"patterns/**/*",
|
|
37
46
|
"protocols/**/*",
|
|
38
47
|
"reference/**/*",
|
|
48
|
+
"templates/**/*",
|
|
39
49
|
"LICENSE"
|
|
40
50
|
],
|
|
41
51
|
"keywords": [
|
|
@@ -52,9 +52,9 @@ Security blocking -> advisory warnings -> matcher-specific -> observability.
|
|
|
52
52
|
|
|
53
53
|
---
|
|
54
54
|
|
|
55
|
-
## PostToolUse (
|
|
55
|
+
## PostToolUse (11 hooks)
|
|
56
56
|
|
|
57
|
-
Security scan -> immediate feedback -> context tracking ->
|
|
57
|
+
Security scan -> immediate feedback -> context tracking -> incident capture -> memory sync -> observability.
|
|
58
58
|
|
|
59
59
|
| position: 1 | CI monitor | standard | Bash(git push) -- immediate push feedback |
|
|
60
60
|
|---|---|---|---|
|
|
@@ -62,23 +62,17 @@ Security scan -> immediate feedback -> context tracking -> fix detection -> inci
|
|
|
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 | `auto-ingest
|
|
67
|
-
| position: 8 | `
|
|
68
|
-
| position: 9 | `
|
|
69
|
-
| position: 10 | `
|
|
70
|
-
| position: 11 | `
|
|
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 |
|
|
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 |
|
|
74
71
|
|
|
75
72
|
**Dependencies**:
|
|
76
73
|
- `output-secret-filter.sh` MUST run before any feedback hooks -- security first
|
|
77
74
|
- `pattern-feedback.sh` before `post-tool-use.js` -- immediate feedback before tracking
|
|
78
|
-
- `
|
|
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
|
|
75
|
+
- `memory-auto-ingest.sh` runs after incident capture -- memory sync is data-writing, before validation
|
|
82
76
|
- `compaction-advisor.sh` MUST be last -- widest matcher, just counts tool calls
|
|
83
77
|
|
|
84
78
|
---
|
|
@@ -111,23 +105,21 @@ Quick state capture -> full DB snapshot.
|
|
|
111
105
|
|
|
112
106
|
---
|
|
113
107
|
|
|
114
|
-
## Stop (
|
|
108
|
+
## Stop (7 hooks)
|
|
115
109
|
|
|
116
|
-
Session summary ->
|
|
110
|
+
Session summary -> warnings -> memory extraction -> review -> validation.
|
|
117
111
|
|
|
118
112
|
| position: 1 | `session-end.js` | standard | Write session summary to memory DB |
|
|
119
113
|
|---|---|---|---|
|
|
120
|
-
| position: 2 |
|
|
121
|
-
| position: 3 |
|
|
122
|
-
| position: 4 | `
|
|
123
|
-
| position: 5 | `
|
|
124
|
-
| position: 6 | `
|
|
125
|
-
| position: 7 | `
|
|
126
|
-
| position: 8 | `pattern-extractor.sh` | advisory | Extract new patterns from session |
|
|
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 |
|
|
127
120
|
|
|
128
121
|
**Dependencies**:
|
|
129
122
|
- `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
|
|
131
123
|
- `memory-auto-extract.sh` MUST come after `session-end.js` -- depends on DB observations
|
|
132
124
|
- `surface-review-findings.sh` MUST come after `auto-review-on-stop.sh` -- displays its output
|
|
133
125
|
- `pattern-extractor.sh` runs last -- advisory tier (skipped in minimal/standard profiles)
|
package/src/cli.ts
CHANGED
|
@@ -89,7 +89,8 @@ Options:
|
|
|
89
89
|
--version, -v Show version
|
|
90
90
|
|
|
91
91
|
Getting started:
|
|
92
|
-
npx massu init
|
|
92
|
+
npx massu init # Full setup in one command
|
|
93
|
+
npx massu init --help # Show all init options (--ci, --force, --template)
|
|
93
94
|
|
|
94
95
|
Documentation: https://massu.ai/docs
|
|
95
96
|
`);
|
package/src/commands/doctor.ts
CHANGED
|
@@ -8,14 +8,13 @@
|
|
|
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 11 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
|
|
15
15
|
* 8. better-sqlite3 native module loads
|
|
16
16
|
* 9. Node.js version >= 18
|
|
17
17
|
* 10. Git repository detected
|
|
18
|
-
* 11. CLAUDE.md exists with content
|
|
19
18
|
*/
|
|
20
19
|
|
|
21
20
|
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
@@ -374,32 +373,6 @@ function checkPythonHealth(projectRoot: string): CheckResult | null {
|
|
|
374
373
|
};
|
|
375
374
|
}
|
|
376
375
|
|
|
377
|
-
function checkClaudeMd(projectRoot: string): CheckResult {
|
|
378
|
-
const claudeMdPath = resolve(projectRoot, 'CLAUDE.md');
|
|
379
|
-
if (!existsSync(claudeMdPath)) {
|
|
380
|
-
return {
|
|
381
|
-
name: 'CLAUDE.md',
|
|
382
|
-
status: 'warn',
|
|
383
|
-
detail: 'CLAUDE.md not found. Run: npx massu init (or create manually)',
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
const content = readFileSync(claudeMdPath, 'utf-8');
|
|
388
|
-
if (content.trim().length < 50) {
|
|
389
|
-
return {
|
|
390
|
-
name: 'CLAUDE.md',
|
|
391
|
-
status: 'warn',
|
|
392
|
-
detail: 'CLAUDE.md exists but appears empty or minimal',
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
return {
|
|
397
|
-
name: 'CLAUDE.md',
|
|
398
|
-
status: 'pass',
|
|
399
|
-
detail: 'CLAUDE.md found and has content',
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
|
|
403
376
|
// ============================================================
|
|
404
377
|
// Main Doctor Flow
|
|
405
378
|
// ============================================================
|
|
@@ -424,7 +397,6 @@ export async function runDoctor(): Promise<void> {
|
|
|
424
397
|
checkNodeVersion(),
|
|
425
398
|
await checkGitRepo(projectRoot),
|
|
426
399
|
await checkLicenseStatus(),
|
|
427
|
-
checkClaudeMd(projectRoot),
|
|
428
400
|
];
|
|
429
401
|
|
|
430
402
|
// Add Python health check if configured
|