@massu/core 1.2.1 → 1.4.0-soak.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 (61) hide show
  1. package/README.md +40 -0
  2. package/commands/README.md +137 -0
  3. package/commands/massu-deploy.python-docker.md +170 -0
  4. package/commands/massu-deploy.python-fly.md +189 -0
  5. package/commands/massu-deploy.python-launchd.md +144 -0
  6. package/commands/massu-deploy.python-systemd.md +163 -0
  7. package/commands/massu-deploy.python.md +200 -0
  8. package/commands/massu-scaffold-page.md +172 -59
  9. package/commands/massu-scaffold-page.swift.md +121 -0
  10. package/commands/massu-scaffold-router.python-django.md +153 -0
  11. package/commands/massu-scaffold-router.python-fastapi.md +145 -0
  12. package/commands/massu-scaffold-router.python.md +143 -0
  13. package/dist/cli.js +10170 -4138
  14. package/dist/hooks/auto-learning-pipeline.js +44 -6
  15. package/dist/hooks/classify-failure.js +44 -6
  16. package/dist/hooks/cost-tracker.js +44 -6
  17. package/dist/hooks/fix-detector.js +44 -6
  18. package/dist/hooks/incident-pipeline.js +44 -6
  19. package/dist/hooks/post-edit-context.js +44 -6
  20. package/dist/hooks/post-tool-use.js +44 -6
  21. package/dist/hooks/pre-compact.js +44 -6
  22. package/dist/hooks/pre-delete-check.js +44 -6
  23. package/dist/hooks/quality-event.js +44 -6
  24. package/dist/hooks/rule-enforcement-pipeline.js +44 -6
  25. package/dist/hooks/session-end.js +44 -6
  26. package/dist/hooks/session-start.js +4789 -410
  27. package/dist/hooks/user-prompt.js +44 -6
  28. package/package.json +10 -4
  29. package/src/cli.ts +28 -2
  30. package/src/commands/config-refresh.ts +88 -20
  31. package/src/commands/init.ts +130 -23
  32. package/src/commands/install-commands.ts +482 -42
  33. package/src/commands/refresh-log.ts +37 -0
  34. package/src/commands/show-template.ts +65 -0
  35. package/src/commands/template-engine.ts +262 -0
  36. package/src/commands/watch.ts +430 -0
  37. package/src/config.ts +69 -3
  38. package/src/detect/adapters/nextjs-trpc.ts +166 -0
  39. package/src/detect/adapters/parse-guard.ts +133 -0
  40. package/src/detect/adapters/python-django.ts +208 -0
  41. package/src/detect/adapters/python-fastapi.ts +223 -0
  42. package/src/detect/adapters/query-helpers.ts +170 -0
  43. package/src/detect/adapters/runner.ts +252 -0
  44. package/src/detect/adapters/swift-swiftui.ts +171 -0
  45. package/src/detect/adapters/tree-sitter-loader.ts +348 -0
  46. package/src/detect/adapters/types.ts +174 -0
  47. package/src/detect/codebase-introspector.ts +190 -0
  48. package/src/detect/index.ts +28 -2
  49. package/src/detect/regex-fallback.ts +449 -0
  50. package/src/hooks/session-start.ts +94 -3
  51. package/src/lib/gitToplevel.ts +22 -0
  52. package/src/lib/installLock.ts +179 -0
  53. package/src/lib/pidLiveness.ts +67 -0
  54. package/src/lsp/auto-detect.ts +89 -0
  55. package/src/lsp/client.ts +590 -0
  56. package/src/lsp/enrich.ts +127 -0
  57. package/src/lsp/types.ts +221 -0
  58. package/src/watch/daemon.ts +385 -0
  59. package/src/watch/lockfile-detector.ts +65 -0
  60. package/src/watch/paths.ts +279 -0
  61. package/src/watch/state.ts +178 -0
@@ -252,6 +252,7 @@ var FrameworkConfigSchema = z.object({
252
252
  ui: z.string().default("none"),
253
253
  languages: z.record(z.string(), LanguageFrameworkEntrySchema).optional()
254
254
  }).passthrough();
