@massu/core 0.5.0 → 0.6.1

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 (119) hide show
  1. package/README.md +40 -0
  2. package/agents/massu-architecture-reviewer.md +104 -0
  3. package/agents/massu-blast-radius-analyzer.md +84 -0
  4. package/agents/massu-competitive-scorer.md +126 -0
  5. package/agents/massu-help-sync.md +73 -0
  6. package/agents/massu-migration-writer.md +94 -0
  7. package/agents/massu-output-scorer.md +87 -0
  8. package/agents/massu-pattern-reviewer.md +84 -0
  9. package/agents/massu-plan-auditor.md +170 -0
  10. package/agents/massu-schema-sync-verifier.md +70 -0
  11. package/agents/massu-security-reviewer.md +98 -0
  12. package/agents/massu-ux-reviewer.md +106 -0
  13. package/commands/_shared-preamble.md +53 -23
  14. package/commands/_shared-references/auto-learning-protocol.md +71 -0
  15. package/commands/_shared-references/blast-radius-protocol.md +76 -0
  16. package/commands/_shared-references/security-pre-screen.md +64 -0
  17. package/commands/_shared-references/test-first-protocol.md +87 -0
  18. package/commands/_shared-references/verification-table.md +55 -0
  19. package/commands/massu-article-review.md +343 -0
  20. package/commands/massu-autoresearch/references/eval-runner.md +84 -0
  21. package/commands/massu-autoresearch/references/safety-rails.md +125 -0
  22. package/commands/massu-autoresearch/references/scoring-protocol.md +151 -0
  23. package/commands/massu-autoresearch.md +258 -0
  24. package/commands/massu-batch.md +44 -12
  25. package/commands/massu-bearings.md +42 -8
  26. package/commands/massu-checkpoint.md +588 -0
  27. package/commands/massu-ci-fix.md +2 -2
  28. package/commands/massu-command-health.md +132 -0
  29. package/commands/massu-command-improve.md +232 -0
  30. package/commands/massu-commit.md +205 -44
  31. package/commands/massu-create-plan.md +239 -57
  32. package/commands/massu-data/references/common-queries.md +79 -0
  33. package/commands/massu-data/references/table-guide.md +50 -0
  34. package/commands/massu-data.md +66 -0
  35. package/commands/massu-dead-code.md +29 -34
  36. package/commands/massu-debug/references/auto-learning.md +61 -0
  37. package/commands/massu-debug/references/codegraph-tracing.md +80 -0
  38. package/commands/massu-debug/references/common-shortcuts.md +98 -0
  39. package/commands/massu-debug/references/investigation-phases.md +294 -0
  40. package/commands/massu-debug/references/report-format.md +107 -0
  41. package/commands/massu-debug.md +105 -386
  42. package/commands/massu-docs.md +1 -1
  43. package/commands/massu-full-audit.md +61 -0
  44. package/commands/massu-gap-enhancement-analyzer.md +276 -16
  45. package/commands/massu-golden-path/references/approval-points.md +216 -0
  46. package/commands/massu-golden-path/references/competitive-mode.md +273 -0
  47. package/commands/massu-golden-path/references/error-handling.md +121 -0
  48. package/commands/massu-golden-path/references/phase-0-requirements.md +53 -0
  49. package/commands/massu-golden-path/references/phase-1-plan-creation.md +168 -0
  50. package/commands/massu-golden-path/references/phase-2-implementation.md +403 -0
  51. package/commands/massu-golden-path/references/phase-2.5-gap-analyzer.md +170 -0
  52. package/commands/massu-golden-path/references/phase-3-simplify.md +40 -0
  53. package/commands/massu-golden-path/references/phase-3.5-security-audit.md +108 -0
  54. package/commands/massu-golden-path/references/phase-4-commit.md +94 -0
  55. package/commands/massu-golden-path/references/phase-5-push.md +116 -0
  56. package/commands/massu-golden-path/references/phase-5.5-production-verify.md +170 -0
  57. package/commands/massu-golden-path/references/phase-6-completion.md +113 -0
  58. package/commands/massu-golden-path/references/qa-evaluator-spec.md +137 -0
  59. package/commands/massu-golden-path/references/sprint-contract-protocol.md +117 -0
  60. package/commands/massu-golden-path/references/vr-visual-calibration.md +73 -0
  61. package/commands/massu-golden-path.md +121 -844
  62. package/commands/massu-guide.md +72 -69
  63. package/commands/massu-hooks.md +27 -12
  64. package/commands/massu-hotfix.md +221 -144
  65. package/commands/massu-incident.md +49 -20
  66. package/commands/massu-infra-audit.md +187 -0
  67. package/commands/massu-learning-audit.md +211 -0
  68. package/commands/massu-loop/references/auto-learning.md +49 -0
  69. package/commands/massu-loop/references/checkpoint-audit.md +40 -0
  70. package/commands/massu-loop/references/guardrails.md +17 -0
  71. package/commands/massu-loop/references/iteration-structure.md +115 -0
  72. package/commands/massu-loop/references/loop-controller.md +188 -0
  73. package/commands/massu-loop/references/plan-extraction.md +78 -0
  74. package/commands/massu-loop/references/vr-plan-spec.md +140 -0
  75. package/commands/massu-loop-playwright.md +9 -9
  76. package/commands/massu-loop.md +115 -670
  77. package/commands/massu-new-pattern.md +423 -0
  78. package/commands/massu-perf.md +422 -0
  79. package/commands/massu-plan-audit.md +1 -1
  80. package/commands/massu-plan.md +389 -122
  81. package/commands/massu-production-verify.md +433 -0
  82. package/commands/massu-push.md +62 -378
  83. package/commands/massu-recap.md +29 -3
  84. package/commands/massu-rollback.md +613 -0
  85. package/commands/massu-scaffold-hook.md +2 -4
  86. package/commands/massu-scaffold-page.md +2 -3
  87. package/commands/massu-scaffold-router.md +1 -2
  88. package/commands/massu-security.md +619 -0
  89. package/commands/massu-simplify.md +115 -85
  90. package/commands/massu-squirrels.md +2 -2
  91. package/commands/massu-tdd.md +38 -22
  92. package/commands/massu-test.md +3 -3
  93. package/commands/massu-type-mismatch-audit.md +469 -0
  94. package/commands/massu-ui-audit.md +587 -0
  95. package/commands/massu-verify-playwright.md +287 -32
  96. package/commands/massu-verify.md +150 -46
  97. package/dist/cli.js +146 -95
  98. package/package.json +6 -2
  99. package/patterns/build-patterns.md +302 -0
  100. package/patterns/component-patterns.md +246 -0
  101. package/patterns/display-patterns.md +185 -0
  102. package/patterns/form-patterns.md +890 -0
  103. package/patterns/integration-testing-checklist.md +445 -0
  104. package/patterns/security-patterns.md +219 -0
  105. package/patterns/testing-patterns.md +569 -0
  106. package/patterns/tool-routing.md +81 -0
  107. package/patterns/ui-patterns.md +371 -0
  108. package/protocols/plan-implementation.md +267 -0
  109. package/protocols/recovery.md +225 -0
  110. package/protocols/verification.md +404 -0
  111. package/reference/command-taxonomy.md +178 -0
  112. package/reference/cr-rules-reference.md +76 -0
  113. package/reference/hook-execution-order.md +148 -0
  114. package/reference/lessons-learned.md +175 -0
  115. package/reference/patterns-quickref.md +208 -0
  116. package/reference/standards.md +135 -0
  117. package/reference/subagents-reference.md +17 -0
  118. package/reference/vr-verification-reference.md +867 -0
  119. package/src/commands/install-commands.ts +149 -53
