@massu/core 1.10.7 → 1.12.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.
Files changed (177) hide show
  1. package/CHANGELOG.md +1303 -0
  2. package/dist/adapter.js +6 -4
  3. package/dist/cli.js +559 -400
  4. package/dist/detect/adapters/.bundle-shasums.json +4 -7
  5. package/dist/detect/adapters/rails.js +6 -4
  6. package/dist/detect/adapters/spring.js +6 -4
  7. package/dist/detect/adapters/tree-sitter-loader.js +6 -4
  8. package/dist/hooks/auto-learning-pipeline.js +24 -1
  9. package/dist/hooks/classify-failure.js +32 -3
  10. package/dist/hooks/cost-tracker.js +24 -0
  11. package/dist/hooks/fix-detector.js +97 -1
  12. package/dist/hooks/incident-pipeline.js +30 -1
  13. package/dist/hooks/intent-suggester.js +6 -3
  14. package/dist/hooks/post-edit-context.js +24 -1
  15. package/dist/hooks/post-tool-use.js +66 -18
  16. package/dist/hooks/pre-compact.js +27 -2
  17. package/dist/hooks/pre-delete-check.js +48 -22
  18. package/dist/hooks/pre-tool-use-gate.js +962 -0
  19. package/dist/hooks/quality-event.js +24 -0
  20. package/dist/hooks/rule-enforcement-pipeline.js +24 -1
  21. package/dist/hooks/security-gate.js +29 -16
  22. package/dist/hooks/session-end.js +24 -0
  23. package/dist/hooks/session-start.js +8710 -8460
  24. package/dist/hooks/user-prompt.js +24 -0
  25. package/package.json +15 -18
  26. package/commands/README.md +0 -134
  27. package/src/adapter.ts +0 -121
  28. package/src/adr-generator.ts +0 -292
  29. package/src/analytics.ts +0 -373
  30. package/src/audit-trail.ts +0 -450
  31. package/src/backfill-sessions.ts +0 -182
  32. package/src/changelog-generator.ts +0 -178
  33. package/src/cli.ts +0 -246
  34. package/src/cloud-sync.ts +0 -234
  35. package/src/commands/adapters.ts +0 -824
  36. package/src/commands/changelog.ts +0 -165
  37. package/src/commands/config-check-drift.ts +0 -133
  38. package/src/commands/config-refresh.ts +0 -396
  39. package/src/commands/config-upgrade.ts +0 -127
  40. package/src/commands/doctor.ts +0 -485
  41. package/src/commands/hook-runner.ts +0 -145
  42. package/src/commands/init.ts +0 -1823
  43. package/src/commands/install-commands.ts +0 -692
  44. package/src/commands/install-hooks.ts +0 -27
  45. package/src/commands/permissions.ts +0 -150
  46. package/src/commands/refresh-log.ts +0 -37
  47. package/src/commands/show-template.ts +0 -65
  48. package/src/commands/template-engine.ts +0 -301
  49. package/src/commands/watch.ts +0 -430
  50. package/src/config.ts +0 -765
  51. package/src/cost-tracker.ts +0 -355
  52. package/src/db.ts +0 -370
  53. package/src/dependency-scorer.ts +0 -337
  54. package/src/detect/adapters/aspnet.ts +0 -4
  55. package/src/detect/adapters/discover.ts +0 -469
  56. package/src/detect/adapters/file-sampler.ts +0 -225
  57. package/src/detect/adapters/go-chi.ts +0 -4
  58. package/src/detect/adapters/index.ts +0 -51
  59. package/src/detect/adapters/nextjs-trpc.ts +0 -166
  60. package/src/detect/adapters/parse-guard.ts +0 -133
  61. package/src/detect/adapters/phoenix.ts +0 -4
  62. package/src/detect/adapters/python-django.ts +0 -208
  63. package/src/detect/adapters/python-fastapi.ts +0 -223
  64. package/src/detect/adapters/python-flask.ts +0 -235
  65. package/src/detect/adapters/query-helpers.ts +0 -170
  66. package/src/detect/adapters/rails.ts +0 -4
  67. package/src/detect/adapters/runner.ts +0 -284
  68. package/src/detect/adapters/spring.ts +0 -4
  69. package/src/detect/adapters/swift-swiftui.ts +0 -171
  70. package/src/detect/adapters/tree-sitter-loader.ts +0 -517
  71. package/src/detect/adapters/types.ts +0 -191
  72. package/src/detect/codebase-introspector.ts +0 -210
  73. package/src/detect/domain-inferrer.ts +0 -197
  74. package/src/detect/drift.ts +0 -199
  75. package/src/detect/framework-detector.ts +0 -307
  76. package/src/detect/index.ts +0 -209
  77. package/src/detect/manifest-registry.ts +0 -261
  78. package/src/detect/migrate.ts +0 -362
  79. package/src/detect/monorepo-detector.ts +0 -348
  80. package/src/detect/package-detector.ts +0 -828
  81. package/src/detect/passthrough.ts +0 -108
  82. package/src/detect/regex-fallback.ts +0 -449
  83. package/src/detect/source-dir-detector.ts +0 -297
  84. package/src/detect/vr-command-map.ts +0 -167
  85. package/src/docs-map.json +0 -100
  86. package/src/docs-tools.ts +0 -519
  87. package/src/domains.ts +0 -182
  88. package/src/hooks/auto-learning-pipeline.ts +0 -228
  89. package/src/hooks/classify-failure.ts +0 -259
  90. package/src/hooks/cost-tracker.ts +0 -66
  91. package/src/hooks/fix-detector.ts +0 -186
  92. package/src/hooks/incident-pipeline.ts +0 -190
  93. package/src/hooks/intent-suggester.ts +0 -131
  94. package/src/hooks/post-edit-context.ts +0 -91
  95. package/src/hooks/post-tool-use.ts +0 -413
  96. package/src/hooks/pre-compact.ts +0 -168
  97. package/src/hooks/pre-delete-check.ts +0 -243
  98. package/src/hooks/quality-event.ts +0 -127
  99. package/src/hooks/rule-enforcement-pipeline.ts +0 -159
  100. package/src/hooks/security-gate.ts +0 -153
  101. package/src/hooks/session-end.ts +0 -467
  102. package/src/hooks/session-start.ts +0 -436
  103. package/src/hooks/user-prompt.ts +0 -157
  104. package/src/import-resolver.ts +0 -226
  105. package/src/knowledge-db.ts +0 -176
  106. package/src/knowledge-indexer.ts +0 -708
  107. package/src/knowledge-tools.ts +0 -1414
  108. package/src/lib/fileLock.ts +0 -203
  109. package/src/lib/gitToplevel.ts +0 -22
  110. package/src/lib/hook-registry.ts +0 -43
  111. package/src/lib/installLock.ts +0 -66
  112. package/src/lib/memory-path.ts +0 -49
  113. package/src/lib/pidLiveness.ts +0 -67
  114. package/src/lib/settings-local.ts +0 -110
  115. package/src/lib/sql-table-names.ts +0 -71
  116. package/src/license.ts +0 -547
  117. package/src/lsp/auto-detect.ts +0 -98
  118. package/src/lsp/client.ts +0 -776
  119. package/src/lsp/enrich.ts +0 -127
  120. package/src/lsp/types.ts +0 -221
  121. package/src/memory-db.ts +0 -1614
  122. package/src/memory-file-ingest.ts +0 -128
  123. package/src/memory-tools.ts +0 -424
  124. package/src/middleware-tree.ts +0 -71
  125. package/src/observability-tools.ts +0 -343
  126. package/src/observation-extractor.ts +0 -418
  127. package/src/page-deps.ts +0 -285
  128. package/src/permissions.ts +0 -422
  129. package/src/prompt-analyzer.ts +0 -332
  130. package/src/python/coupling-detector.ts +0 -125
  131. package/src/python/domain-enforcer.ts +0 -84
  132. package/src/python/impact-analyzer.ts +0 -96
  133. package/src/python/import-parser.ts +0 -244
  134. package/src/python/import-resolver.ts +0 -136
  135. package/src/python/migration-indexer.ts +0 -116
  136. package/src/python/migration-parser.ts +0 -332
  137. package/src/python/model-indexer.ts +0 -71
  138. package/src/python/model-parser.ts +0 -279
  139. package/src/python/route-indexer.ts +0 -59
  140. package/src/python/route-parser.ts +0 -317
  141. package/src/python-tools.ts +0 -630
  142. package/src/regression-detector.ts +0 -319
  143. package/src/rules.ts +0 -57
  144. package/src/schema-mapper.ts +0 -232
  145. package/src/security/adapter-origin.ts +0 -130
  146. package/src/security/adapter-verifier.ts +0 -319
  147. package/src/security/atomic-write.ts +0 -164
  148. package/src/security/fetcher.ts +0 -200
  149. package/src/security/install-tracking.ts +0 -319
  150. package/src/security/license-pubkey.generated.ts +0 -30
  151. package/src/security/license-response-verifier.ts +0 -149
  152. package/src/security/local-fingerprint.ts +0 -225
  153. package/src/security/manifest-cache.ts +0 -333
  154. package/src/security/manifest-schema.ts +0 -129
  155. package/src/security/registry-pubkey.generated.ts +0 -35
  156. package/src/security/telemetry.ts +0 -320
  157. package/src/security-scorer.ts +0 -405
  158. package/src/security-utils.ts +0 -133
  159. package/src/sentinel-db.ts +0 -580
  160. package/src/sentinel-scanner.ts +0 -406
  161. package/src/sentinel-tools.ts +0 -513
  162. package/src/sentinel-types.ts +0 -140
  163. package/src/server-dispatch.ts +0 -225
  164. package/src/server.ts +0 -107
  165. package/src/session-archiver.ts +0 -111
  166. package/src/session-state-generator.ts +0 -174
  167. package/src/team-knowledge.ts +0 -407
  168. package/src/tool-db-needs.ts +0 -232
  169. package/src/tools.ts +0 -1205
  170. package/src/transcript-parser.ts +0 -458
  171. package/src/trpc-index.ts +0 -215
  172. package/src/validate-features-runner.ts +0 -107
  173. package/src/validation-engine.ts +0 -358
  174. package/src/watch/daemon.ts +0 -385
  175. package/src/watch/lockfile-detector.ts +0 -65
  176. package/src/watch/paths.ts +0 -279
  177. package/src/watch/state.ts +0 -178