255
+ var DetectedConfigSchema = z.object({}).passthrough().optional();
255
256
  var VerificationEntrySchema = z.object({
256
257
  type: z.string().optional(),
257
258
  test: z.string().optional(),
@@ -276,6 +277,31 @@ var DetectionConfigSchema = z.object({
276
277
  signal_weights: z.record(z.string(), z.number()).optional(),
277
278
  disable_builtin: z.boolean().optional()
278
279
  }).passthrough().optional();
280
+ var WatchConfigSchema = z.object({
281
+ debounce_ms: z.number().int().positive().default(3e3),
282
+ storm_threshold: z.number().int().positive().default(50),
283
+ deep_storm_threshold: z.number().int().positive().default(500),
284
+ hard_timeout_ms: z.number().int().positive().default(3e5),
285
+ scope: z.enum(["paths", "full"]).default("paths"),
286
+ // Plan 3a hotfix 2026-05-02: refuse to start if the watch surface
287
+ // exceeds this many files. Prevents the misconfig pattern where
288
+ // `paths.source_dirs` includes `.` or otherwise expands to a 60K+
289
+ // file tree, producing 30-100% steady CPU. Override via
290
+ // `paths_full_root_opt_in: true` for users on small repos who genuinely
291
+ // need root-level watching.
292
+ max_watched_files: z.number().int().positive().default(1e4),
293
+ paths_full_root_opt_in: z.boolean().default(false)
294
+ }).passthrough().optional();
295
+ var LSPConfigSchema = z.object({
296
+ enabled: z.boolean().default(false),
297
+ servers: z.array(z.object({
298
+ language: z.string(),
299
+ command: z.string()
300
+ })).default([]),
301
+ autoDetect: z.object({
302
+ viaPortScan: z.boolean().default(false)
303
+ }).optional()
304
+ }).passthrough();
279
305
  var RawConfigSchema = z.object({
280
306
  schema_version: z.union([z.literal(1), z.literal(2)]).default(1),
281
307
  project: z.object({
@@ -308,7 +334,13 @@ var RawConfigSchema = z.object({
308
334
  verification: VerificationConfigSchema,
309
335
  canonical_paths: CanonicalPathsSchema,
310
336
  verification_types: VerificationTypesSchema,
311
- detection: DetectionConfigSchema
337
+ detection: DetectionConfigSchema,
338
+ // Plan #2: detector-owned per-language conventions (free-form passthrough)
339
+ detected: DetectedConfigSchema,
340
+ // Plan 3a: file-watcher daemon tunables
341
+ watch: WatchConfigSchema,
342
+ // Plan 3b Phase 4: optional LSP enrichment of AST adapter results.
343
+ lsp: LSPConfigSchema.optional()
312
344
  }).passthrough();
313
345
  var _config = null;
314
346
  var _projectRoot = null;
@@ -385,13 +417,16 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
385
417
  name: parsed.project.name,
386
418
  root: projectRoot
387
419
  },
420
+ // Spread `fw` first so zod-`.passthrough()` extras (e.g., `framework.swift`,
421
+ // `framework.python`) survive into the consumer-visible Config. Then override
422
+ // the v2-backcompat-mirrored router/orm/ui values. Without the spread, the
423
+ // variant-resolution `pickVariant` (install-commands.ts) cannot see the
424
+ // top-level passthrough language blocks.
388
425
  framework: {
389
- type: fw.type,
426
+ ...fw,
390
427
  router,
391
428
  orm,
392
- ui,
393
- primary: fw.primary,
394
- languages: fw.languages
429
+ ui
395
430
  },
396
431
  paths: parsed.paths,
397
432
  toolPrefix: parsed.toolPrefix,
@@ -412,7 +447,10 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
412
447
  verification: parsed.verification,
413
448
  canonical_paths: parsed.canonical_paths,
414
449
  verification_types: parsed.verification_types,
415
- detection: parsed.detection
450
+ detection: parsed.detection,
451
+ detected: parsed.detected,
452
+ watch: parsed.watch,
453
+ lsp: parsed.lsp
416
454
  };
417
455
  if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
418
456
  _config.cloud = {
@@ -252,6 +252,7 @@ var FrameworkConfigSchema = z.object({
252
252
  ui: z.string().default("none"),
253
253
  languages: z.record(z.string(), LanguageFrameworkEntrySchema).optional()
254
254
  }).passthrough();
255
+ var DetectedConfigSchema = z.object({}).passthrough().optional();
255
256
  var VerificationEntrySchema = z.object({
256
257
  type: z.string().optional(),
257
258
  test: z.string().optional(),
@@ -276,6 +277,31 @@ var DetectionConfigSchema = z.object({
276
277
  signal_weights: z.record(z.string(), z.number()).optional(),
277
278
  disable_builtin: z.boolean().optional()
278
279
  }).passthrough().optional();
280
+ var WatchConfigSchema = z.object({
281
+ debounce_ms: z.number().int().positive().default(3e3),
282
+ storm_threshold: z.number().int().positive().default(50),
283
+ deep_storm_threshold: z.number().int().positive().default(500),
284
+ hard_timeout_ms: z.number().int().positive().default(3e5),
285
+ scope: z.enum(["paths", "full"]).default("paths"),
286
+ // Plan 3a hotfix 2026-05-02: refuse to start if the watch surface
287
+ // exceeds this many files. Prevents the misconfig pattern where
288
+ // `paths.source_dirs` includes `.` or otherwise expands to a 60K+
289
+ // file tree, producing 30-100% steady CPU. Override via
290
+ // `paths_full_root_opt_in: true` for users on small repos who genuinely
291
+ // need root-level watching.
292
+ max_watched_files: z.number().int().positive().default(1e4),
293
+ paths_full_root_opt_in: z.boolean().default(false)
294
+ }).passthrough().optional();
295
+ var LSPConfigSchema = z.object({
296
+ enabled: z.boolean().default(false),
297
+ servers: z.array(z.object({
298
+ language: z.string(),
299
+ command: z.string()
300
+ })).default([]),
301
+ autoDetect: z.object({
302
+ viaPortScan: z.boolean().default(false)
303
+ }).optional()
304
+ }).passthrough();
279
305
  var RawConfigSchema = z.object({
280
306
  schema_version: z.union([z.literal(1), z.literal(2)]).default(1),
281
307
  project: z.object({
@@ -308,7 +334,13 @@ var RawConfigSchema = z.object({
308
334
  verification: VerificationConfigSchema,
309
335
  canonical_paths: CanonicalPathsSchema,
310
336
  verification_types: VerificationTypesSchema,
311
- detection: DetectionConfigSchema
337
+ detection: DetectionConfigSchema,
338
+ // Plan #2: detector-owned per-language conventions (free-form passthrough)
339
+ detected: DetectedConfigSchema,
340
+ // Plan 3a: file-watcher daemon tunables
341
+ watch: WatchConfigSchema,
342
+ // Plan 3b Phase 4: optional LSP enrichment of AST adapter results.
343
+ lsp: LSPConfigSchema.optional()
312
344
  }).passthrough();
313
345
  var _config = null;
314
346
  var _projectRoot = null;
@@ -385,13 +417,16 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
385
417
  name: parsed.project.name,
386
418
  root: projectRoot
387
419
  },
420
+ // Spread `fw` first so zod-`.passthrough()` extras (e.g., `framework.swift`,
421
+ // `framework.python`) survive into the consumer-visible Config. Then override
422
+ // the v2-backcompat-mirrored router/orm/ui values. Without the spread, the
423
+ // variant-resolution `pickVariant` (install-commands.ts) cannot see the
424
+ // top-level passthrough language blocks.
388
425
  framework: {
389
- type: fw.type,
426
+ ...fw,
390
427
  router,
391
428
  orm,
392
- ui,
393
- primary: fw.primary,
394
- languages: fw.languages
429
+ ui
395
430
  },
396
431
  paths: parsed.paths,
397
432
  toolPrefix: parsed.toolPrefix,
@@ -412,7 +447,10 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
412
447
  verification: parsed.verification,
413
448
  canonical_paths: parsed.canonical_paths,
414
449
  verification_types: parsed.verification_types,
415
- detection: parsed.detection
450
+ detection: parsed.detection,
451
+ detected: parsed.detected,
452
+ watch: parsed.watch,
453
+ lsp: parsed.lsp
416
454
  };
417
455
  if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
418
456
  _config.cloud = {
@@ -252,6 +252,7 @@ var FrameworkConfigSchema = z.object({
252
252
  ui: z.string().default("none"),
253
253
  languages: z.record(z.string(), LanguageFrameworkEntrySchema).optional()
254
254
  }).passthrough();
255
+ var DetectedConfigSchema = z.object({}).passthrough().optional();
255
256
  var VerificationEntrySchema = z.object({
256
257
  type: z.string().optional(),
257
258
  test: z.string().optional(),
@@ -276,6 +277,31 @@ var DetectionConfigSchema = z.object({
276
277
  signal_weights: z.record(z.string(), z.number()).optional(),
277
278
  disable_builtin: z.boolean().optional()
278
279
  }).passthrough().optional();
280
+ var WatchConfigSchema = z.object({
281
+ debounce_ms: z.number().int().positive().default(3e3),
282
+ storm_threshold: z.number().int().positive().default(50),
283
+ deep_storm_threshold: z.number().int().positive().default(500),
284
+ hard_timeout_ms: z.number().int().positive().default(3e5),
285
+ scope: z.enum(["paths", "full"]).default("paths"),
286
+ // Plan 3a hotfix 2026-05-02: refuse to start if the watch surface
287
+ // exceeds this many files. Prevents the misconfig pattern where
288
+ // `paths.source_dirs` includes `.` or otherwise expands to a 60K+
289
+ // file tree, producing 30-100% steady CPU. Override via
290
+ // `paths_full_root_opt_in: true` for users on small repos who genuinely
291
+ // need root-level watching.
292
+ max_watched_files: z.number().int().positive().default(1e4),
293
+ paths_full_root_opt_in: z.boolean().default(false)
294
+ }).passthrough().optional();
295
+ var LSPConfigSchema = z.object({
296
+ enabled: z.boolean().default(false),
297
+ servers: z.array(z.object({
298
+ language: z.string(),
299
+ command: z.string()
300
+ })).default([]),
301
+ autoDetect: z.object({
302
+ viaPortScan: z.boolean().default(false)
303
+ }).optional()
304
+ }).passthrough();
279
305
  var RawConfigSchema = z.object({
280
306
  schema_version: z.union([z.literal(1), z.literal(2)]).default(1),
281
307
  project: z.object({
@@ -308,7 +334,13 @@ var RawConfigSchema = z.object({
308
334
  verification: VerificationConfigSchema,
309
335
  canonical_paths: CanonicalPathsSchema,
310
336
  verification_types: VerificationTypesSchema,
311
- detection: DetectionConfigSchema
337
+ detection: DetectionConfigSchema,
338
+ // Plan #2: detector-owned per-language conventions (free-form passthrough)
339
+ detected: DetectedConfigSchema,
340
+ // Plan 3a: file-watcher daemon tunables
341
+ watch: WatchConfigSchema,
342
+ // Plan 3b Phase 4: optional LSP enrichment of AST adapter results.
343
+ lsp: LSPConfigSchema.optional()
312
344
  }).passthrough();
313
345
  var _config = null;
314
346
  var _projectRoot = null;
@@ -385,13 +417,16 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
385
417
  name: parsed.project.name,
386
418
  root: projectRoot
387
419
  },
420
+ // Spread `fw` first so zod-`.passthrough()` extras (e.g., `framework.swift`,
421
+ // `framework.python`) survive into the consumer-visible Config. Then override
422
+ // the v2-backcompat-mirrored router/orm/ui values. Without the spread, the
423
+ // variant-resolution `pickVariant` (install-commands.ts) cannot see the
424
+ // top-level passthrough language blocks.
388
425
  framework: {
389
- type: fw.type,
426
+ ...fw,
390
427
  router,
391
428
  orm,
392
- ui,
393
- primary: fw.primary,
394
- languages: fw.languages
429
+ ui
395
430
  },
396
431
  paths: parsed.paths,
397
432
  toolPrefix: parsed.toolPrefix,
@@ -412,7 +447,10 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
412
447
  verification: parsed.verification,
413
448
  canonical_paths: parsed.canonical_paths,
414
449
  verification_types: parsed.verification_types,
415
- detection: parsed.detection
450
+ detection: parsed.detection,
451
+ detected: parsed.detected,
452
+ watch: parsed.watch,
453
+ lsp: parsed.lsp
416
454
  };
417
455
  if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
418
456
  _config.cloud = {
@@ -252,6 +252,7 @@ var FrameworkConfigSchema = z.object({
252
252
  ui: z.string().default("none"),
253
253
  languages: z.record(z.string(), LanguageFrameworkEntrySchema).optional()
254
254
  }).passthrough();
255
+ var DetectedConfigSchema = z.object({}).passthrough().optional();
255
256
  var VerificationEntrySchema = z.object({
256
257
  type: z.string().optional(),
257
258
  test: z.string().optional(),
@@ -276,6 +277,31 @@ var DetectionConfigSchema = z.object({
276
277
  signal_weights: z.record(z.string(), z.number()).optional(),
277
278
  disable_builtin: z.boolean().optional()
278
279
  }).passthrough().optional();
280
+ var WatchConfigSchema = z.object({
281
+ debounce_ms: z.number().int().positive().default(3e3),
282
+ storm_threshold: z.number().int().positive().default(50),
283
+ deep_storm_threshold: z.number().int().positive().default(500),
284
+ hard_timeout_ms: z.number().int().positive().default(3e5),
285
+ scope: z.enum(["paths", "full"]).default("paths"),
286
+ // Plan 3a hotfix 2026-05-02: refuse to start if the watch surface
287
+ // exceeds this many files. Prevents the misconfig pattern where
288
+ // `paths.source_dirs` includes `.` or otherwise expands to a 60K+
289
+ // file tree, producing 30-100% steady CPU. Override via
290
+ // `paths_full_root_opt_in: true` for users on small repos who genuinely
291
+ // need root-level watching.
292
+ max_watched_files: z.number().int().positive().default(1e4),
293
+ paths_full_root_opt_in: z.boolean().default(false)
294
+ }).passthrough().optional();
295
+ var LSPConfigSchema = z.object({
296
+ enabled: z.boolean().default(false),
297
+ servers: z.array(z.object({
298
+ language: z.string(),
299
+ command: z.string()
300
+ })).default([]),
301
+ autoDetect: z.object({
302
+ viaPortScan: z.boolean().default(false)
303
+ }).optional()
304
+ }).passthrough();
279
305
  var RawConfigSchema = z.object({
280
306
  schema_version: z.union([z.literal(1), z.literal(2)]).default(1),
281
307
  project: z.object({
@@ -308,7 +334,13 @@ var RawConfigSchema = z.object({
308
334
  verification: VerificationConfigSchema,
309
335
  canonical_paths: CanonicalPathsSchema,
310
336
  verification_types: VerificationTypesSchema,
311
- detection: DetectionConfigSchema
337
+ detection: DetectionConfigSchema,
338
+ // Plan #2: detector-owned per-language conventions (free-form passthrough)
339
+ detected: DetectedConfigSchema,
340
+ // Plan 3a: file-watcher daemon tunables
341
+ watch: WatchConfigSchema,
342
+ // Plan 3b Phase 4: optional LSP enrichment of AST adapter results.
343
+ lsp: LSPConfigSchema.optional()
312
344
  }).passthrough();
313
345
  var _config = null;
314
346
  var _projectRoot = null;
@@ -385,13 +417,16 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
385
417
  name: parsed.project.name,
386
418
  root: projectRoot
387
419
  },
420
+ // Spread `fw` first so zod-`.passthrough()` extras (e.g., `framework.swift`,
421
+ // `framework.python`) survive into the consumer-visible Config. Then override
422
+ // the v2-backcompat-mirrored router/orm/ui values. Without the spread, the
423
+ // variant-resolution `pickVariant` (install-commands.ts) cannot see the
424
+ // top-level passthrough language blocks.
388
425
  framework: {
389
- type: fw.type,
426
+ ...fw,
390
427
  router,
391
428
  orm,
392
- ui,
393
- primary: fw.primary,
394
- languages: fw.languages
429
+ ui
395
430
  },
396
431
  paths: parsed.paths,
397
432
  toolPrefix: parsed.toolPrefix,
@@ -412,7 +447,10 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
412
447
  verification: parsed.verification,
413
448
  canonical_paths: parsed.canonical_paths,
414
449
  verification_types: parsed.verification_types,
415
- detection: parsed.detection
450
+ detection: parsed.detection,
451
+ detected: parsed.detected,
452
+ watch: parsed.watch,
453
+ lsp: parsed.lsp
416
454
  };
417
455
  if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
418
456
  _config.cloud = {
@@ -251,6 +251,7 @@ var FrameworkConfigSchema = z.object({
251
251
  ui: z.string().default("none"),
252
252
  languages: z.record(z.string(), LanguageFrameworkEntrySchema).optional()
253
253
  }).passthrough();
254
+ var DetectedConfigSchema = z.object({}).passthrough().optional();
254
255
  var VerificationEntrySchema = z.object({
255
256
  type: z.string().optional(),
256
257
  test: z.string().optional(),
@@ -275,6 +276,31 @@ var DetectionConfigSchema = z.object({
275
276
  signal_weights: z.record(z.string(), z.number()).optional(),
276
277
  disable_builtin: z.boolean().optional()
277
278
  }).passthrough().optional();
279
+ var WatchConfigSchema = z.object({
280
+ debounce_ms: z.number().int().positive().default(3e3),
281
+ storm_threshold: z.number().int().positive().default(50),
282
+ deep_storm_threshold: z.number().int().positive().default(500),
283
+ hard_timeout_ms: z.number().int().positive().default(3e5),
284
+ scope: z.enum(["paths", "full"]).default("paths"),
285
+ // Plan 3a hotfix 2026-05-02: refuse to start if the watch surface
286
+ // exceeds this many files. Prevents the misconfig pattern where
287
+ // `paths.source_dirs` includes `.` or otherwise expands to a 60K+
288
+ // file tree, producing 30-100% steady CPU. Override via
289
+ // `paths_full_root_opt_in: true` for users on small repos who genuinely
290
+ // need root-level watching.
291
+ max_watched_files: z.number().int().positive().default(1e4),
292
+ paths_full_root_opt_in: z.boolean().default(false)
293
+ }).passthrough().optional();
294
+ var LSPConfigSchema = z.object({
295
+ enabled: z.boolean().default(false),
296
+ servers: z.array(z.object({
297
+ language: z.string(),
298
+ command: z.string()
299
+ })).default([]),
300
+ autoDetect: z.object({
301
+ viaPortScan: z.boolean().default(false)
302
+ }).optional()
303
+ }).passthrough();
278
304
  var RawConfigSchema = z.object({
279
305
  schema_version: z.union([z.literal(1), z.literal(2)]).default(1),
280
306
  project: z.object({
@@ -307,7 +333,13 @@ var RawConfigSchema = z.object({
307
333
  verification: VerificationConfigSchema,
308
334
  canonical_paths: CanonicalPathsSchema,
309
335
  verification_types: VerificationTypesSchema,
310
- detection: DetectionConfigSchema
336
+ detection: DetectionConfigSchema,
337
+ // Plan #2: detector-owned per-language conventions (free-form passthrough)
338
+ detected: DetectedConfigSchema,
339
+ // Plan 3a: file-watcher daemon tunables
340
+ watch: WatchConfigSchema,
341
+ // Plan 3b Phase 4: optional LSP enrichment of AST adapter results.
342
+ lsp: LSPConfigSchema.optional()
311
343
  }).passthrough();
312
344
  var _config = null;
313
345
  var _projectRoot = null;
@@ -384,13 +416,16 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
384
416
  name: parsed.project.name,
385
417
  root: projectRoot
386
418
  },
419
+ // Spread `fw` first so zod-`.passthrough()` extras (e.g., `framework.swift`,
420
+ // `framework.python`) survive into the consumer-visible Config. Then override
421
+ // the v2-backcompat-mirrored router/orm/ui values. Without the spread, the
422
+ // variant-resolution `pickVariant` (install-commands.ts) cannot see the
423
+ // top-level passthrough language blocks.
387
424
  framework: {
388
- type: fw.type,
425
+ ...fw,
389
426
  router,
390
427
  orm,
391
- ui,
392
- primary: fw.primary,
393
- languages: fw.languages
428
+ ui
394
429
  },
395
430
  paths: parsed.paths,
396
431
  toolPrefix: parsed.toolPrefix,
@@ -411,7 +446,10 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
411
446
  verification: parsed.verification,
412
447
  canonical_paths: parsed.canonical_paths,
413
448
  verification_types: parsed.verification_types,
414
- detection: parsed.detection
449
+ detection: parsed.detection,
450
+ detected: parsed.detected,
451
+ watch: parsed.watch,
452
+ lsp: parsed.lsp
415
453
  };
416
454
  if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
417
455
  _config.cloud = {
@@ -250,6 +250,7 @@ var FrameworkConfigSchema = z.object({
250
250
  ui: z.string().default("none"),
251
251
  languages: z.record(z.string(), LanguageFrameworkEntrySchema).optional()
252
252
  }).passthrough();
253
+ var DetectedConfigSchema = z.object({}).passthrough().optional();
253
254
  var VerificationEntrySchema = z.object({
254
255
  type: z.string().optional(),
255
256
  test: z.string().optional(),
@@ -274,6 +275,31 @@ var DetectionConfigSchema = z.object({
274
275
  signal_weights: z.record(z.string(), z.number()).optional(),
275
276
  disable_builtin: z.boolean().optional()
276
277
  }).passthrough().optional();
278
+ var WatchConfigSchema = z.object({
279
+ debounce_ms: z.number().int().positive().default(3e3),
280
+ storm_threshold: z.number().int().positive().default(50),
281
+ deep_storm_threshold: z.number().int().positive().default(500),
282
+ hard_timeout_ms: z.number().int().positive().default(3e5),
283
+ scope: z.enum(["paths", "full"]).default("paths"),
284
+ // Plan 3a hotfix 2026-05-02: refuse to start if the watch surface
285
+ // exceeds this many files. Prevents the misconfig pattern where
286
+ // `paths.source_dirs` includes `.` or otherwise expands to a 60K+
287
+ // file tree, producing 30-100% steady CPU. Override via
288
+ // `paths_full_root_opt_in: true` for users on small repos who genuinely
289
+ // need root-level watching.
290
+ max_watched_files: z.number().int().positive().default(1e4),
291
+ paths_full_root_opt_in: z.boolean().default(false)
292
+ }).passthrough().optional();
293
+ var LSPConfigSchema = z.object({
294
+ enabled: z.boolean().default(false),
295
+ servers: z.array(z.object({
296
+ language: z.string(),
297
+ command: z.string()
298
+ })).default([]),
299
+ autoDetect: z.object({
300
+ viaPortScan: z.boolean().default(false)
301
+ }).optional()
302
+ }).passthrough();
277
303
  var RawConfigSchema = z.object({
278
304
  schema_version: z.union([z.literal(1), z.literal(2)]).default(1),
279
305
  project: z.object({
@@ -306,7 +332,13 @@ var RawConfigSchema = z.object({
306
332
  verification: VerificationConfigSchema,
307
333
  canonical_paths: CanonicalPathsSchema,
308
334
  verification_types: VerificationTypesSchema,
309
- detection: DetectionConfigSchema
335
+ detection: DetectionConfigSchema,
336
+ // Plan #2: detector-owned per-language conventions (free-form passthrough)
337
+ detected: DetectedConfigSchema,
338
+ // Plan 3a: file-watcher daemon tunables
339
+ watch: WatchConfigSchema,
340
+ // Plan 3b Phase 4: optional LSP enrichment of AST adapter results.
341
+ lsp: LSPConfigSchema.optional()
310
342
  }).passthrough();
311
343
  var _config = null;
312
344
  var _projectRoot = null;
@@ -383,13 +415,16 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
383
415
  name: parsed.project.name,
384
416
  root: projectRoot
385
417
  },
418
+ // Spread `fw` first so zod-`.passthrough()` extras (e.g., `framework.swift`,
419
+ // `framework.python`) survive into the consumer-visible Config. Then override
420
+ // the v2-backcompat-mirrored router/orm/ui values. Without the spread, the
421
+ // variant-resolution `pickVariant` (install-commands.ts) cannot see the
422
+ // top-level passthrough language blocks.
386
423
  framework: {
387
- type: fw.type,
424
+ ...fw,
388
425
  router,
389
426
  orm,
390
- ui,
391
- primary: fw.primary,
392
- languages: fw.languages
427
+ ui
393
428
  },
394
429
  paths: parsed.paths,
395
430
  toolPrefix: parsed.toolPrefix,
@@ -410,7 +445,10 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
410
445
  verification: parsed.verification,
411
446
  canonical_paths: parsed.canonical_paths,
412
447
  verification_types: parsed.verification_types,
413
- detection: parsed.detection
448
+ detection: parsed.detection,
449
+ detected: parsed.detected,
450
+ watch: parsed.watch,
451
+ lsp: parsed.lsp
414
452
  };
415
453
  if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
416
454
  _config.cloud = {
@@ -252,6 +252,7 @@ var FrameworkConfigSchema = z.object({
252
252
  ui: z.string().default("none"),
253
253
  languages: z.record(z.string(), LanguageFrameworkEntrySchema).optional()
254
254
  }).passthrough();
255
+ var DetectedConfigSchema = z.object({}).passthrough().optional();
255
256
  var VerificationEntrySchema = z.object({
256
257
  type: z.string().optional(),
257
258
  test: z.string().optional(),
@@ -276,6 +277,31 @@ var DetectionConfigSchema = z.object({
276
277
  signal_weights: z.record(z.string(), z.number()).optional(),
277
278
  disable_builtin: z.boolean().optional()
278
279
  }).passthrough().optional();
280
+ var WatchConfigSchema = z.object({
281
+ debounce_ms: z.number().int().positive().default(3e3),
282
+ storm_threshold: z.number().int().positive().default(50),
283
+ deep_storm_threshold: z.number().int().positive().default(500),
284
+ hard_timeout_ms: z.number().int().positive().default(3e5),
285
+ scope: z.enum(["paths", "full"]).default("paths"),
286
+ // Plan 3a hotfix 2026-05-02: refuse to start if the watch surface
287
+ // exceeds this many files. Prevents the misconfig pattern where
288
+ // `paths.source_dirs` includes `.` or otherwise expands to a 60K+
289
+ // file tree, producing 30-100% steady CPU. Override via
290
+ // `paths_full_root_opt_in: true` for users on small repos who genuinely
291
+ // need root-level watching.
292
+ max_watched_files: z.number().int().positive().default(1e4),
293
+ paths_full_root_opt_in: z.boolean().default(false)
294
+ }).passthrough().optional();
295
+ var LSPConfigSchema = z.object({
296
+ enabled: z.boolean().default(false),
297
+ servers: z.array(z.object({
298
+ language: z.string(),
299
+ command: z.string()
300
+ })).default([]),
301
+ autoDetect: z.object({
302
+ viaPortScan: z.boolean().default(false)
303
+ }).optional()
304
+ }).passthrough();
279
305
  var RawConfigSchema = z.object({
280
306
  schema_version: z.union([z.literal(1), z.literal(2)]).default(1),
281
307
  project: z.object({
@@ -308,7 +334,13 @@ var RawConfigSchema = z.object({
308
334
  verification: VerificationConfigSchema,
309
335
  canonical_paths: CanonicalPathsSchema,
310
336
  verification_types: VerificationTypesSchema,
311
- detection: DetectionConfigSchema
337
+ detection: DetectionConfigSchema,
338
+ // Plan #2: detector-owned per-language conventions (free-form passthrough)
339
+ detected: DetectedConfigSchema,
340
+ // Plan 3a: file-watcher daemon tunables
341
+ watch: WatchConfigSchema,
342
+ // Plan 3b Phase 4: optional LSP enrichment of AST adapter results.
343
+ lsp: LSPConfigSchema.optional()
312
344
  }).passthrough();
313
345
  var _config = null;
314
346
  var _projectRoot = null;
@@ -385,13 +417,16 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
385
417
  name: parsed.project.name,
386
418
  root: projectRoot
387
419
  },
420
+ // Spread `fw` first so zod-`.passthrough()` extras (e.g., `framework.swift`,
421
+ // `framework.python`) survive into the consumer-visible Config. Then override
422
+ // the v2-backcompat-mirrored router/orm/ui values. Without the spread, the
423
+ // variant-resolution `pickVariant` (install-commands.ts) cannot see the
424
+ // top-level passthrough language blocks.
388
425
  framework: {
389
- type: fw.type,
426
+ ...fw,
390
427
  router,
391
428
  orm,
392
- ui,
393
- primary: fw.primary,
394
- languages: fw.languages
429
+ ui
395
430
  },
396
431
  paths: parsed.paths,
397
432
  toolPrefix: parsed.toolPrefix,
@@ -412,7 +447,10 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
412
447
  verification: parsed.verification,
413
448
  canonical_paths: parsed.canonical_paths,
414
449
  verification_types: parsed.verification_types,
415
- detection: parsed.detection
450
+ detection: parsed.detection,
451
+ detected: parsed.detected,
452
+ watch: parsed.watch,
453
+ lsp: parsed.lsp
416
454
  };
417
455
  if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
418
456
  _config.cloud = {