package/dist/cli.js CHANGED
@@ -1397,94 +1397,145 @@ var init_memory_file_ingest = __esm({
1397
1397
  // src/commands/install-commands.ts
1398
1398
  var install_commands_exports = {};
1399
1399
  __export(install_commands_exports, {
1400
+ installAll: () => installAll,
1400
1401
  installCommands: () => installCommands,
1402
+ resolveAssetDir: () => resolveAssetDir,
1401
1403
  resolveCommandsDir: () => resolveCommandsDir,
1402
1404
  runInstallCommands: () => runInstallCommands
1403
1405
  });
1404
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync, mkdirSync as mkdirSync2, readdirSync as readdirSync2 } from "fs";
1406
+ import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync, mkdirSync as mkdirSync2, readdirSync as readdirSync2, statSync } from "fs";
1405
1407
  import { resolve as resolve3, dirname as dirname3 } from "path";
1406
1408
  import { fileURLToPath } from "url";
1407
- function resolveCommandsDir() {
1409
+ function resolveAssetDir(assetName) {
1408
1410
  const cwd = process.cwd();
1409
- const nodeModulesPath = resolve3(cwd, "node_modules/@massu/core/commands");
1411
+ const nodeModulesPath = resolve3(cwd, "node_modules/@massu/core", assetName);
1410
1412
  if (existsSync4(nodeModulesPath)) {
1411
1413
  return nodeModulesPath;
1412
1414
  }
1413
- const distRelPath = resolve3(__dirname, "../commands");
1415
+ const distRelPath = resolve3(__dirname, "..", assetName);
1414
1416
  if (existsSync4(distRelPath)) {
1415
1417
  return distRelPath;
1416
1418
  }
1417
- const srcRelPath = resolve3(__dirname, "../../commands");
1419
+ const srcRelPath = resolve3(__dirname, "../..", assetName);
1418
1420
  if (existsSync4(srcRelPath)) {
1419
1421
  return srcRelPath;
1420
1422
  }
1421
1423
  return null;
1422
1424
  }
1425
+ function resolveCommandsDir() {
1426
+ return resolveAssetDir("commands");
1427
+ }
1428
+ function syncDirectory(sourceDir, targetDir) {
1429
+ const stats = { installed: 0, updated: 0, skipped: 0 };
1430
+ if (!existsSync4(targetDir)) {
1431
+ mkdirSync2(targetDir, { recursive: true });
1432
+ }
1433
+ const entries = readdirSync2(sourceDir);
1434
+ for (const entry of entries) {
1435
+ const sourcePath = resolve3(sourceDir, entry);
1436
+ const targetPath = resolve3(targetDir, entry);
1437
+ const entryStat = statSync(sourcePath);
1438
+ if (entryStat.isDirectory()) {
1439
+ const subStats = syncDirectory(sourcePath, targetPath);
1440
+ stats.installed += subStats.installed;
1441
+ stats.updated += subStats.updated;
1442
+ stats.skipped += subStats.skipped;
1443
+ } else if (entry.endsWith(".md")) {
1444
+ const sourceContent = readFileSync3(sourcePath, "utf-8");
1445
+ if (existsSync4(targetPath)) {
1446
+ const existingContent = readFileSync3(targetPath, "utf-8");
1447
+ if (existingContent === sourceContent) {
1448
+ stats.skipped++;
1449
+ continue;
1450
+ }
1451
+ writeFileSync(targetPath, sourceContent, "utf-8");
1452
+ stats.updated++;
1453
+ } else {
1454
+ writeFileSync(targetPath, sourceContent, "utf-8");
1455
+ stats.installed++;
1456
+ }
1457
+ }
1458
+ }
1459
+ return stats;
1460
+ }
1423
1461
  function installCommands(projectRoot) {
1424
1462
  const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
1425
1463
  const targetDir = resolve3(projectRoot, claudeDirName, "commands");
1426
1464
  if (!existsSync4(targetDir)) {
1427
1465
  mkdirSync2(targetDir, { recursive: true });
1428
1466
  }
1429
- const sourceDir = resolveCommandsDir();
1467
+ const sourceDir = resolveAssetDir("commands");
1430
1468
  if (!sourceDir) {
1431
1469
  console.error(" ERROR: Could not find massu commands directory.");
1432
1470
  console.error(" Try reinstalling: npm install @massu/core");
1433
1471
  return { installed: 0, updated: 0, skipped: 0, commandsDir: targetDir };
1434
1472
  }
1435
- const sourceFiles = readdirSync2(sourceDir).filter((f) => f.endsWith(".md"));
1436
- let installed = 0;
1437
- let updated = 0;
1438
- let skipped = 0;
1439
- for (const file of sourceFiles) {
1440
- const sourcePath = resolve3(sourceDir, file);
1441
- const targetPath = resolve3(targetDir, file);
1442
- const sourceContent = readFileSync3(sourcePath, "utf-8");
1443
- if (existsSync4(targetPath)) {
1444
- const existingContent = readFileSync3(targetPath, "utf-8");
1445
- if (existingContent === sourceContent) {
1446
- skipped++;
1447
- continue;
1448
- }
1449
- writeFileSync(targetPath, sourceContent, "utf-8");
1450
- updated++;
1451
- } else {
1452
- writeFileSync(targetPath, sourceContent, "utf-8");
1453
- installed++;
1473
+ const stats = syncDirectory(sourceDir, targetDir);
1474
+ return { ...stats, commandsDir: targetDir };
1475
+ }
1476
+ function installAll(projectRoot) {
1477
+ const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
1478
+ const claudeDir = resolve3(projectRoot, claudeDirName);
1479
+ const assets = {};
1480
+ let totalInstalled = 0;
1481
+ let totalUpdated = 0;
1482
+ let totalSkipped = 0;
1483
+ for (const assetType of ASSET_TYPES) {
1484
+ const sourceDir = resolveAssetDir(assetType.name);
1485
+ if (!sourceDir) {
1486
+ continue;
1454
1487
  }
1488
+ const targetDir = resolve3(claudeDir, assetType.targetSubdir);
1489
+ const stats = syncDirectory(sourceDir, targetDir);
1490
+ assets[assetType.name] = stats;
1491
+ totalInstalled += stats.installed;
1492
+ totalUpdated += stats.updated;
1493
+ totalSkipped += stats.skipped;
1455
1494
  }
1456
- return { installed, updated, skipped, commandsDir: targetDir };
1495
+ return { assets, totalInstalled, totalUpdated, totalSkipped, claudeDir };
1457
1496
  }
1458
1497
  async function runInstallCommands() {
1459
1498
  const projectRoot = process.cwd();
1460
1499
  console.log("");
1461
- console.log("Massu AI - Install Slash Commands");
1500
+ console.log("Massu AI - Install Project Assets");
1462
1501
  console.log("==================================");
1463
1502
  console.log("");
1464
- const result = installCommands(projectRoot);
1465
- if (result.installed > 0) {
1466
- console.log(` Installed ${result.installed} new commands`);
1467
- }
1468
- if (result.updated > 0) {
1469
- console.log(` Updated ${result.updated} existing commands`);
1470
- }
1471
- if (result.skipped > 0) {
1472
- console.log(` ${result.skipped} commands already up to date`);
1503
+ const result = installAll(projectRoot);
1504
+ for (const assetType of ASSET_TYPES) {
1505
+ const stats = result.assets[assetType.name];
1506
+ if (!stats) {
1507
+ continue;
1508
+ }
1509
+ const total = stats.installed + stats.updated + stats.skipped;
1510
+ if (total === 0) continue;
1511
+ const parts = [];
1512
+ if (stats.installed > 0) parts.push(`${stats.installed} new`);
1513
+ if (stats.updated > 0) parts.push(`${stats.updated} updated`);
1514
+ if (stats.skipped > 0) parts.push(`${stats.skipped} current`);
1515
+ const description = assetType.description;
1516
+ console.log(` ${description}: ${parts.join(", ")} (${total} total)`);
1473
1517
  }
1474
- const total = result.installed + result.updated + result.skipped;
1518
+ const grandTotal = result.totalInstalled + result.totalUpdated + result.totalSkipped;
1475
1519
  console.log("");
1476
- console.log(` ${total} slash commands available in ${result.commandsDir}`);
1520
+ console.log(` ${grandTotal} total files synced to ${result.claudeDir}`);
1477
1521
  console.log("");
1478
1522
  console.log(" Restart your Claude Code session to use them.");
1479
1523
  console.log("");
1480
1524
  }
1481
- var __filename, __dirname;
1525
+ var __filename, __dirname, ASSET_TYPES;
1482
1526
  var init_install_commands = __esm({
1483
1527
  "src/commands/install-commands.ts"() {
1484
1528
  "use strict";
1485
1529
  init_config();
1486
1530
  __filename = fileURLToPath(import.meta.url);
1487
1531
  __dirname = dirname3(__filename);
1532
+ ASSET_TYPES = [
1533
+ { name: "commands", targetSubdir: "commands", description: "slash commands" },
1534
+ { name: "agents", targetSubdir: "agents", description: "agent definitions" },
1535
+ { name: "patterns", targetSubdir: "patterns", description: "pattern files" },
1536
+ { name: "protocols", targetSubdir: "protocols", description: "protocol files" },
1537
+ { name: "reference", targetSubdir: "reference", description: "reference files" }
1538
+ ];
1488
1539
  }
1489
1540
  });
1490
1541
 
@@ -2656,8 +2707,8 @@ var init_install_hooks = __esm({
2656
2707
 
2657
2708
  // src/db.ts
2658
2709
  import Database2 from "better-sqlite3";
2659
- import { dirname as dirname6, join as join2 } from "path";
2660
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, readdirSync as readdirSync5, statSync } from "fs";
2710
+ import { dirname as dirname6, join as join3 } from "path";
2711
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
2661
2712
  function getCodeGraphDb() {
2662
2713
  const dbPath = getResolvedPaths().codegraphDbPath;
2663
2714
  if (!existsSync7(dbPath)) {
@@ -2943,12 +2994,12 @@ function isPythonDataStale(dataDb2, pythonRoot) {
2943
2994
  try {
2944
2995
  const entries = readdirSync5(dir, { withFileTypes: true });
2945
2996
  for (const entry of entries) {
2946
- const fullPath = join2(dir, entry.name);
2997
+ const fullPath = join3(dir, entry.name);
2947
2998
  if (entry.isDirectory()) {
2948
2999
  if (["__pycache__", ".venv", "venv", "node_modules", ".mypy_cache", ".pytest_cache"].includes(entry.name)) continue;
2949
3000
  if (checkDir(fullPath)) return true;
2950
3001
  } else if (entry.name.endsWith(".py")) {
2951
- if (statSync(fullPath).mtimeMs > lastBuildTime) return true;
3002
+ if (statSync2(fullPath).mtimeMs > lastBuildTime) return true;
2952
3003
  }
2953
3004
  }
2954
3005
  } catch {
@@ -3044,8 +3095,8 @@ var init_rules = __esm({
3044
3095
  });
3045
3096
 
3046
3097
  // src/import-resolver.ts
3047
- import { readFileSync as readFileSync6, existsSync as existsSync8, statSync as statSync2 } from "fs";
3048
- import { resolve as resolve7, dirname as dirname7, join as join3 } from "path";
3098
+ import { readFileSync as readFileSync6, existsSync as existsSync8, statSync as statSync3 } from "fs";
3099
+ import { resolve as resolve7, dirname as dirname7, join as join4 } from "path";
3049
3100
  function parseImports(source) {
3050
3101
  const imports = [];
3051
3102
  const lines = source.split("\n");
@@ -3116,7 +3167,7 @@ function resolveImportPath(specifier, fromFile) {
3116
3167
  }
3117
3168
  }
3118
3169
  for (const indexFile of resolvedPaths.indexFiles) {
3119
- const indexPath = join3(basePath, indexFile);
3170
+ const indexPath = join4(basePath, indexFile);
3120
3171
  if (existsSync8(indexPath)) {
3121
3172
  return toRelative(indexPath);
3122
3173
  }
@@ -3125,7 +3176,7 @@ function resolveImportPath(specifier, fromFile) {
3125
3176
  }
3126
3177
  function isDirectory(path) {
3127
3178
  try {
3128
- return statSync2(path).isDirectory();
3179
+ return statSync3(path).isDirectory();
3129
3180
  } catch {
3130
3181
  return false;
3131
3182
  }
@@ -3194,7 +3245,7 @@ var init_import_resolver = __esm({
3194
3245
 
3195
3246
  // src/trpc-index.ts
3196
3247
  import { readFileSync as readFileSync7, existsSync as existsSync9, readdirSync as readdirSync6 } from "fs";
3197
- import { resolve as resolve8, join as join4 } from "path";
3248
+ import { resolve as resolve8, join as join5 } from "path";
3198
3249
  function parseRootRouter() {
3199
3250
  const paths = getResolvedPaths();
3200
3251
  const rootPath = paths.rootRouterPath;
@@ -3217,7 +3268,7 @@ function parseRootRouter() {
3217
3268
  filePath = routersRelPath + "/" + filePath + ext;
3218
3269
  break;
3219
3270
  }
3220
- const indexCandidate = join4(fullPath, "index.ts");
3271
+ const indexCandidate = join5(fullPath, "index.ts");
3221
3272
  if (existsSync9(indexCandidate)) {
3222
3273
  filePath = routersRelPath + "/" + filePath + "/index.ts";
3223
3274
  break;
@@ -3276,7 +3327,7 @@ function findUICallSites(routerKey, procedureName) {
3276
3327
  function searchDirectory(dir, pattern, results) {
3277
3328
  const entries = readdirSync6(dir, { withFileTypes: true });
3278
3329
  for (const entry of entries) {
3279
- const fullPath = join4(dir, entry.name);
3330
+ const fullPath = join5(dir, entry.name);
3280
3331
  if (entry.isDirectory()) {
3281
3332
  if (entry.name === "node_modules" || entry.name === ".next") continue;
3282
3333
  searchDirectory(fullPath, pattern, results);
@@ -3678,7 +3729,7 @@ var init_domains = __esm({
3678
3729
 
3679
3730
  // src/schema-mapper.ts
3680
3731
  import { readFileSync as readFileSync9, existsSync as existsSync11, readdirSync as readdirSync7 } from "fs";
3681
- import { join as join5 } from "path";
3732
+ import { join as join6 } from "path";
3682
3733
  function parsePrismaSchema() {
3683
3734
  const schemaPath = getResolvedPaths().prismaSchemaPath;
3684
3735
  if (!existsSync11(schemaPath)) {
@@ -3749,7 +3800,7 @@ function findColumnUsageInRouters(tableName) {
3749
3800
  function scanDirectory(dir, tableName, usage) {
3750
3801
  const entries = readdirSync7(dir, { withFileTypes: true });
3751
3802
  for (const entry of entries) {
3752
- const fullPath = join5(dir, entry.name);
3803
+ const fullPath = join6(dir, entry.name);
3753
3804
  if (entry.isDirectory()) {
3754
3805
  scanDirectory(fullPath, tableName, usage);
3755
3806
  } else if (entry.name.endsWith(".ts")) {
@@ -3807,7 +3858,7 @@ function findFilesUsingColumn(dir, column, tableName) {
3807
3858
  if (!existsSync11(dir)) return result;
3808
3859
  const entries = readdirSync7(dir, { withFileTypes: true });
3809
3860
  for (const entry of entries) {
3810
- const fullPath = join5(dir, entry.name);
3861
+ const fullPath = join6(dir, entry.name);
3811
3862
  if (entry.isDirectory()) {
3812
3863
  result.push(...findFilesUsingColumn(fullPath, column, tableName));
3813
3864
  } else if (entry.name.endsWith(".ts")) {
@@ -3958,7 +4009,7 @@ var init_import_parser = __esm({
3958
4009
 
3959
4010
  // src/python/import-resolver.ts
3960
4011
  import { readFileSync as readFileSync10, existsSync as existsSync12, readdirSync as readdirSync8 } from "fs";
3961
- import { resolve as resolve11, join as join6, relative, dirname as dirname8 } from "path";
4012
+ import { resolve as resolve11, join as join7, relative as relative2, dirname as dirname8 } from "path";
3962
4013
  function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
3963
4014
  const projectRoot = getProjectRoot();
3964
4015
  if (level > 0) {
@@ -3969,23 +4020,23 @@ function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
3969
4020
  const modulePart = module.replace(/^\.+/, "");
3970
4021
  if (modulePart) {
3971
4022
  const parts2 = modulePart.split(".");
3972
- return tryResolvePythonPath(join6(baseDir, ...parts2), projectRoot);
4023
+ return tryResolvePythonPath(join7(baseDir, ...parts2), projectRoot);
3973
4024
  }
3974
4025
  return tryResolvePythonPath(baseDir, projectRoot);
3975
4026
  }
3976
4027
  const parts = module.split(".");
3977
- const candidate = join6(resolve11(projectRoot, pythonRoot), ...parts);
4028
+ const candidate = join7(resolve11(projectRoot, pythonRoot), ...parts);
3978
4029
  return tryResolvePythonPath(candidate, projectRoot);
3979
4030
  }
3980
4031
  function tryResolvePythonPath(basePath, projectRoot) {
3981
4032
  if (existsSync12(basePath + ".py")) {
3982
- return relative(projectRoot, basePath + ".py");
4033
+ return relative2(projectRoot, basePath + ".py");
3983
4034
  }
3984
- if (existsSync12(join6(basePath, "__init__.py"))) {
3985
- return relative(projectRoot, join6(basePath, "__init__.py"));
4035
+ if (existsSync12(join7(basePath, "__init__.py"))) {
4036
+ return relative2(projectRoot, join7(basePath, "__init__.py"));
3986
4037
  }
3987
4038
  if (basePath.endsWith(".py") && existsSync12(basePath)) {
3988
- return relative(projectRoot, basePath);
4039
+ return relative2(projectRoot, basePath);
3989
4040
  }
3990
4041
  return null;
3991
4042
  }
@@ -3996,9 +4047,9 @@ function walkPythonFiles(dir, excludeDirs) {
3996
4047
  for (const entry of entries) {
3997
4048
  if (entry.isDirectory()) {
3998
4049
  if (excludeDirs.includes(entry.name)) continue;
3999
- files.push(...walkPythonFiles(join6(dir, entry.name), excludeDirs));
4050
+ files.push(...walkPythonFiles(join7(dir, entry.name), excludeDirs));
4000
4051
  } else if (entry.name.endsWith(".py")) {
4001
- files.push(join6(dir, entry.name));
4052
+ files.push(join7(dir, entry.name));
4002
4053
  }
4003
4054
  }
4004
4055
  } catch {
@@ -4021,7 +4072,7 @@ function buildPythonImportIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__
4021
4072
  });
4022
4073
  const batch = [];
4023
4074
  for (const absFile of files) {
4024
- const relFile = relative(projectRoot, absFile);
4075
+ const relFile = relative2(projectRoot, absFile);
4025
4076
  let source;
4026
4077
  try {
4027
4078
  source = readFileSync10(absFile, "utf-8");
@@ -4291,7 +4342,7 @@ var init_route_parser = __esm({
4291
4342
 
4292
4343
  // src/python/route-indexer.ts
4293
4344
  import { readFileSync as readFileSync11, readdirSync as readdirSync9 } from "fs";
4294
- import { join as join7, relative as relative2 } from "path";
4345
+ import { join as join8, relative as relative3 } from "path";
4295
4346
  function walkPyFiles(dir, excludeDirs) {
4296
4347
  const files = [];
4297
4348
  try {
@@ -4299,9 +4350,9 @@ function walkPyFiles(dir, excludeDirs) {
4299
4350
  for (const entry of entries) {
4300
4351
  if (entry.isDirectory()) {
4301
4352
  if (excludeDirs.includes(entry.name)) continue;
4302
- files.push(...walkPyFiles(join7(dir, entry.name), excludeDirs));
4353
+ files.push(...walkPyFiles(join8(dir, entry.name), excludeDirs));
4303
4354
  } else if (entry.name.endsWith(".py")) {
4304
- files.push(join7(dir, entry.name));
4355
+ files.push(join8(dir, entry.name));
4305
4356
  }
4306
4357
  }
4307
4358
  } catch {
@@ -4310,7 +4361,7 @@ function walkPyFiles(dir, excludeDirs) {
4310
4361
  }
4311
4362
  function buildPythonRouteIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
4312
4363
  const projectRoot = getProjectRoot();
4313
- const absRoot = join7(projectRoot, pythonRoot);
4364
+ const absRoot = join8(projectRoot, pythonRoot);
4314
4365
  dataDb2.exec("DELETE FROM massu_py_routes");
4315
4366
  dataDb2.exec("DELETE FROM massu_py_route_callers");
4316
4367
  const insertStmt = dataDb2.prepare(
@@ -4320,7 +4371,7 @@ function buildPythonRouteIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__"
4320
4371
  let count = 0;
4321
4372
  dataDb2.transaction(() => {
4322
4373
  for (const absFile of files) {
4323
- const relFile = relative2(projectRoot, absFile);
4374
+ const relFile = relative3(projectRoot, absFile);
4324
4375
  let source;
4325
4376
  try {
4326
4377
  source = readFileSync11(absFile, "utf-8");
@@ -4535,7 +4586,7 @@ var init_model_parser = __esm({
4535
4586
 
4536
4587
  // src/python/model-indexer.ts
4537
4588
  import { readFileSync as readFileSync12, readdirSync as readdirSync10 } from "fs";
4538
- import { join as join8, relative as relative3 } from "path";
4589
+ import { join as join9, relative as relative4 } from "path";
4539
4590
  function walkPyFiles2(dir, excludeDirs) {
4540
4591
  const files = [];
4541
4592
  try {
@@ -4543,9 +4594,9 @@ function walkPyFiles2(dir, excludeDirs) {
4543
4594
  for (const entry of entries) {
4544
4595
  if (entry.isDirectory()) {
4545
4596
  if (excludeDirs.includes(entry.name)) continue;
4546
- files.push(...walkPyFiles2(join8(dir, entry.name), excludeDirs));
4597
+ files.push(...walkPyFiles2(join9(dir, entry.name), excludeDirs));
4547
4598
  } else if (entry.name.endsWith(".py")) {
4548
- files.push(join8(dir, entry.name));
4599
+ files.push(join9(dir, entry.name));
4549
4600
  }
4550
4601
  }
4551
4602
  } catch {
@@ -4554,7 +4605,7 @@ function walkPyFiles2(dir, excludeDirs) {
4554
4605
  }
4555
4606
  function buildPythonModelIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
4556
4607
  const projectRoot = getProjectRoot();
4557
- const absRoot = join8(projectRoot, pythonRoot);
4608
+ const absRoot = join9(projectRoot, pythonRoot);
4558
4609
  dataDb2.exec("DELETE FROM massu_py_models");
4559
4610
  dataDb2.exec("DELETE FROM massu_py_fk_edges");
4560
4611
  const insertModel = dataDb2.prepare(
@@ -4567,7 +4618,7 @@ function buildPythonModelIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__"
4567
4618
  let count = 0;
4568
4619
  dataDb2.transaction(() => {
4569
4620
  for (const absFile of files) {
4570
- const relFile = relative3(projectRoot, absFile);
4621
+ const relFile = relative4(projectRoot, absFile);
4571
4622
  let source;
4572
4623
  try {
4573
4624
  source = readFileSync12(absFile, "utf-8");
@@ -4831,18 +4882,18 @@ var init_migration_parser = __esm({
4831
4882
 
4832
4883
  // src/python/migration-indexer.ts
4833
4884
  import { readFileSync as readFileSync13, readdirSync as readdirSync11 } from "fs";
4834
- import { join as join9, relative as relative4 } from "path";
4885
+ import { join as join10, relative as relative5 } from "path";
4835
4886
  function buildPythonMigrationIndex(dataDb2, alembicDir) {
4836
4887
  const projectRoot = getProjectRoot();
4837
- const absDir = join9(projectRoot, alembicDir);
4888
+ const absDir = join10(projectRoot, alembicDir);
4838
4889
  dataDb2.exec("DELETE FROM massu_py_migrations");
4839
- const versionsDir = join9(absDir, "versions");
4890
+ const versionsDir = join10(absDir, "versions");
4840
4891
  let files = [];
4841
4892
  try {
4842
- files = readdirSync11(versionsDir).filter((f) => f.endsWith(".py")).map((f) => join9(versionsDir, f));
4893
+ files = readdirSync11(versionsDir).filter((f) => f.endsWith(".py")).map((f) => join10(versionsDir, f));
4843
4894
  } catch {
4844
4895
  try {
4845
- files = readdirSync11(absDir).filter((f) => f.endsWith(".py") && f !== "env.py").map((f) => join9(absDir, f));
4896
+ files = readdirSync11(absDir).filter((f) => f.endsWith(".py") && f !== "env.py").map((f) => join10(absDir, f));
4846
4897
  } catch {
4847
4898
  }
4848
4899
  }
@@ -4867,7 +4918,7 @@ function buildPythonMigrationIndex(dataDb2, alembicDir) {
4867
4918
  rows.push({
4868
4919
  revision: parsed.revision,
4869
4920
  downRevision: parsed.downRevision,
4870
- file: relative4(projectRoot, absFile),
4921
+ file: relative5(projectRoot, absFile),
4871
4922
  description: parsed.description,
4872
4923
  operations: JSON.stringify(parsed.operations)
4873
4924
  });
@@ -4891,11 +4942,11 @@ var init_migration_indexer = __esm({
4891
4942
 
4892
4943
  // src/python/coupling-detector.ts
4893
4944
  import { readFileSync as readFileSync14, readdirSync as readdirSync12 } from "fs";
4894
- import { join as join10, relative as relative5 } from "path";
4945
+ import { join as join11, relative as relative6 } from "path";
4895
4946
  function buildPythonCouplingIndex(dataDb2) {
4896
4947
  const projectRoot = getProjectRoot();
4897
4948
  const config = getConfig();
4898
- const srcDir = join10(projectRoot, config.paths.source);
4949
+ const srcDir = join11(projectRoot, config.paths.source);
4899
4950
  const routes = dataDb2.prepare("SELECT id, method, path FROM massu_py_routes").all();
4900
4951
  if (routes.length === 0) return 0;
4901
4952
  dataDb2.exec("DELETE FROM massu_py_route_callers");
@@ -4924,7 +4975,7 @@ function buildPythonCouplingIndex(dataDb2) {
4924
4975
  ];
4925
4976
  dataDb2.transaction(() => {
4926
4977
  for (const absFile of frontendFiles) {
4927
- const relFile = relative5(projectRoot, absFile);
4978
+ const relFile = relative6(projectRoot, absFile);
4928
4979
  let source;
4929
4980
  try {
4930
4981
  source = readFileSync14(absFile, "utf-8");
@@ -4959,9 +5010,9 @@ function walkFrontendFiles(dir) {
4959
5010
  for (const entry of entries) {
4960
5011
  if (entry.isDirectory()) {
4961
5012
  if (exclude.includes(entry.name)) continue;
4962
- files.push(...walkFrontendFiles(join10(dir, entry.name)));
5013
+ files.push(...walkFrontendFiles(join11(dir, entry.name)));
4963
5014
  } else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
4964
- files.push(join10(dir, entry.name));
5015
+ files.push(join11(dir, entry.name));
4965
5016
  }
4966
5017
  }
4967
5018
  } catch {
@@ -6720,8 +6771,8 @@ var init_sentinel_tools = __esm({
6720
6771
  });
6721
6772
 
6722
6773
  // src/sentinel-scanner.ts
6723
- import { readFileSync as readFileSync16, existsSync as existsSync15, readdirSync as readdirSync13, statSync as statSync3 } from "fs";
6724
- import { resolve as resolve14, join as join11, basename as basename4, dirname as dirname9, relative as relative6 } from "path";
6774
+ import { readFileSync as readFileSync16, existsSync as existsSync15, readdirSync as readdirSync13, statSync as statSync4 } from "fs";
6775
+ import { resolve as resolve14, join as join12, basename as basename4, dirname as dirname9, relative as relative7 } from "path";
6725
6776
  function inferDomain(filePath) {
6726
6777
  const domains = getConfig().domains;
6727
6778
  const path = filePath.toLowerCase();
@@ -6867,7 +6918,7 @@ function scanComponentExports(dataDb2) {
6867
6918
  if (!existsSync15(absDir)) continue;
6868
6919
  const files = walkDir(absDir).filter((f) => f.endsWith(".tsx") || f.endsWith(".ts"));
6869
6920
  for (const file of files) {
6870
- const relPath = relative6(projectRoot, file);
6921
+ const relPath = relative7(projectRoot, file);
6871
6922
  const source = readFileSync16(file, "utf-8");
6872
6923
  const annotations = parseFeatureAnnotations(source);
6873
6924
  if (annotations.length > 0) {
@@ -6919,9 +6970,9 @@ function walkDir(dir) {
6919
6970
  try {
6920
6971
  const entries = readdirSync13(dir);
6921
6972
  for (const entry of entries) {
6922
- const fullPath = join11(dir, entry);
6973
+ const fullPath = join12(dir, entry);
6923
6974
  try {
6924
- const stat = statSync3(fullPath);
6975
+ const stat = statSync4(fullPath);
6925
6976
  if (stat.isDirectory()) {
6926
6977
  results.push(...walkDir(fullPath));
6927
6978
  } else {
@@ -9349,8 +9400,8 @@ var init_regression_detector = __esm({
9349
9400
 
9350
9401
  // src/knowledge-indexer.ts
9351
9402
  import { createHash as createHash2 } from "crypto";
9352
- import { readFileSync as readFileSync20, readdirSync as readdirSync14, statSync as statSync4, existsSync as existsSync19 } from "fs";
9353
- import { resolve as resolve16, relative as relative7, basename as basename5, extname } from "path";
9403
+ import { readFileSync as readFileSync20, readdirSync as readdirSync14, statSync as statSync5, existsSync as existsSync19 } from "fs";
9404
+ import { resolve as resolve16, relative as relative8, basename as basename5, extname } from "path";
9354
9405
  function getKnowledgePaths() {
9355
9406
  const resolved = getResolvedPaths();
9356
9407
  const config = getConfig();
@@ -9398,7 +9449,7 @@ function categorizeFile(filePath) {
9398
9449
  const paths = getKnowledgePaths();
9399
9450
  if (filePath.startsWith(paths.plansDir)) return "plan";
9400
9451
  if (filePath.startsWith(paths.docsDir)) {
9401
- const relFromDocs = relative7(paths.docsDir, filePath).replace(/\\/g, "/").toLowerCase();
9452
+ const relFromDocs = relative8(paths.docsDir, filePath).replace(/\\/g, "/").toLowerCase();
9402
9453
  if (relFromDocs.startsWith("plans/")) return "plan";
9403
9454
  if (relFromDocs.includes("architecture")) return "architecture";
9404
9455
  if (relFromDocs.includes("security")) return "security";
@@ -9414,7 +9465,7 @@ function categorizeFile(filePath) {
9414
9465
  }
9415
9466
  const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
9416
9467
  if (filePath.includes(`${claudeDirName}/projects/`) && filePath.includes("/memory/")) return "memory";
9417
- const rel = relative7(paths.claudeDir, filePath).replace(/\\/g, "/");
9468
+ const rel = relative8(paths.claudeDir, filePath).replace(/\\/g, "/");
9418
9469
  const firstDir = rel.split("/")[0];
9419
9470
  const knownCategories = getConfig().conventions?.knowledgeCategories ?? [
9420
9471
  "patterns",
@@ -9706,7 +9757,7 @@ function indexAllKnowledge(db) {
9706
9757
  if (!existsSync19(filePath)) continue;
9707
9758
  const content = readFileSync20(filePath, "utf-8");
9708
9759
  const hash = hashContent(content);
9709
- const relPath = filePath.startsWith(paths.claudeDir) ? relative7(paths.claudeDir, filePath) : filePath.startsWith(paths.plansDir) ? "plans/" + relative7(paths.plansDir, filePath) : filePath.startsWith(paths.docsDir) ? "docs/" + relative7(paths.docsDir, filePath) : filePath.startsWith(paths.memoryDir) ? `memory/${relative7(paths.memoryDir, filePath)}` : basename5(filePath);
9760
+ const relPath = filePath.startsWith(paths.claudeDir) ? relative8(paths.claudeDir, filePath) : filePath.startsWith(paths.plansDir) ? "plans/" + relative8(paths.plansDir, filePath) : filePath.startsWith(paths.docsDir) ? "docs/" + relative8(paths.docsDir, filePath) : filePath.startsWith(paths.memoryDir) ? `memory/${relative8(paths.memoryDir, filePath)}` : basename5(filePath);
9710
9761
  const category = categorizeFile(filePath);
9711
9762
  const title = extractTitle(content, filePath);
9712
9763
  const description = extractDescription2(content);
@@ -9834,7 +9885,7 @@ function isKnowledgeStale(db) {
9834
9885
  }
9835
9886
  for (const filePath of files) {
9836
9887
  try {
9837
- const stat = statSync4(filePath);
9888
+ const stat = statSync5(filePath);
9838
9889
  if (stat.mtimeMs > lastIndexTime) return true;
9839
9890
  } catch {
9840
9891
  continue;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@massu/core",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "type": "module",
5
- "description": "AI Engineering Governance MCP Server - Session memory, knowledge system, feature registry, code intelligence, rule enforcement, tiered tooling (12 free / 72 total), 43 workflow commands",
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"
@@ -32,6 +32,10 @@
32
32
  "!src/__tests__/**",
33
33
  "dist/**/*",
34
34
  "commands/**/*",
35
+ "agents/**/*",
36
+ "patterns/**/*",
37
+ "protocols/**/*",
38
+ "reference/**/*",
35
39
  "LICENSE"
36
40
  ],
37
41
  "keywords": [