@@ -1,12 +1,9 @@
1
1
  {
2
- "@massu/core/adapter": "c6967280a498fc95b0f22a3e083a7dda509b5a3f5d249b94397c654e241dda2b",
2
+ "@massu/core/adapter": "37b40614a34247e7ca753f0fdc1db9ab646df7ce8f96684e8555fd4be032881b",
3
3
  "query-helpers": "f1372ef965450109258ad5bd300a1b3655660149e0b1b69ad30e1cc4acedcb2e",
4
4
  "parse-guard": "1bed6c2298b40c07f0bd9641d975bf00e4d656fb4f52ab42a28666390d47ec6d",
5
- "tree-sitter-loader": "6e26e2c81385ce39f6edc2d7bc6537e4f3a048693a62e191ff9928ebfdddd14e",
5
+ "tree-sitter-loader": "90da8caa618a3fe21d4b8cd6c4e62f39a5cebd4c34b54ee7bd789a2bcee014a9",
6
6
  "types": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
7
- "rails": "a58db04f6f232a859f41714229b0680db3e2960213df9dab1ef95cd9d6670ede",
8
- "phoenix": "3902e42393f4edbbe17c0058f9fc09c20b80ece1b08b56305cca6e9b972a8821",
9
- "aspnet": "a04de27b4bf31c626375c376b613e2851d420a8c94494d9a222bd01c76f505ad",
10
- "spring": "101ed8ab391b4769383dec6c1fee7429c2ee1f57efdb7d391f391d69e5dcbe1b",
11
- "go-chi": "03be4cb66102ae172ec443f154ce18f0d5280e5e32cf408112539f06dad347c9"
7
+ "rails": "c9b1a0c19960d9e78a6ad57fce3fd05bfdd3ea3f862c76dd731c36bf8ef1f34b",
8
+ "spring": "5fb2f16d3aa733693b5090f2a972aab4818c23c850d15f6ef35c8e70a9884290"
12
9
  }
@@ -258,8 +258,9 @@ function evictBeyondRetainCount(retain = getCacheRetainCount()) {
258
258
  continue;
259
259
  }
260
260
  if (stat.isSymbolicLink() || !stat.isFile()) {
261
- console.error(
262
- `[tree-sitter-loader] cache eviction skipped non-regular file: ${path} (possible symlink attack \u2014 see Phase 3.5 finding F-008).`
261
+ process.stderr.write(
262
+ `[tree-sitter-loader] cache eviction skipped non-regular file: ${path} (possible symlink attack \u2014 see Phase 3.5 finding F-008).
263
+ `
263
264
  );
264
265
  continue;
265
266
  }
@@ -374,8 +375,9 @@ async function loadGrammar(language, options = {}) {
374
375
  }
375
376
  evictBeyondRetainCount();
376
377
  } catch (e) {
377
- console.error(
378
- `[tree-sitter-loader] cache write failed for ${language}: ${e instanceof Error ? e.message : String(e)} \u2014 loading directly from memory.`
378
+ process.stderr.write(
379
+ `[tree-sitter-loader] cache write failed for ${language}: ${e instanceof Error ? e.message : String(e)} \u2014 loading directly from memory.
380
+ `
379
381
  );
380
382
  }
381
383
  const lang = await Language.load(body);
@@ -258,8 +258,9 @@ function evictBeyondRetainCount(retain = getCacheRetainCount()) {
258
258
  continue;
259
259
  }
260
260
  if (stat.isSymbolicLink() || !stat.isFile()) {
261
- console.error(
262
- `[tree-sitter-loader] cache eviction skipped non-regular file: ${path} (possible symlink attack \u2014 see Phase 3.5 finding F-008).`
261
+ process.stderr.write(
262
+ `[tree-sitter-loader] cache eviction skipped non-regular file: ${path} (possible symlink attack \u2014 see Phase 3.5 finding F-008).
263
+ `
263
264
  );
264
265
  continue;
265
266
  }
@@ -374,8 +375,9 @@ async function loadGrammar(language, options = {}) {
374
375
  }
375
376
  evictBeyondRetainCount();
376
377
  } catch (e) {
377
- console.error(
378
- `[tree-sitter-loader] cache write failed for ${language}: ${e instanceof Error ? e.message : String(e)} \u2014 loading directly from memory.`
378
+ process.stderr.write(
379
+ `[tree-sitter-loader] cache write failed for ${language}: ${e instanceof Error ? e.message : String(e)} \u2014 loading directly from memory.
380
+ `
379
381
  );
380
382
  }
381
383
  const lang = await Language.load(body);
@@ -174,8 +174,9 @@ function evictBeyondRetainCount(retain = getCacheRetainCount()) {
174
174
  continue;
175
175
  }
176
176
  if (stat.isSymbolicLink() || !stat.isFile()) {
177
- console.error(
178
- `[tree-sitter-loader] cache eviction skipped non-regular file: ${path} (possible symlink attack \u2014 see Phase 3.5 finding F-008).`
177
+ process.stderr.write(
178
+ `[tree-sitter-loader] cache eviction skipped non-regular file: ${path} (possible symlink attack \u2014 see Phase 3.5 finding F-008).
179
+ `
179
180
  );
180
181
  continue;
181
182
  }
@@ -293,8 +294,9 @@ async function loadGrammar(language, options = {}) {
293
294
  }
294
295
  evictBeyondRetainCount();
295
296
  } catch (e) {
296
- console.error(
297
- `[tree-sitter-loader] cache write failed for ${language}: ${e instanceof Error ? e.message : String(e)} \u2014 loading directly from memory.`
297
+ process.stderr.write(
298
+ `[tree-sitter-loader] cache write failed for ${language}: ${e instanceof Error ? e.message : String(e)} \u2014 loading directly from memory.
299
+ `
298
300
  );
299
301
  }
300
302
  const lang = await Language.load(body);
@@ -341,6 +341,22 @@ var RawConfigSchema = z.object({
341
341
  accessScopes: z.array(z.string()).optional(),
342
342
  domains: z.array(DomainConfigSchema).default([]),
343
343
  rules: z.array(PatternRuleConfigSchema).default([]),
344
+ // P-M-036 (plan-stage-d-medium-sweep): customer-authored CR-style
345
+ // governance rules. DISTINCT from `rules:` above (path-scoped lint hints
346
+ // used by pattern-scanner). At config-refresh time these entries are
347
+ // loaded into the `knowledge_rules` SQLite table with
348
+ // `source = 'customer-config'` so `massu_knowledge_rule` and the
349
+ // governance docs surface customer-defined rules alongside framework CRs.
350
+ governance_rules: z.array(
351
+ z.object({
352
+ id: z.string().min(1, "governance_rules[].id is required"),
353
+ title: z.string().min(1, "governance_rules[].title is required"),
354
+ description: z.string().min(1, "governance_rules[].description is required"),
355
+ vr_type: z.string().default("VR-CUSTOM"),
356
+ reference_path: z.string().optional(),
357
+ severity: z.enum(["critical", "high", "medium", "low", "info"]).default("medium")
358
+ }).passthrough()
359
+ ).default([]),
344
360
  analytics: AnalyticsConfigSchema,
345
361
  governance: GovernanceConfigSchema,
346
362
  security: SecurityConfigSchema,
@@ -459,6 +475,8 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
459
475
  accessScopes: parsed.accessScopes,
460
476
  domains: parsed.domains,
461
477
  rules: parsed.rules,
478
+ // P-M-036: customer-authored CR-style governance rules.
479
+ governance_rules: parsed.governance_rules,
462
480
  analytics: parsed.analytics,
463
481
  governance: parsed.governance,
464
482
  security: parsed.security,
@@ -489,6 +507,11 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
489
507
  return _config;
490
508
  }
491
509
 
510
+ // src/hooks/lib/write-hook-message.ts
511
+ function writeHookMessage(message) {
512
+ process.stdout.write(JSON.stringify({ message }) + "\n");
513
+ }
514
+
492
515
  // src/hooks/auto-learning-pipeline.ts
493
516
  var MAX_FULL_DIFF_BYTES = 2 * 1024 * 1024;
494
517
  function getSessionFlagPath(sessionId) {
@@ -608,7 +631,7 @@ async function main() {
608
631
  lines.push("");
609
632
  lines.push("============================================================================");
610
633
  lines.push("");
611
- console.log(lines.join("\n"));
634
+ writeHookMessage(lines.join("\n"));
612
635
  cleanup(flagPath);
613
636
  } catch {
614
637
  }
@@ -348,6 +348,22 @@ var RawConfigSchema = z.object({
348
348
  accessScopes: z.array(z.string()).optional(),
349
349
  domains: z.array(DomainConfigSchema).default([]),
350
350
  rules: z.array(PatternRuleConfigSchema).default([]),
351
+ // P-M-036 (plan-stage-d-medium-sweep): customer-authored CR-style
352
+ // governance rules. DISTINCT from `rules:` above (path-scoped lint hints
353
+ // used by pattern-scanner). At config-refresh time these entries are
354
+ // loaded into the `knowledge_rules` SQLite table with
355
+ // `source = 'customer-config'` so `massu_knowledge_rule` and the
356
+ // governance docs surface customer-defined rules alongside framework CRs.
357
+ governance_rules: z.array(
358
+ z.object({
359
+ id: z.string().min(1, "governance_rules[].id is required"),
360
+ title: z.string().min(1, "governance_rules[].title is required"),
361
+ description: z.string().min(1, "governance_rules[].description is required"),
362
+ vr_type: z.string().default("VR-CUSTOM"),
363
+ reference_path: z.string().optional(),
364
+ severity: z.enum(["critical", "high", "medium", "low", "info"]).default("medium")
365
+ }).passthrough()
366
+ ).default([]),
351
367
  analytics: AnalyticsConfigSchema,
352
368
  governance: GovernanceConfigSchema,
353
369
  security: SecurityConfigSchema,
@@ -466,6 +482,8 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
466
482
  accessScopes: parsed.accessScopes,
467
483
  domains: parsed.domains,
468
484
  rules: parsed.rules,
485
+ // P-M-036: customer-authored CR-style governance rules.
486
+ governance_rules: parsed.governance_rules,
469
487
  analytics: parsed.analytics,
470
488
  governance: parsed.governance,
471
489
  security: parsed.security,
@@ -1021,6 +1039,12 @@ function initMemorySchema(db) {
1021
1039
  features TEXT DEFAULT '[]'
1022
1040
  );
1023
1041
  `);
1042
+ const licenseCacheCols = db.prepare(`PRAGMA table_info(license_cache)`).all();
1043
+ if (!licenseCacheCols.some((c) => c.name === "signed_payload_json")) {
1044
+ db.exec(
1045
+ `ALTER TABLE license_cache ADD COLUMN signed_payload_json TEXT NOT NULL DEFAULT ''`
1046
+ );
1047
+ }
1024
1048
  db.exec(`
1025
1049
  CREATE TABLE IF NOT EXISTS failure_classes (
1026
1050
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -1117,6 +1141,11 @@ function scoreFailureClasses(db, matchText, filePath, promptContext, weights) {
1117
1141
  return bestMatch;
1118
1142
  }
1119
1143
 
1144
+ // src/hooks/lib/write-hook-message.ts
1145
+ function writeHookMessage(message) {
1146
+ process.stdout.write(JSON.stringify({ message }) + "\n");
1147
+ }
1148
+
1120
1149
  // src/hooks/classify-failure.ts
1121
1150
  var BUG_FIX_INDICATORS = [
1122
1151
  /\b(catch|error|throw|fail|fix|bug|broken|missing|crash|wrong|typo|incorrect)\b/i
@@ -1258,7 +1287,7 @@ function outputKnownPattern(match) {
1258
1287
  lines.push(` Covered by: ${match.rules.join(", ")}`);
1259
1288
  }
1260
1289
  lines.push(" No new rules needed. Reference existing incident if logging.");
1261
- console.log(lines.join("\n"));
1290
+ writeHookMessage(lines.join("\n"));
1262
1291
  }
1263
1292
  function outputSimilarPattern(match) {
1264
1293
  const lines = [];
@@ -1268,7 +1297,7 @@ function outputSimilarPattern(match) {
1268
1297
  lines.push(` Check if existing rules cover this case: ${match.rules.join(", ")}`);
1269
1298
  }
1270
1299
  lines.push(" If genuinely new: create incident + prevention rule + enforcement.");
1271
- console.log(lines.join("\n"));
1300
+ writeHookMessage(lines.join("\n"));
1272
1301
  }
1273
1302
  function outputNewPattern(fileName, match) {
1274
1303
  const lines = [];
@@ -1282,7 +1311,7 @@ function outputNewPattern(fileName, match) {
1282
1311
  lines.push(" 1. INCIDENT REPORT");
1283
1312
  lines.push(" 2. PREVENTION RULE (if new failure pattern)");
1284
1313
  lines.push(" 3. ENFORCEMENT (hook or static check)");
1285
- console.log(lines.join("\n"));
1314
+ writeHookMessage(lines.join("\n"));
1286
1315
  }
1287
1316
  function readStdin() {
1288
1317
  return new Promise((resolve3) => {
@@ -348,6 +348,22 @@ var RawConfigSchema = z.object({
348
348
  accessScopes: z.array(z.string()).optional(),
349
349
  domains: z.array(DomainConfigSchema).default([]),
350
350
  rules: z.array(PatternRuleConfigSchema).default([]),
351
+ // P-M-036 (plan-stage-d-medium-sweep): customer-authored CR-style
352
+ // governance rules. DISTINCT from `rules:` above (path-scoped lint hints
353
+ // used by pattern-scanner). At config-refresh time these entries are
354
+ // loaded into the `knowledge_rules` SQLite table with
355
+ // `source = 'customer-config'` so `massu_knowledge_rule` and the
356
+ // governance docs surface customer-defined rules alongside framework CRs.
357
+ governance_rules: z.array(
358
+ z.object({
359
+ id: z.string().min(1, "governance_rules[].id is required"),
360
+ title: z.string().min(1, "governance_rules[].title is required"),
361
+ description: z.string().min(1, "governance_rules[].description is required"),
362
+ vr_type: z.string().default("VR-CUSTOM"),
363
+ reference_path: z.string().optional(),
364
+ severity: z.enum(["critical", "high", "medium", "low", "info"]).default("medium")
365
+ }).passthrough()
366
+ ).default([]),
351
367
  analytics: AnalyticsConfigSchema,
352
368
  governance: GovernanceConfigSchema,
353
369
  security: SecurityConfigSchema,
@@ -466,6 +482,8 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
466
482
  accessScopes: parsed.accessScopes,
467
483
  domains: parsed.domains,
468
484
  rules: parsed.rules,
485
+ // P-M-036: customer-authored CR-style governance rules.
486
+ governance_rules: parsed.governance_rules,
469
487
  analytics: parsed.analytics,
470
488
  governance: parsed.governance,
471
489
  security: parsed.security,
@@ -1018,6 +1036,12 @@ function initMemorySchema(db) {
1018
1036
  features TEXT DEFAULT '[]'
1019
1037
  );
1020
1038
  `);
1039
+ const licenseCacheCols = db.prepare(`PRAGMA table_info(license_cache)`).all();
1040
+ if (!licenseCacheCols.some((c) => c.name === "signed_payload_json")) {
1041
+ db.exec(
1042
+ `ALTER TABLE license_cache ADD COLUMN signed_payload_json TEXT NOT NULL DEFAULT ''`
1043
+ );
1044
+ }
1021
1045
  db.exec(`
1022
1046
  CREATE TABLE IF NOT EXISTS failure_classes (
1023
1047
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -1,5 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import{createRequire as __cr}from"module";const require=__cr(import.meta.url);
3
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
+ }) : x)(function(x) {
6
+ if (typeof require !== "undefined") return require.apply(this, arguments);
7
+ throw Error('Dynamic require of "' + x + '" is not supported');
8
+ });
3
9
 
4
10
  // src/hooks/fix-detector.ts
5
11
  import { execFileSync } from "child_process";
@@ -341,6 +347,22 @@ var RawConfigSchema = z.object({
341
347
  accessScopes: z.array(z.string()).optional(),
342
348
  domains: z.array(DomainConfigSchema).default([]),
343
349
  rules: z.array(PatternRuleConfigSchema).default([]),
350
+ // P-M-036 (plan-stage-d-medium-sweep): customer-authored CR-style
351
+ // governance rules. DISTINCT from `rules:` above (path-scoped lint hints
352
+ // used by pattern-scanner). At config-refresh time these entries are
353
+ // loaded into the `knowledge_rules` SQLite table with
354
+ // `source = 'customer-config'` so `massu_knowledge_rule` and the
355
+ // governance docs surface customer-defined rules alongside framework CRs.
356
+ governance_rules: z.array(
357
+ z.object({
358
+ id: z.string().min(1, "governance_rules[].id is required"),
359
+ title: z.string().min(1, "governance_rules[].title is required"),
360
+ description: z.string().min(1, "governance_rules[].description is required"),
361
+ vr_type: z.string().default("VR-CUSTOM"),
362
+ reference_path: z.string().optional(),
363
+ severity: z.enum(["critical", "high", "medium", "low", "info"]).default("medium")
364
+ }).passthrough()
365
+ ).default([]),
344
366
  analytics: AnalyticsConfigSchema,
345
367
  governance: GovernanceConfigSchema,
346
368
  security: SecurityConfigSchema,
@@ -459,6 +481,8 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
459
481
  accessScopes: parsed.accessScopes,
460
482
  domains: parsed.domains,
461
483
  rules: parsed.rules,
484
+ // P-M-036: customer-authored CR-style governance rules.
485
+ governance_rules: parsed.governance_rules,
462
486
  analytics: parsed.analytics,
463
487
  governance: parsed.governance,
464
488
  security: parsed.security,
@@ -489,6 +513,11 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
489
513
  return _config;
490
514
  }
491
515
 
516
+ // src/hooks/lib/write-hook-message.ts
517
+ function writeHookMessage(message) {
518
+ process.stdout.write(JSON.stringify({ message }) + "\n");
519
+ }
520
+
492
521
  // src/hooks/fix-detector.ts
493
522
  var FIX_HEURISTICS = [
494
523
  {
@@ -538,6 +567,64 @@ function getSessionFlagPath(sessionId) {
538
567
  }
539
568
  return join(dir, `fixes-${sessionId.slice(0, 12)}.jsonl`);
540
569
  }
570
+ function _stateDir() {
571
+ const dir = join(tmpdir(), "massu-fix-detector-state");
572
+ if (!existsSync2(dir)) {
573
+ mkdirSync(dir, { recursive: true });
574
+ }
575
+ return dir;
576
+ }
577
+ function _cwdHash(cwd) {
578
+ let h = 5381;
579
+ for (let i = 0; i < cwd.length; i++) {
580
+ h = (h << 5) + h + cwd.charCodeAt(i) | 0;
581
+ }
582
+ return Math.abs(h).toString(36);
583
+ }
584
+ function isGitWorkTreeFast(cwd) {
585
+ try {
586
+ const cachePath = join(_stateDir(), `worktree-${_cwdHash(cwd)}.json`);
587
+ if (existsSync2(cachePath)) {
588
+ const cached = JSON.parse(readFileSync2(cachePath, "utf-8"));
589
+ const ageMs = Date.now() - cached.ts;
590
+ if (ageMs < 36e5) return cached.isWorkTree;
591
+ }
592
+ let isWorkTree = false;
593
+ try {
594
+ execFileSync("git", ["rev-parse", "--is-inside-work-tree"], { cwd, timeout: 500, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
595
+ isWorkTree = true;
596
+ } catch {
597
+ isWorkTree = false;
598
+ }
599
+ try {
600
+ appendFileSync(cachePath, "");
601
+ const fs = __require("fs");
602
+ fs.writeFileSync(cachePath, JSON.stringify({ isWorkTree, ts: Date.now() }));
603
+ } catch {
604
+ }
605
+ return isWorkTree;
606
+ } catch {
607
+ return false;
608
+ }
609
+ }
610
+ function _disabledFlagPath(sessionId) {
611
+ return join(_stateDir(), `disabled-${sessionId.slice(0, 12)}.flag`);
612
+ }
613
+ function isSessionAutoDisabled(sessionId) {
614
+ try {
615
+ return existsSync2(_disabledFlagPath(sessionId));
616
+ } catch {
617
+ return false;
618
+ }
619
+ }
620
+ function markSessionAutoDisabled(sessionId, elapsedMs) {
621
+ try {
622
+ const fs = __require("fs");
623
+ fs.writeFileSync(_disabledFlagPath(sessionId), `disabled: previous git diff took ${elapsedMs}ms
624
+ `);
625
+ } catch {
626
+ }
627
+ }
541
628
  async function main() {
542
629
  try {
543
630
  const input = await readStdin();
@@ -563,12 +650,21 @@ async function main() {
563
650
  return;
564
651
  }
565
652
  const root = getProjectRoot();
653
+ if (!isGitWorkTreeFast(root) || isSessionAutoDisabled(hookInput.session_id)) {
654
+ process.exit(0);
655
+ return;
656
+ }
566
657
  let diff = "";
567
658
  try {
659
+ const startMs = Date.now();
568
660
  diff = execFileSync("git", ["diff", "--", filePath], { cwd: root, timeout: 3e3, encoding: "utf-8" });
569
661
  if (!diff) {
570
662
  diff = execFileSync("git", ["diff", "HEAD", "--", filePath], { cwd: root, timeout: 3e3, encoding: "utf-8" });
571
663
  }
664
+ const elapsedMs = Date.now() - startMs;
665
+ if (elapsedMs > 2e3) {
666
+ markSessionAutoDisabled(hookInput.session_id, elapsedMs);
667
+ }
572
668
  } catch {
573
669
  process.exit(0);
574
670
  return;
@@ -597,7 +693,7 @@ async function main() {
597
693
  appendFileSync(flagPath, JSON.stringify(signal) + "\n");
598
694
  const lines = readFileSync2(flagPath, "utf-8").split("\n").filter(Boolean);
599
695
  if (lines.length === 1) {
600
- console.log(
696
+ writeHookMessage(
601
697
  `[Massu Auto-Learning] Bug fix detected in ${filePath} (signals: ${detected.join(", ")}). The auto-learning pipeline will prompt you at session end to create an incident report, derive a prevention rule, and add enforcement.`
602
698
  );
603
699
  }
@@ -347,6 +347,22 @@ var RawConfigSchema = z.object({
347
347
  accessScopes: z.array(z.string()).optional(),
348
348
  domains: z.array(DomainConfigSchema).default([]),
349
349
  rules: z.array(PatternRuleConfigSchema).default([]),
350
+ // P-M-036 (plan-stage-d-medium-sweep): customer-authored CR-style
351
+ // governance rules. DISTINCT from `rules:` above (path-scoped lint hints
352
+ // used by pattern-scanner). At config-refresh time these entries are
353
+ // loaded into the `knowledge_rules` SQLite table with
354
+ // `source = 'customer-config'` so `massu_knowledge_rule` and the
355
+ // governance docs surface customer-defined rules alongside framework CRs.
356
+ governance_rules: z.array(
357
+ z.object({
358
+ id: z.string().min(1, "governance_rules[].id is required"),
359
+ title: z.string().min(1, "governance_rules[].title is required"),
360
+ description: z.string().min(1, "governance_rules[].description is required"),
361
+ vr_type: z.string().default("VR-CUSTOM"),
362
+ reference_path: z.string().optional(),
363
+ severity: z.enum(["critical", "high", "medium", "low", "info"]).default("medium")
364
+ }).passthrough()
365
+ ).default([]),
350
366
  analytics: AnalyticsConfigSchema,
351
367
  governance: GovernanceConfigSchema,
352
368
  security: SecurityConfigSchema,
@@ -465,6 +481,8 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
465
481
  accessScopes: parsed.accessScopes,
466
482
  domains: parsed.domains,
467
483
  rules: parsed.rules,
484
+ // P-M-036: customer-authored CR-style governance rules.
485
+ governance_rules: parsed.governance_rules,
468
486
  analytics: parsed.analytics,
469
487
  governance: parsed.governance,
470
488
  security: parsed.security,
@@ -1020,6 +1038,12 @@ function initMemorySchema(db) {
1020
1038
  features TEXT DEFAULT '[]'
1021
1039
  );
1022
1040
  `);
1041
+ const licenseCacheCols = db.prepare(`PRAGMA table_info(license_cache)`).all();
1042
+ if (!licenseCacheCols.some((c) => c.name === "signed_payload_json")) {
1043
+ db.exec(
1044
+ `ALTER TABLE license_cache ADD COLUMN signed_payload_json TEXT NOT NULL DEFAULT ''`
1045
+ );
1046
+ }
1023
1047
  db.exec(`
1024
1048
  CREATE TABLE IF NOT EXISTS failure_classes (
1025
1049
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -1143,6 +1167,11 @@ function scoreFailureClasses(db, matchText, filePath, promptContext, weights) {
1143
1167
  return bestMatch;
1144
1168
  }
1145
1169
 
1170
+ // src/hooks/lib/write-hook-message.ts
1171
+ function writeHookMessage(message) {
1172
+ process.stdout.write(JSON.stringify({ message }) + "\n");
1173
+ }
1174
+
1146
1175
  // src/hooks/incident-pipeline.ts
1147
1176
  async function main() {
1148
1177
  try {
@@ -1226,7 +1255,7 @@ async function main() {
1226
1255
  lines.push(" This step is MANDATORY per the auto-learning pipeline.");
1227
1256
  lines.push("============================================================================");
1228
1257
  lines.push("");
1229
- console.log(lines.join("\n"));
1258
+ writeHookMessage(lines.join("\n"));
1230
1259
  try {
1231
1260
  const taxonomyConfig = config.autoLearning?.failureClassification;
1232
1261
  if (taxonomyConfig?.enabled !== false) {
@@ -1,6 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import{createRequire as __cr}from"module";const require=__cr(import.meta.url);
3
3
 
4
+ // src/hooks/lib/write-hook-message.ts
5
+ function writeHookMessage(message) {
6
+ process.stdout.write(JSON.stringify({ message }) + "\n");
7
+ }
8
+
4
9
  // src/hooks/intent-suggester.ts
5
10
  var COMMAND_MAPPINGS = [
6
11
  {
@@ -79,9 +84,7 @@ async function main() {
79
84
  process.exit(0);
80
85
  return;
81
86
  }
82
- process.stdout.write(
83
- `Tip: Use ${match.command} to ${match.description}.`
84
- );
87
+ writeHookMessage(`Tip: Use ${match.command} to ${match.description}.`);
85
88
  } catch (_e) {
86
89
  }
87
90
  process.exit(0);
@@ -346,6 +346,22 @@ var RawConfigSchema = z.object({
346
346
  accessScopes: z.array(z.string()).optional(),
347
347
  domains: z.array(DomainConfigSchema).default([]),
348
348
  rules: z.array(PatternRuleConfigSchema).default([]),
349
+ // P-M-036 (plan-stage-d-medium-sweep): customer-authored CR-style
350
+ // governance rules. DISTINCT from `rules:` above (path-scoped lint hints
351
+ // used by pattern-scanner). At config-refresh time these entries are
352
+ // loaded into the `knowledge_rules` SQLite table with
353
+ // `source = 'customer-config'` so `massu_knowledge_rule` and the
354
+ // governance docs surface customer-defined rules alongside framework CRs.
355
+ governance_rules: z.array(
356
+ z.object({
357
+ id: z.string().min(1, "governance_rules[].id is required"),
358
+ title: z.string().min(1, "governance_rules[].title is required"),
359
+ description: z.string().min(1, "governance_rules[].description is required"),
360
+ vr_type: z.string().default("VR-CUSTOM"),
361
+ reference_path: z.string().optional(),
362
+ severity: z.enum(["critical", "high", "medium", "low", "info"]).default("medium")
363
+ }).passthrough()
364
+ ).default([]),
349
365
  analytics: AnalyticsConfigSchema,
350
366
  governance: GovernanceConfigSchema,
351
367
  security: SecurityConfigSchema,
@@ -464,6 +480,8 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
464
480
  accessScopes: parsed.accessScopes,
465
481
  domains: parsed.domains,
466
482
  rules: parsed.rules,
483
+ // P-M-036: customer-authored CR-style governance rules.
484
+ governance_rules: parsed.governance_rules,
467
485
  analytics: parsed.analytics,
468
486
  governance: parsed.governance,
469
487
  security: parsed.security,
@@ -558,6 +576,11 @@ function isInMiddlewareTree(dataDb, file) {
558
576
  return result !== void 0;
559
577
  }
560
578
 
579
+ // src/hooks/lib/write-hook-message.ts
580
+ function writeHookMessage(message) {
581
+ process.stdout.write(JSON.stringify({ message }) + "\n");
582
+ }
583
+
561
584
  // src/hooks/post-edit-context.ts
562
585
  async function main() {
563
586
  try {
@@ -595,7 +618,7 @@ async function main() {
595
618
  } catch (_e) {
596
619
  }
597
620
  if (warnings.length > 0) {
598
- console.log(`[Massu] ${warnings.join(" | ")}`);
621
+ writeHookMessage(`[Massu] ${warnings.join(" | ")}`);
599
622
  }
600
623
  } catch (_e) {
601
624
  }