@massu/core 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1485,20 +1485,25 @@ var init_memory_db = __esm({
1485
1485
  // src/memory-file-ingest.ts
1486
1486
  import { readFileSync as readFileSync2, existsSync as existsSync3, readdirSync } from "fs";
1487
1487
  import { join } from "path";
1488
- import { parse as parseYaml2 } from "yaml";
1489
1488
  function ingestMemoryFile(db, sessionId, filePath) {
1490
1489
  if (!existsSync3(filePath)) return "skipped";
1491
1490
  const content = readFileSync2(filePath, "utf-8");
1492
- const basename8 = (filePath.split("/").pop() ?? "").replace(".md", "");
1491
+ const basename9 = (filePath.split("/").pop() ?? "").replace(".md", "");
1493
1492
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
1494
- let name = basename8;
1493
+ let name = basename9;
1495
1494
  let description = "";
1496
1495
  let type = "discovery";
1497
1496
  let confidence;
1498
1497
  if (frontmatterMatch) {
1499
1498
  try {
1500
- const fm = parseYaml2(frontmatterMatch[1]);
1501
- name = fm.name ?? basename8;
1499
+ const fm = {};
1500
+ for (const line of frontmatterMatch[1].split("\n")) {
1501
+ const sep = line.indexOf(":");
1502
+ if (sep > 0) {
1503
+ fm[line.slice(0, sep).trim()] = line.slice(sep + 1).trim();
1504
+ }
1505
+ }
1506
+ name = fm.name ?? basename9;
1502
1507
  description = fm.description ?? "";
1503
1508
  type = fm.type ?? "discovery";
1504
1509
  confidence = fm.confidence != null ? Number(fm.confidence) : void 0;
@@ -1558,6 +1563,265 @@ var init_memory_file_ingest = __esm({
1558
1563
  }
1559
1564
  });
1560
1565
 
1566
+ // src/claude-md-templates.ts
1567
+ import { readdirSync as readdirSync2, statSync } from "fs";
1568
+ import { resolve as resolve3, basename as basename2 } from "path";
1569
+ function scanDirectoryStructure(projectRoot, maxDepth = 2) {
1570
+ const lines = [];
1571
+ const rootName = basename2(projectRoot);
1572
+ lines.push(`${rootName}/`);
1573
+ scanLevel(projectRoot, "", maxDepth, 0, lines);
1574
+ return lines.join("\n");
1575
+ }
1576
+ function scanLevel(dir, prefix2, maxDepth, currentDepth, lines) {
1577
+ if (currentDepth >= maxDepth) return;
1578
+ let entries;
1579
+ try {
1580
+ entries = readdirSync2(dir).sort();
1581
+ } catch {
1582
+ return;
1583
+ }
1584
+ const dirs = [];
1585
+ const files = [];
1586
+ for (const entry of entries) {
1587
+ if (entry.startsWith(".") && EXCLUDED_DIRS.has(entry)) continue;
1588
+ if (EXCLUDED_DIRS.has(entry)) continue;
1589
+ try {
1590
+ const stat = statSync(resolve3(dir, entry));
1591
+ if (stat.isDirectory()) dirs.push(entry);
1592
+ else files.push(entry);
1593
+ } catch {
1594
+ }
1595
+ }
1596
+ const allEntries = [...dirs, ...files];
1597
+ for (let i = 0; i < allEntries.length; i++) {
1598
+ const entry = allEntries[i];
1599
+ const isLast = i === allEntries.length - 1;
1600
+ const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
1601
+ const childPrefix = isLast ? " " : "\u2502 ";
1602
+ const isDir = dirs.includes(entry);
1603
+ lines.push(`${prefix2}${connector}${entry}${isDir ? "/" : ""}`);
1604
+ if (isDir) {
1605
+ scanLevel(resolve3(dir, entry), prefix2 + childPrefix, maxDepth, currentDepth + 1, lines);
1606
+ }
1607
+ }
1608
+ }
1609
+ function buildClaudeMdContent(projectName, projectRoot, framework, python) {
1610
+ const sections = [];
1611
+ sections.push(buildProjectOverview(projectName, framework, python));
1612
+ sections.push(buildTechStack(framework, python));
1613
+ sections.push(buildDirectorySection(projectRoot));
1614
+ sections.push(buildCodingConventions(framework, python));
1615
+ sections.push(buildTestingSection(framework, python));
1616
+ sections.push(buildMassuWorkflow());
1617
+ sections.push(buildMemorySystem());
1618
+ sections.push(buildCriticalRules());
1619
+ return sections.join("\n\n---\n\n") + "\n";
1620
+ }
1621
+ function buildProjectOverview(projectName, framework, python) {
1622
+ const stack = [];
1623
+ if (framework.type !== "javascript") stack.push(capitalize(framework.type));
1624
+ if (framework.ui !== "none") stack.push(formatUiName(framework.ui));
1625
+ if (framework.router !== "none") stack.push(framework.router.toUpperCase());
1626
+ if (framework.orm !== "none") stack.push(capitalize(framework.orm));
1627
+ if (python.detected) {
1628
+ stack.push("Python");
1629
+ if (python.hasFastapi) stack.push("FastAPI");
1630
+ }
1631
+ const stackStr = stack.length > 0 ? stack.join(", ") : "JavaScript";
1632
+ return `# ${projectName}
1633
+
1634
+ ## Project Overview
1635
+
1636
+ ${projectName} is a ${stackStr} project.
1637
+
1638
+ <!-- Add a brief description of what this project does -->`;
1639
+ }
1640
+ function buildTechStack(framework, python) {
1641
+ const rows = [];
1642
+ rows.push("| Technology | Details |");
1643
+ rows.push("|-----------|---------|");
1644
+ rows.push(`| Language | ${capitalize(framework.type)} |`);
1645
+ if (framework.ui !== "none") rows.push(`| UI Framework | ${formatUiName(framework.ui)} |`);
1646
+ if (framework.router !== "none") rows.push(`| Router/API | ${framework.router.toUpperCase()} |`);
1647
+ if (framework.orm !== "none") rows.push(`| ORM | ${capitalize(framework.orm)} |`);
1648
+ if (python.detected) {
1649
+ rows.push("| Python | Yes |");
1650
+ if (python.hasFastapi) rows.push("| Python Framework | FastAPI |");
1651
+ if (python.hasSqlalchemy) rows.push("| Python ORM | SQLAlchemy |");
1652
+ if (python.hasAlembic) rows.push("| Migrations | Alembic |");
1653
+ }
1654
+ return `## Tech Stack
1655
+
1656
+ ${rows.join("\n")}`;
1657
+ }
1658
+ function buildDirectorySection(projectRoot) {
1659
+ const tree = scanDirectoryStructure(projectRoot);
1660
+ return `## Directory Structure
1661
+
1662
+ \`\`\`
1663
+ ${tree}
1664
+ \`\`\``;
1665
+ }
1666
+ function buildCodingConventions(framework, python) {
1667
+ const rules = [];
1668
+ if (framework.type === "typescript") {
1669
+ rules.push("- Use ESM imports (`import`), not CommonJS (`require`)");
1670
+ rules.push("- Enable strict TypeScript (`strict: true` in tsconfig.json)");
1671
+ rules.push("- Prefer explicit types over `any`");
1672
+ }
1673
+ switch (framework.ui) {
1674
+ case "nextjs":
1675
+ rules.push("- Use App Router conventions (`app/` directory)");
1676
+ rules.push('- Default to Server Components; add `"use client"` only when needed');
1677
+ rules.push("- Use `next/image` for images, `next/link` for navigation");
1678
+ rules.push("- API routes go in `app/api/` using Route Handlers");
1679
+ break;
1680
+ case "sveltekit":
1681
+ rules.push("- Use load functions for data fetching (`+page.server.ts`)");
1682
+ rules.push("- Use form actions for mutations");
1683
+ rules.push("- Server-only code in `+server.ts` files");
1684
+ break;
1685
+ case "react":
1686
+ rules.push("- Prefer functional components with hooks");
1687
+ rules.push("- Colocate component, styles, and tests");
1688
+ break;
1689
+ }
1690
+ if (framework.router === "trpc") {
1691
+ rules.push("- Define tRPC routers with Zod input validation");
1692
+ rules.push("- Keep router files focused (one domain per router)");
1693
+ }
1694
+ if (framework.orm === "prisma") {
1695
+ rules.push("- Define models in `prisma/schema.prisma`");
1696
+ rules.push("- Run `npx prisma generate` after schema changes");
1697
+ } else if (framework.orm === "drizzle") {
1698
+ rules.push("- Define schemas with Drizzle table builders");
1699
+ rules.push("- Run migrations with `drizzle-kit`");
1700
+ }
1701
+ if (python.detected) {
1702
+ rules.push("- Use type hints for function signatures");
1703
+ rules.push("- Use `async def` for async endpoints");
1704
+ if (python.hasFastapi) {
1705
+ rules.push("- Use Pydantic models for request/response schemas");
1706
+ rules.push("- Organize routes with `APIRouter`");
1707
+ }
1708
+ if (python.hasSqlalchemy) {
1709
+ rules.push("- Use SQLAlchemy 2.0 style (select/insert builders)");
1710
+ }
1711
+ }
1712
+ if (rules.length === 0) {
1713
+ rules.push("- Follow consistent naming conventions");
1714
+ rules.push("- Keep functions small and focused");
1715
+ }
1716
+ return `## Coding Conventions
1717
+
1718
+ ${rules.join("\n")}`;
1719
+ }
1720
+ function buildTestingSection(framework, python) {
1721
+ const lines = [];
1722
+ if (framework.type === "typescript") {
1723
+ lines.push("- Test framework: vitest (or jest)");
1724
+ lines.push("- Test files: `__tests__/*.test.ts` or `*.test.ts` colocated");
1725
+ lines.push("- Run tests: `npm test`");
1726
+ }
1727
+ if (python.detected) {
1728
+ lines.push("- Python tests: pytest");
1729
+ lines.push("- Test files: `tests/` directory or `test_*.py` files");
1730
+ lines.push("- Run: `pytest`");
1731
+ }
1732
+ if (lines.length === 0) {
1733
+ lines.push("- Configure a test framework for this project");
1734
+ lines.push("- Run tests before committing changes");
1735
+ }
1736
+ return `## Testing
1737
+
1738
+ ${lines.join("\n")}`;
1739
+ }
1740
+ function buildMassuWorkflow() {
1741
+ return `## Massu Workflow
1742
+
1743
+ This project uses [Massu AI](https://massu.ai) for development governance.
1744
+
1745
+ ### Common Commands
1746
+
1747
+ | Command | Purpose |
1748
+ |---------|---------|
1749
+ | \`/massu-create-plan\` | Create an implementation plan |
1750
+ | \`/massu-plan\` | Audit and improve a plan |
1751
+ | \`/massu-golden-path\` | Full implementation flow (plan to push) |
1752
+ | \`/massu-test\` | Run tests with failure analysis |
1753
+ | \`/massu-commit\` | Pre-commit verification gate |
1754
+ | \`/massu-push\` | Pre-push verification gate |
1755
+ | \`/massu-status\` | Project health dashboard |
1756
+ | \`/massu-debug\` | Systematic debugging |
1757
+
1758
+ ### Workflow Flow
1759
+
1760
+ \`\`\`
1761
+ /massu-create-plan -> /massu-plan (audit) -> /massu-golden-path (implement + push)
1762
+ \`\`\``;
1763
+ }
1764
+ function buildMemorySystem() {
1765
+ return `## Memory System
1766
+
1767
+ Massu maintains persistent memory across sessions in \`~/.claude/projects/.../memory/\`.
1768
+
1769
+ - **User memories**: Your role, preferences, and expertise
1770
+ - **Feedback memories**: Corrections and validated approaches
1771
+ - **Project memories**: Ongoing work, decisions, deadlines
1772
+ - **Reference memories**: External resources and tools
1773
+
1774
+ Memory is automatically loaded at session start and updated as you work.`;
1775
+ }
1776
+ function buildCriticalRules() {
1777
+ return `## Critical Rules
1778
+
1779
+ 1. **Never commit secrets** \u2014 no API keys, tokens, or credentials in code
1780
+ 2. **Run tests before committing** \u2014 all tests must pass
1781
+ 3. **Verify before claiming done** \u2014 use VR-* verification checks
1782
+ 4. **Fix all issues encountered** \u2014 pre-existing issues get fixed too
1783
+ 5. **Read before editing** \u2014 understand existing code before modifying
1784
+
1785
+ <!-- Add project-specific rules as you discover them -->`;
1786
+ }
1787
+ function capitalize(str) {
1788
+ return str.charAt(0).toUpperCase() + str.slice(1);
1789
+ }
1790
+ function formatUiName(name) {
1791
+ const names = {
1792
+ nextjs: "Next.js",
1793
+ sveltekit: "SvelteKit",
1794
+ nuxt: "Nuxt",
1795
+ angular: "Angular",
1796
+ vue: "Vue",
1797
+ react: "React"
1798
+ };
1799
+ return names[name] ?? capitalize(name);
1800
+ }
1801
+ var EXCLUDED_DIRS;
1802
+ var init_claude_md_templates = __esm({
1803
+ "src/claude-md-templates.ts"() {
1804
+ "use strict";
1805
+ EXCLUDED_DIRS = /* @__PURE__ */ new Set([
1806
+ "node_modules",
1807
+ ".git",
1808
+ ".venv",
1809
+ "venv",
1810
+ "__pycache__",
1811
+ "dist",
1812
+ "build",
1813
+ ".next",
1814
+ ".nuxt",
1815
+ ".svelte-kit",
1816
+ "coverage",
1817
+ ".massu",
1818
+ ".turbo",
1819
+ ".cache",
1820
+ ".output"
1821
+ ]);
1822
+ }
1823
+ });
1824
+
1561
1825
  // src/commands/install-commands.ts
1562
1826
  var install_commands_exports = {};
1563
1827
  __export(install_commands_exports, {
@@ -1567,20 +1831,20 @@ __export(install_commands_exports, {
1567
1831
  resolveCommandsDir: () => resolveCommandsDir,
1568
1832
  runInstallCommands: () => runInstallCommands
1569
1833
  });
1570
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync, mkdirSync as mkdirSync2, readdirSync as readdirSync2, statSync } from "fs";
1571
- import { resolve as resolve3, dirname as dirname3 } from "path";
1834
+ import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync, mkdirSync as mkdirSync2, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
1835
+ import { resolve as resolve4, dirname as dirname3 } from "path";
1572
1836
  import { fileURLToPath } from "url";
1573
1837
  function resolveAssetDir(assetName) {
1574
1838
  const cwd = process.cwd();
1575
- const nodeModulesPath = resolve3(cwd, "node_modules/@massu/core", assetName);
1839
+ const nodeModulesPath = resolve4(cwd, "node_modules/@massu/core", assetName);
1576
1840
  if (existsSync4(nodeModulesPath)) {
1577
1841
  return nodeModulesPath;
1578
1842
  }
1579
- const distRelPath = resolve3(__dirname, "..", assetName);
1843
+ const distRelPath = resolve4(__dirname, "..", assetName);
1580
1844
  if (existsSync4(distRelPath)) {
1581
1845
  return distRelPath;
1582
1846
  }
1583
- const srcRelPath = resolve3(__dirname, "../..", assetName);
1847
+ const srcRelPath = resolve4(__dirname, "../..", assetName);
1584
1848
  if (existsSync4(srcRelPath)) {
1585
1849
  return srcRelPath;
1586
1850
  }
@@ -1594,11 +1858,11 @@ function syncDirectory(sourceDir, targetDir) {
1594
1858
  if (!existsSync4(targetDir)) {
1595
1859
  mkdirSync2(targetDir, { recursive: true });
1596
1860
  }
1597
- const entries = readdirSync2(sourceDir);
1861
+ const entries = readdirSync3(sourceDir);
1598
1862
  for (const entry of entries) {
1599
- const sourcePath = resolve3(sourceDir, entry);
1600
- const targetPath = resolve3(targetDir, entry);
1601
- const entryStat = statSync(sourcePath);
1863
+ const sourcePath = resolve4(sourceDir, entry);
1864
+ const targetPath = resolve4(targetDir, entry);
1865
+ const entryStat = statSync2(sourcePath);
1602
1866
  if (entryStat.isDirectory()) {
1603
1867
  const subStats = syncDirectory(sourcePath, targetPath);
1604
1868
  stats.installed += subStats.installed;
@@ -1624,7 +1888,7 @@ function syncDirectory(sourceDir, targetDir) {
1624
1888
  }
1625
1889
  function installCommands(projectRoot) {
1626
1890
  const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
1627
- const targetDir = resolve3(projectRoot, claudeDirName, "commands");
1891
+ const targetDir = resolve4(projectRoot, claudeDirName, "commands");
1628
1892
  if (!existsSync4(targetDir)) {
1629
1893
  mkdirSync2(targetDir, { recursive: true });
1630
1894
  }
@@ -1639,7 +1903,7 @@ function installCommands(projectRoot) {
1639
1903
  }
1640
1904
  function installAll(projectRoot) {
1641
1905
  const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
1642
- const claudeDir = resolve3(projectRoot, claudeDirName);
1906
+ const claudeDir = resolve4(projectRoot, claudeDirName);
1643
1907
  const assets = {};
1644
1908
  let totalInstalled = 0;
1645
1909
  let totalUpdated = 0;
@@ -1649,7 +1913,7 @@ function installAll(projectRoot) {
1649
1913
  if (!sourceDir) {
1650
1914
  continue;
1651
1915
  }
1652
- const targetDir = resolve3(claudeDir, assetType.targetSubdir);
1916
+ const targetDir = resolve4(claudeDir, assetType.targetSubdir);
1653
1917
  const stats = syncDirectory(sourceDir, targetDir);
1654
1918
  assets[assetType.name] = stats;
1655
1919
  totalInstalled += stats.installed;
@@ -1709,6 +1973,7 @@ __export(init_exports, {
1709
1973
  buildHooksConfig: () => buildHooksConfig,
1710
1974
  detectFramework: () => detectFramework,
1711
1975
  detectPython: () => detectPython,
1976
+ generateClaudeMd: () => generateClaudeMd,
1712
1977
  generateConfig: () => generateConfig,
1713
1978
  initMemoryDir: () => initMemoryDir,
1714
1979
  installHooks: () => installHooks,
@@ -1716,8 +1981,8 @@ __export(init_exports, {
1716
1981
  resolveHooksDir: () => resolveHooksDir,
1717
1982
  runInit: () => runInit
1718
1983
  });
1719
- import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, readdirSync as readdirSync3 } from "fs";
1720
- import { resolve as resolve4, basename as basename2, dirname as dirname4 } from "path";
1984
+ import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, readdirSync as readdirSync4 } from "fs";
1985
+ import { resolve as resolve5, basename as basename3, dirname as dirname4 } from "path";
1721
1986
  import { fileURLToPath as fileURLToPath2 } from "url";
1722
1987
  import { homedir as homedir2 } from "os";
1723
1988
  import { stringify as yamlStringify } from "yaml";
@@ -1728,7 +1993,7 @@ function detectFramework(projectRoot) {
1728
1993
  orm: "none",
1729
1994
  ui: "none"
1730
1995
  };
1731
- const pkgPath = resolve4(projectRoot, "package.json");
1996
+ const pkgPath = resolve5(projectRoot, "package.json");
1732
1997
  if (!existsSync5(pkgPath)) return result;
1733
1998
  try {
1734
1999
  const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
@@ -1765,7 +2030,7 @@ function detectPython(projectRoot) {
1765
2030
  alembicDir: null
1766
2031
  };
1767
2032
  const markers = ["pyproject.toml", "setup.py", "requirements.txt", "Pipfile"];
1768
- const hasMarker = markers.some((m) => existsSync5(resolve4(projectRoot, m)));
2033
+ const hasMarker = markers.some((m) => existsSync5(resolve5(projectRoot, m)));
1769
2034
  if (!hasMarker) return result;
1770
2035
  result.detected = true;
1771
2036
  const depFiles = [
@@ -1775,7 +2040,7 @@ function detectPython(projectRoot) {
1775
2040
  { file: "Pipfile", parser: parsePipfileDeps }
1776
2041
  ];
1777
2042
  for (const { file, parser } of depFiles) {
1778
- const filePath = resolve4(projectRoot, file);
2043
+ const filePath = resolve5(projectRoot, file);
1779
2044
  if (existsSync5(filePath)) {
1780
2045
  try {
1781
2046
  const content = readFileSync4(filePath, "utf-8");
@@ -1786,25 +2051,25 @@ function detectPython(projectRoot) {
1786
2051
  }
1787
2052
  }
1788
2053
  }
1789
- if (existsSync5(resolve4(projectRoot, "alembic.ini"))) {
2054
+ if (existsSync5(resolve5(projectRoot, "alembic.ini"))) {
1790
2055
  result.hasAlembic = true;
1791
- if (existsSync5(resolve4(projectRoot, "alembic"))) {
2056
+ if (existsSync5(resolve5(projectRoot, "alembic"))) {
1792
2057
  result.alembicDir = "alembic";
1793
2058
  }
1794
- } else if (existsSync5(resolve4(projectRoot, "alembic"))) {
2059
+ } else if (existsSync5(resolve5(projectRoot, "alembic"))) {
1795
2060
  result.hasAlembic = true;
1796
2061
  result.alembicDir = "alembic";
1797
2062
  }
1798
2063
  const candidateRoots = ["app", "src", "backend", "api"];
1799
2064
  for (const candidate of candidateRoots) {
1800
- const candidatePath = resolve4(projectRoot, candidate);
1801
- if (existsSync5(candidatePath) && existsSync5(resolve4(candidatePath, "__init__.py"))) {
2065
+ const candidatePath = resolve5(projectRoot, candidate);
2066
+ if (existsSync5(candidatePath) && existsSync5(resolve5(candidatePath, "__init__.py"))) {
1802
2067
  result.root = candidate;
1803
2068
  break;
1804
2069
  }
1805
2070
  if (existsSync5(candidatePath)) {
1806
2071
  try {
1807
- const files = readdirSync3(candidatePath);
2072
+ const files = readdirSync4(candidatePath);
1808
2073
  if (files.some((f) => f.endsWith(".py"))) {
1809
2074
  result.root = candidate;
1810
2075
  break;
@@ -1850,11 +2115,11 @@ function parsePipfileDeps(content) {
1850
2115
  return deps;
1851
2116
  }
1852
2117
  function generateConfig(projectRoot, framework) {
1853
- const configPath = resolve4(projectRoot, "massu.config.yaml");
2118
+ const configPath = resolve5(projectRoot, "massu.config.yaml");
1854
2119
  if (existsSync5(configPath)) {
1855
2120
  return false;
1856
2121
  }
1857
- const projectName = basename2(projectRoot);
2122
+ const projectName = basename3(projectRoot);
1858
2123
  const config = {
1859
2124
  project: {
1860
2125
  name: projectName,
@@ -1900,8 +2165,18 @@ ${yamlStringify(config)}`;
1900
2165
  writeFileSync2(configPath, yamlContent, "utf-8");
1901
2166
  return true;
1902
2167
  }
2168
+ function generateClaudeMd(projectRoot, framework, python) {
2169
+ const claudeMdPath = resolve5(projectRoot, "CLAUDE.md");
2170
+ if (existsSync5(claudeMdPath)) {
2171
+ return { created: false, skipped: true };
2172
+ }
2173
+ const projectName = basename3(projectRoot);
2174
+ const content = buildClaudeMdContent(projectName, projectRoot, framework, python);
2175
+ writeFileSync2(claudeMdPath, content, "utf-8");
2176
+ return { created: true, skipped: false };
2177
+ }
1903
2178
  function registerMcpServer(projectRoot) {
1904
- const mcpPath = resolve4(projectRoot, ".mcp.json");
2179
+ const mcpPath = resolve5(projectRoot, ".mcp.json");
1905
2180
  let existing = {};
1906
2181
  if (existsSync5(mcpPath)) {
1907
2182
  try {
@@ -1924,19 +2199,11 @@ function registerMcpServer(projectRoot) {
1924
2199
  return true;
1925
2200
  }
1926
2201
  function resolveHooksDir() {
1927
- const cwd = process.cwd();
1928
- const nodeModulesPath = resolve4(cwd, "node_modules/@massu/core/dist/hooks");
1929
- if (existsSync5(nodeModulesPath)) {
1930
- return "node_modules/@massu/core/dist/hooks";
1931
- }
1932
- const localPath = resolve4(__dirname2, "../dist/hooks");
1933
- if (existsSync5(localPath)) {
1934
- return localPath;
1935
- }
1936
2202
  return "node_modules/@massu/core/dist/hooks";
1937
2203
  }
1938
2204
  function hookCmd(hooksDir, hookFile) {
1939
- return `node ${hooksDir}/${hookFile}`;
2205
+ const hookPath = `${hooksDir}/${hookFile}`;
2206
+ return `d="$PWD"; while [ "$d" != "/" ] && [ ! -f "$d/${hookPath}" ]; do d="$(dirname "$d")"; done; cd "$d" && node ${hookPath}`;
1940
2207
  }
1941
2208
  function buildHooksConfig(hooksDir) {
1942
2209
  return {
@@ -2012,8 +2279,8 @@ function buildHooksConfig(hooksDir) {
2012
2279
  }
2013
2280
  function installHooks(projectRoot) {
2014
2281
  const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
2015
- const claudeDir = resolve4(projectRoot, claudeDirName);
2016
- const settingsPath = resolve4(claudeDir, "settings.local.json");
2282
+ const claudeDir = resolve5(projectRoot, claudeDirName);
2283
+ const settingsPath = resolve5(claudeDir, "settings.local.json");
2017
2284
  if (!existsSync5(claudeDir)) {
2018
2285
  mkdirSync3(claudeDir, { recursive: true });
2019
2286
  }
@@ -2039,16 +2306,16 @@ function installHooks(projectRoot) {
2039
2306
  }
2040
2307
  function initMemoryDir(projectRoot) {
2041
2308
  const encodedRoot = "-" + projectRoot.replace(/\//g, "-");
2042
- const memoryDir = resolve4(homedir2(), `.claude/projects/${encodedRoot}/memory`);
2309
+ const memoryDir = resolve5(homedir2(), `.claude/projects/${encodedRoot}/memory`);
2043
2310
  let created = false;
2044
2311
  if (!existsSync5(memoryDir)) {
2045
2312
  mkdirSync3(memoryDir, { recursive: true });
2046
2313
  created = true;
2047
2314
  }
2048
- const memoryMdPath = resolve4(memoryDir, "MEMORY.md");
2315
+ const memoryMdPath = resolve5(memoryDir, "MEMORY.md");
2049
2316
  let memoryMdCreated = false;
2050
2317
  if (!existsSync5(memoryMdPath)) {
2051
- const projectName = basename2(projectRoot);
2318
+ const projectName = basename3(projectRoot);
2052
2319
  const memoryContent = `# ${projectName} - Massu Memory
2053
2320
 
2054
2321
  ## Key Learnings
@@ -2076,9 +2343,9 @@ async function runInit() {
2076
2343
  console.log("");
2077
2344
  const framework = detectFramework(projectRoot);
2078
2345
  const frameworkParts = [];
2079
- if (framework.type !== "javascript") frameworkParts.push(capitalize(framework.type));
2346
+ if (framework.type !== "javascript") frameworkParts.push(capitalize2(framework.type));
2080
2347
  if (framework.ui !== "none") frameworkParts.push(formatName(framework.ui));
2081
- if (framework.orm !== "none") frameworkParts.push(capitalize(framework.orm));
2348
+ if (framework.orm !== "none") frameworkParts.push(capitalize2(framework.orm));
2082
2349
  if (framework.router !== "none") frameworkParts.push(framework.router.toUpperCase());
2083
2350
  const detected = frameworkParts.length > 0 ? frameworkParts.join(", ") : "JavaScript";
2084
2351
  console.log(` Detected: ${detected}`);
@@ -2090,6 +2357,12 @@ async function runInit() {
2090
2357
  if (python.hasAlembic) pyParts.push("Alembic");
2091
2358
  console.log(` Detected: ${pyParts.join(", ")} (root: ${python.root})`);
2092
2359
  }
2360
+ const claudeMdResult = generateClaudeMd(projectRoot, framework, python);
2361
+ if (claudeMdResult.created) {
2362
+ console.log(" Created CLAUDE.md (project instructions for Claude Code)");
2363
+ } else {
2364
+ console.log(" CLAUDE.md already exists (preserved)");
2365
+ }
2093
2366
  const configCreated = generateConfig(projectRoot, framework);
2094
2367
  if (configCreated) {
2095
2368
  console.log(" Created massu.config.yaml");
@@ -2123,8 +2396,8 @@ async function runInit() {
2123
2396
  try {
2124
2397
  const claudeDirName = ".claude";
2125
2398
  const encodedRoot = projectRoot.replace(/\//g, "-");
2126
- const computedMemoryDir = resolve4(homedir2(), claudeDirName, "projects", encodedRoot, "memory");
2127
- const memFiles = existsSync5(computedMemoryDir) ? readdirSync3(computedMemoryDir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md") : [];
2399
+ const computedMemoryDir = resolve5(homedir2(), claudeDirName, "projects", encodedRoot, "memory");
2400
+ const memFiles = existsSync5(computedMemoryDir) ? readdirSync4(computedMemoryDir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md") : [];
2128
2401
  if (memFiles.length > 0) {
2129
2402
  const { getMemoryDb: getMemoryDb2 } = await Promise.resolve().then(() => (init_memory_db(), memory_db_exports));
2130
2403
  const db = getMemoryDb2();
@@ -2150,7 +2423,7 @@ async function runInit() {
2150
2423
  console.log("Documentation: https://massu.ai/docs");
2151
2424
  console.log("");
2152
2425
  }
2153
- function capitalize(str) {
2426
+ function capitalize2(str) {
2154
2427
  return str.charAt(0).toUpperCase() + str.slice(1);
2155
2428
  }
2156
2429
  function formatName(name) {
@@ -2162,13 +2435,14 @@ function formatName(name) {
2162
2435
  vue: "Vue",
2163
2436
  react: "React"
2164
2437
  };
2165
- return names[name] ?? capitalize(name);
2438
+ return names[name] ?? capitalize2(name);
2166
2439
  }
2167
2440
  var __filename2, __dirname2;
2168
2441
  var init_init = __esm({
2169
2442
  "src/commands/init.ts"() {
2170
2443
  "use strict";
2171
2444
  init_memory_file_ingest();
2445
+ init_claude_md_templates();
2172
2446
  init_config();
2173
2447
  init_install_commands();
2174
2448
  __filename2 = fileURLToPath2(import.meta.url);
@@ -2472,18 +2746,18 @@ __export(doctor_exports, {
2472
2746
  runDoctor: () => runDoctor,
2473
2747
  runValidateConfig: () => runValidateConfig
2474
2748
  });
2475
- import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "fs";
2476
- import { resolve as resolve5, dirname as dirname5 } from "path";
2749
+ import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync5 } from "fs";
2750
+ import { resolve as resolve6, dirname as dirname5 } from "path";
2477
2751
  import { fileURLToPath as fileURLToPath3 } from "url";
2478
- import { parse as parseYaml3 } from "yaml";
2752
+ import { parse as parseYaml2 } from "yaml";
2479
2753
  function checkConfig(projectRoot) {
2480
- const configPath = resolve5(projectRoot, "massu.config.yaml");
2754
+ const configPath = resolve6(projectRoot, "massu.config.yaml");
2481
2755
  if (!existsSync6(configPath)) {
2482
2756
  return { name: "Configuration", status: "fail", detail: "massu.config.yaml not found. Run: npx massu init" };
2483
2757
  }
2484
2758
  try {
2485
2759
  const content = readFileSync5(configPath, "utf-8");
2486
- const parsed = parseYaml3(content);
2760
+ const parsed = parseYaml2(content);
2487
2761
  if (!parsed || typeof parsed !== "object") {
2488
2762
  return { name: "Configuration", status: "fail", detail: "massu.config.yaml is empty or invalid YAML" };
2489
2763
  }
@@ -2538,10 +2812,10 @@ function checkHooksConfig(projectRoot) {
2538
2812
  }
2539
2813
  }
2540
2814
  function checkHookFiles(projectRoot) {
2541
- const nodeModulesHooksDir = resolve5(projectRoot, "node_modules/@massu/core/dist/hooks");
2815
+ const nodeModulesHooksDir = resolve6(projectRoot, "node_modules/@massu/core/dist/hooks");
2542
2816
  let hooksDir = nodeModulesHooksDir;
2543
2817
  if (!existsSync6(nodeModulesHooksDir)) {
2544
- const devHooksDir = resolve5(__dirname3, "../../dist/hooks");
2818
+ const devHooksDir = resolve6(__dirname3, "../../dist/hooks");
2545
2819
  if (existsSync6(devHooksDir)) {
2546
2820
  hooksDir = devHooksDir;
2547
2821
  } else {
@@ -2550,7 +2824,7 @@ function checkHookFiles(projectRoot) {
2550
2824
  }
2551
2825
  const missing = [];
2552
2826
  for (const hookFile of EXPECTED_HOOKS) {
2553
- if (!existsSync6(resolve5(hooksDir, hookFile))) {
2827
+ if (!existsSync6(resolve6(hooksDir, hookFile))) {
2554
2828
  missing.push(hookFile);
2555
2829
  }
2556
2830
  }
@@ -2576,7 +2850,7 @@ function checkNodeVersion() {
2576
2850
  return { name: "Node.js", status: "fail", detail: `v${version} \u2014 Node.js 18+ is required` };
2577
2851
  }
2578
2852
  async function checkGitRepo(projectRoot) {
2579
- const gitDir = resolve5(projectRoot, ".git");
2853
+ const gitDir = resolve6(projectRoot, ".git");
2580
2854
  if (!existsSync6(gitDir)) {
2581
2855
  return { name: "Git Repository", status: "warn", detail: "Not a git repository (optional but recommended)" };
2582
2856
  }
@@ -2676,7 +2950,7 @@ async function checkLicenseStatus() {
2676
2950
  function checkPythonHealth(projectRoot) {
2677
2951
  const config = getConfig();
2678
2952
  if (!config.python?.root) return null;
2679
- const pythonRoot = resolve5(projectRoot, config.python.root);
2953
+ const pythonRoot = resolve6(projectRoot, config.python.root);
2680
2954
  if (!existsSync6(pythonRoot)) {
2681
2955
  return {
2682
2956
  name: "Python",
@@ -2691,15 +2965,15 @@ function checkPythonHealth(projectRoot) {
2691
2965
  function scanDir(dir, depth) {
2692
2966
  if (depth > 5) return;
2693
2967
  try {
2694
- const entries = readdirSync4(dir, { withFileTypes: true });
2968
+ const entries = readdirSync5(dir, { withFileTypes: true });
2695
2969
  for (const entry of entries) {
2696
2970
  if (entry.isDirectory()) {
2697
2971
  const excludeDirs = config.python?.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
2698
2972
  if (!excludeDirs.includes(entry.name)) {
2699
- const subdir = resolve5(dir, entry.name);
2700
- if (depth <= 2 && !existsSync6(resolve5(subdir, "__init__.py"))) {
2973
+ const subdir = resolve6(dir, entry.name);
2974
+ if (depth <= 2 && !existsSync6(resolve6(subdir, "__init__.py"))) {
2701
2975
  try {
2702
- const subEntries = readdirSync4(subdir);
2976
+ const subEntries = readdirSync5(subdir);
2703
2977
  if (subEntries.some((f) => f.endsWith(".py") && f !== "__init__.py")) {
2704
2978
  initPyMissing.push(entry.name);
2705
2979
  }
@@ -2746,6 +3020,29 @@ function checkPythonHealth(projectRoot) {
2746
3020
  detail: parts.join(", ")
2747
3021
  };
2748
3022
  }
3023
+ function checkClaudeMd(projectRoot) {
3024
+ const claudeMdPath = resolve6(projectRoot, "CLAUDE.md");
3025
+ if (!existsSync6(claudeMdPath)) {
3026
+ return {
3027
+ name: "CLAUDE.md",
3028
+ status: "warn",
3029
+ detail: "CLAUDE.md not found. Run: npx massu init (or create manually)"
3030
+ };
3031
+ }
3032
+ const content = readFileSync5(claudeMdPath, "utf-8");
3033
+ if (content.trim().length < 50) {
3034
+ return {
3035
+ name: "CLAUDE.md",
3036
+ status: "warn",
3037
+ detail: "CLAUDE.md exists but appears empty or minimal"
3038
+ };
3039
+ }
3040
+ return {
3041
+ name: "CLAUDE.md",
3042
+ status: "pass",
3043
+ detail: "CLAUDE.md found and has content"
3044
+ };
3045
+ }
2749
3046
  async function runDoctor() {
2750
3047
  const projectRoot = process.cwd();
2751
3048
  console.log("");
@@ -2763,7 +3060,8 @@ async function runDoctor() {
2763
3060
  await checkNativeModules(),
2764
3061
  checkNodeVersion(),
2765
3062
  await checkGitRepo(projectRoot),
2766
- await checkLicenseStatus()
3063
+ await checkLicenseStatus(),
3064
+ checkClaudeMd(projectRoot)
2767
3065
  ];
2768
3066
  const pythonCheck = checkPythonHealth(projectRoot);
2769
3067
  if (pythonCheck) checks.push(pythonCheck);
@@ -2792,7 +3090,7 @@ async function runDoctor() {
2792
3090
  }
2793
3091
  async function runValidateConfig() {
2794
3092
  const projectRoot = process.cwd();
2795
- const configPath = resolve5(projectRoot, "massu.config.yaml");
3093
+ const configPath = resolve6(projectRoot, "massu.config.yaml");
2796
3094
  if (!existsSync6(configPath)) {
2797
3095
  console.error("Error: massu.config.yaml not found in current directory");
2798
3096
  console.error("Run: npx massu init");
@@ -2801,7 +3099,7 @@ async function runValidateConfig() {
2801
3099
  }
2802
3100
  try {
2803
3101
  const content = readFileSync5(configPath, "utf-8");
2804
- const parsed = parseYaml3(content);
3102
+ const parsed = parseYaml2(content);
2805
3103
  if (!parsed || typeof parsed !== "object") {
2806
3104
  console.error("Error: massu.config.yaml is empty or not a valid YAML object");
2807
3105
  process.exit(1);
@@ -2883,7 +3181,7 @@ var init_install_hooks = __esm({
2883
3181
  // src/db.ts
2884
3182
  import Database2 from "better-sqlite3";
2885
3183
  import { dirname as dirname6, join as join3 } from "path";
2886
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
3184
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4, readdirSync as readdirSync6, statSync as statSync3 } from "fs";
2887
3185
  function getCodeGraphDb() {
2888
3186
  const dbPath = getResolvedPaths().codegraphDbPath;
2889
3187
  if (!existsSync7(dbPath)) {
@@ -3167,14 +3465,14 @@ function isPythonDataStale(dataDb2, pythonRoot) {
3167
3465
  const lastBuildTime = new Date(lastBuild.value).getTime();
3168
3466
  function checkDir(dir) {
3169
3467
  try {
3170
- const entries = readdirSync5(dir, { withFileTypes: true });
3468
+ const entries = readdirSync6(dir, { withFileTypes: true });
3171
3469
  for (const entry of entries) {
3172
3470
  const fullPath = join3(dir, entry.name);
3173
3471
  if (entry.isDirectory()) {
3174
3472
  if (["__pycache__", ".venv", "venv", "node_modules", ".mypy_cache", ".pytest_cache"].includes(entry.name)) continue;
3175
3473
  if (checkDir(fullPath)) return true;
3176
3474
  } else if (entry.name.endsWith(".py")) {
3177
- if (statSync2(fullPath).mtimeMs > lastBuildTime) return true;
3475
+ if (statSync3(fullPath).mtimeMs > lastBuildTime) return true;
3178
3476
  }
3179
3477
  }
3180
3478
  } catch {
@@ -3194,10 +3492,10 @@ var init_db = __esm({
3194
3492
  });
3195
3493
 
3196
3494
  // src/security-utils.ts
3197
- import { resolve as resolve6, normalize } from "path";
3495
+ import { resolve as resolve7, normalize } from "path";
3198
3496
  function ensureWithinRoot(filePath, projectRoot) {
3199
- const resolvedRoot = resolve6(projectRoot);
3200
- const resolvedPath = resolve6(resolvedRoot, filePath);
3497
+ const resolvedRoot = resolve7(projectRoot);
3498
+ const resolvedPath = resolve7(resolvedRoot, filePath);
3201
3499
  const normalizedPath = normalize(resolvedPath);
3202
3500
  const normalizedRoot = normalize(resolvedRoot);
3203
3501
  if (!normalizedPath.startsWith(normalizedRoot + "/") && normalizedPath !== normalizedRoot) {
@@ -3270,8 +3568,8 @@ var init_rules = __esm({
3270
3568
  });
3271
3569
 
3272
3570
  // src/import-resolver.ts
3273
- import { readFileSync as readFileSync6, existsSync as existsSync8, statSync as statSync3 } from "fs";
3274
- import { resolve as resolve7, dirname as dirname7, join as join4 } from "path";
3571
+ import { readFileSync as readFileSync6, existsSync as existsSync8, statSync as statSync4 } from "fs";
3572
+ import { resolve as resolve8, dirname as dirname7, join as join4 } from "path";
3275
3573
  function parseImports(source) {
3276
3574
  const imports = [];
3277
3575
  const lines = source.split("\n");
@@ -3327,9 +3625,9 @@ function resolveImportPath(specifier, fromFile) {
3327
3625
  let basePath;
3328
3626
  if (specifier.startsWith("@/")) {
3329
3627
  const paths = getResolvedPaths();
3330
- basePath = resolve7(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
3628
+ basePath = resolve8(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
3331
3629
  } else {
3332
- basePath = resolve7(dirname7(fromFile), specifier);
3630
+ basePath = resolve8(dirname7(fromFile), specifier);
3333
3631
  }
3334
3632
  if (existsSync8(basePath) && !isDirectory(basePath)) {
3335
3633
  return toRelative(basePath);
@@ -3351,7 +3649,7 @@ function resolveImportPath(specifier, fromFile) {
3351
3649
  }
3352
3650
  function isDirectory(path) {
3353
3651
  try {
3354
- return statSync3(path).isDirectory();
3652
+ return statSync4(path).isDirectory();
3355
3653
  } catch {
3356
3654
  return false;
3357
3655
  }
@@ -3379,7 +3677,7 @@ function buildImportIndex(dataDb2, codegraphDb2) {
3379
3677
  const batchSize = 500;
3380
3678
  let batch = [];
3381
3679
  for (const file of files) {
3382
- const absPath = ensureWithinRoot(resolve7(projectRoot, file.path), projectRoot);
3680
+ const absPath = ensureWithinRoot(resolve8(projectRoot, file.path), projectRoot);
3383
3681
  if (!existsSync8(absPath)) continue;
3384
3682
  let source;
3385
3683
  try {
@@ -3419,8 +3717,8 @@ var init_import_resolver = __esm({
3419
3717
  });
3420
3718
 
3421
3719
  // src/trpc-index.ts
3422
- import { readFileSync as readFileSync7, existsSync as existsSync9, readdirSync as readdirSync6 } from "fs";
3423
- import { resolve as resolve8, join as join5 } from "path";
3720
+ import { readFileSync as readFileSync7, existsSync as existsSync9, readdirSync as readdirSync7 } from "fs";
3721
+ import { resolve as resolve9, join as join5 } from "path";
3424
3722
  function parseRootRouter() {
3425
3723
  const paths = getResolvedPaths();
3426
3724
  const rootPath = paths.rootRouterPath;
@@ -3435,7 +3733,7 @@ function parseRootRouter() {
3435
3733
  while ((match = importRegex.exec(source)) !== null) {
3436
3734
  const variable = match[1];
3437
3735
  let filePath = match[2];
3438
- const fullPath = resolve8(paths.routersDir, filePath);
3736
+ const fullPath = resolve9(paths.routersDir, filePath);
3439
3737
  for (const ext of [".ts", ".tsx", ""]) {
3440
3738
  const candidate = fullPath + ext;
3441
3739
  const routersRelPath = getConfig().paths.routers ?? "src/server/api/routers";
@@ -3463,7 +3761,7 @@ function parseRootRouter() {
3463
3761
  return mappings;
3464
3762
  }
3465
3763
  function extractProcedures(routerFilePath) {
3466
- const absPath = resolve8(getProjectRoot(), routerFilePath);
3764
+ const absPath = resolve9(getProjectRoot(), routerFilePath);
3467
3765
  if (!existsSync9(absPath)) return [];
3468
3766
  const source = readFileSync7(absPath, "utf-8");
3469
3767
  const procedures = [];
@@ -3488,9 +3786,9 @@ function findUICallSites(routerKey, procedureName) {
3488
3786
  const root = getProjectRoot();
3489
3787
  const src = config.paths.source;
3490
3788
  const searchDirs = [
3491
- resolve8(root, config.paths.pages ?? src + "/app"),
3492
- resolve8(root, config.paths.components ?? src + "/components"),
3493
- resolve8(root, config.paths.hooks ?? src + "/hooks")
3789
+ resolve9(root, config.paths.pages ?? src + "/app"),
3790
+ resolve9(root, config.paths.components ?? src + "/components"),
3791
+ resolve9(root, config.paths.hooks ?? src + "/hooks")
3494
3792
  ];
3495
3793
  const searchPattern = `api.${routerKey}.${procedureName}`;
3496
3794
  for (const dir of searchDirs) {
@@ -3500,7 +3798,7 @@ function findUICallSites(routerKey, procedureName) {
3500
3798
  return callSites;
3501
3799
  }
3502
3800
  function searchDirectory(dir, pattern, results) {
3503
- const entries = readdirSync6(dir, { withFileTypes: true });
3801
+ const entries = readdirSync7(dir, { withFileTypes: true });
3504
3802
  for (const entry of entries) {
3505
3803
  const fullPath = join5(dir, entry.name);
3506
3804
  if (entry.isDirectory()) {
@@ -3572,7 +3870,7 @@ var init_trpc_index = __esm({
3572
3870
 
3573
3871
  // src/page-deps.ts
3574
3872
  import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
3575
- import { resolve as resolve9 } from "path";
3873
+ import { resolve as resolve10 } from "path";
3576
3874
  function deriveRoute(pageFile) {
3577
3875
  let route = pageFile.replace(/^src\/app/, "").replace(/\/page\.tsx?$/, "").replace(/\/page\.jsx?$/, "");
3578
3876
  return route || "/";
@@ -3610,7 +3908,7 @@ function findRouterCalls(files) {
3610
3908
  const routers = /* @__PURE__ */ new Set();
3611
3909
  const projectRoot = getProjectRoot();
3612
3910
  for (const file of files) {
3613
- const absPath = ensureWithinRoot(resolve9(projectRoot, file), projectRoot);
3911
+ const absPath = ensureWithinRoot(resolve10(projectRoot, file), projectRoot);
3614
3912
  if (!existsSync10(absPath)) continue;
3615
3913
  try {
3616
3914
  const source = readFileSync8(absPath, "utf-8");
@@ -3631,7 +3929,7 @@ function findTablesFromRouters(routerNames, dataDb2) {
3631
3929
  "SELECT DISTINCT router_file FROM massu_trpc_procedures WHERE router_name = ?"
3632
3930
  ).all(routerName);
3633
3931
  for (const proc of procs) {
3634
- const absPath = ensureWithinRoot(resolve9(getProjectRoot(), proc.router_file), getProjectRoot());
3932
+ const absPath = ensureWithinRoot(resolve10(getProjectRoot(), proc.router_file), getProjectRoot());
3635
3933
  if (!existsSync10(absPath)) continue;
3636
3934
  try {
3637
3935
  const source = readFileSync8(absPath, "utf-8");
@@ -3903,7 +4201,7 @@ var init_domains = __esm({
3903
4201
  });
3904
4202
 
3905
4203
  // src/schema-mapper.ts
3906
- import { readFileSync as readFileSync9, existsSync as existsSync11, readdirSync as readdirSync7 } from "fs";
4204
+ import { readFileSync as readFileSync9, existsSync as existsSync11, readdirSync as readdirSync8 } from "fs";
3907
4205
  import { join as join6 } from "path";
3908
4206
  function parsePrismaSchema() {
3909
4207
  const schemaPath = getResolvedPaths().prismaSchemaPath;
@@ -3973,7 +4271,7 @@ function findColumnUsageInRouters(tableName) {
3973
4271
  return usage;
3974
4272
  }
3975
4273
  function scanDirectory(dir, tableName, usage) {
3976
- const entries = readdirSync7(dir, { withFileTypes: true });
4274
+ const entries = readdirSync8(dir, { withFileTypes: true });
3977
4275
  for (const entry of entries) {
3978
4276
  const fullPath = join6(dir, entry.name);
3979
4277
  if (entry.isDirectory()) {
@@ -4031,7 +4329,7 @@ function detectMismatches(models) {
4031
4329
  function findFilesUsingColumn(dir, column, tableName) {
4032
4330
  const result = [];
4033
4331
  if (!existsSync11(dir)) return result;
4034
- const entries = readdirSync7(dir, { withFileTypes: true });
4332
+ const entries = readdirSync8(dir, { withFileTypes: true });
4035
4333
  for (const entry of entries) {
4036
4334
  const fullPath = join6(dir, entry.name);
4037
4335
  if (entry.isDirectory()) {
@@ -4183,12 +4481,12 @@ var init_import_parser = __esm({
4183
4481
  });
4184
4482
 
4185
4483
  // src/python/import-resolver.ts
4186
- import { readFileSync as readFileSync10, existsSync as existsSync12, readdirSync as readdirSync8 } from "fs";
4187
- import { resolve as resolve11, join as join7, relative as relative2, dirname as dirname8 } from "path";
4484
+ import { readFileSync as readFileSync10, existsSync as existsSync12, readdirSync as readdirSync9 } from "fs";
4485
+ import { resolve as resolve12, join as join7, relative as relative3, dirname as dirname8 } from "path";
4188
4486
  function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
4189
4487
  const projectRoot = getProjectRoot();
4190
4488
  if (level > 0) {
4191
- let baseDir = dirname8(resolve11(projectRoot, fromFile));
4489
+ let baseDir = dirname8(resolve12(projectRoot, fromFile));
4192
4490
  for (let i = 1; i < level; i++) {
4193
4491
  baseDir = dirname8(baseDir);
4194
4492
  }
@@ -4200,25 +4498,25 @@ function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
4200
4498
  return tryResolvePythonPath(baseDir, projectRoot);
4201
4499
  }
4202
4500
  const parts = module.split(".");
4203
- const candidate = join7(resolve11(projectRoot, pythonRoot), ...parts);
4501
+ const candidate = join7(resolve12(projectRoot, pythonRoot), ...parts);
4204
4502
  return tryResolvePythonPath(candidate, projectRoot);
4205
4503
  }
4206
4504
  function tryResolvePythonPath(basePath, projectRoot) {
4207
4505
  if (existsSync12(basePath + ".py")) {
4208
- return relative2(projectRoot, basePath + ".py");
4506
+ return relative3(projectRoot, basePath + ".py");
4209
4507
  }
4210
4508
  if (existsSync12(join7(basePath, "__init__.py"))) {
4211
- return relative2(projectRoot, join7(basePath, "__init__.py"));
4509
+ return relative3(projectRoot, join7(basePath, "__init__.py"));
4212
4510
  }
4213
4511
  if (basePath.endsWith(".py") && existsSync12(basePath)) {
4214
- return relative2(projectRoot, basePath);
4512
+ return relative3(projectRoot, basePath);
4215
4513
  }
4216
4514
  return null;
4217
4515
  }
4218
4516
  function walkPythonFiles(dir, excludeDirs) {
4219
4517
  const files = [];
4220
4518
  try {
4221
- const entries = readdirSync8(dir, { withFileTypes: true });
4519
+ const entries = readdirSync9(dir, { withFileTypes: true });
4222
4520
  for (const entry of entries) {
4223
4521
  if (entry.isDirectory()) {
4224
4522
  if (excludeDirs.includes(entry.name)) continue;
@@ -4233,7 +4531,7 @@ function walkPythonFiles(dir, excludeDirs) {
4233
4531
  }
4234
4532
  function buildPythonImportIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
4235
4533
  const projectRoot = getProjectRoot();
4236
- const absRoot = resolve11(projectRoot, pythonRoot);
4534
+ const absRoot = resolve12(projectRoot, pythonRoot);
4237
4535
  dataDb2.exec("DELETE FROM massu_py_imports");
4238
4536
  const insertStmt = dataDb2.prepare(
4239
4537
  "INSERT INTO massu_py_imports (source_file, target_file, import_type, imported_names, line) VALUES (?, ?, ?, ?, ?)"
@@ -4247,7 +4545,7 @@ function buildPythonImportIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__
4247
4545
  });
4248
4546
  const batch = [];
4249
4547
  for (const absFile of files) {
4250
- const relFile = relative2(projectRoot, absFile);
4548
+ const relFile = relative3(projectRoot, absFile);
4251
4549
  let source;
4252
4550
  try {
4253
4551
  source = readFileSync10(absFile, "utf-8");
@@ -4516,12 +4814,12 @@ var init_route_parser = __esm({
4516
4814
  });
4517
4815
 
4518
4816
  // src/python/route-indexer.ts
4519
- import { readFileSync as readFileSync11, readdirSync as readdirSync9 } from "fs";
4520
- import { join as join8, relative as relative3 } from "path";
4817
+ import { readFileSync as readFileSync11, readdirSync as readdirSync10 } from "fs";
4818
+ import { join as join8, relative as relative4 } from "path";
4521
4819
  function walkPyFiles(dir, excludeDirs) {
4522
4820
  const files = [];
4523
4821
  try {
4524
- const entries = readdirSync9(dir, { withFileTypes: true });
4822
+ const entries = readdirSync10(dir, { withFileTypes: true });
4525
4823
  for (const entry of entries) {
4526
4824
  if (entry.isDirectory()) {
4527
4825
  if (excludeDirs.includes(entry.name)) continue;
@@ -4546,7 +4844,7 @@ function buildPythonRouteIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__"
4546
4844
  let count = 0;
4547
4845
  dataDb2.transaction(() => {
4548
4846
  for (const absFile of files) {
4549
- const relFile = relative3(projectRoot, absFile);
4847
+ const relFile = relative4(projectRoot, absFile);
4550
4848
  let source;
4551
4849
  try {
4552
4850
  source = readFileSync11(absFile, "utf-8");
@@ -4760,12 +5058,12 @@ var init_model_parser = __esm({
4760
5058
  });
4761
5059
 
4762
5060
  // src/python/model-indexer.ts
4763
- import { readFileSync as readFileSync12, readdirSync as readdirSync10 } from "fs";
4764
- import { join as join9, relative as relative4 } from "path";
5061
+ import { readFileSync as readFileSync12, readdirSync as readdirSync11 } from "fs";
5062
+ import { join as join9, relative as relative5 } from "path";
4765
5063
  function walkPyFiles2(dir, excludeDirs) {
4766
5064
  const files = [];
4767
5065
  try {
4768
- const entries = readdirSync10(dir, { withFileTypes: true });
5066
+ const entries = readdirSync11(dir, { withFileTypes: true });
4769
5067
  for (const entry of entries) {
4770
5068
  if (entry.isDirectory()) {
4771
5069
  if (excludeDirs.includes(entry.name)) continue;
@@ -4793,7 +5091,7 @@ function buildPythonModelIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__"
4793
5091
  let count = 0;
4794
5092
  dataDb2.transaction(() => {
4795
5093
  for (const absFile of files) {
4796
- const relFile = relative4(projectRoot, absFile);
5094
+ const relFile = relative5(projectRoot, absFile);
4797
5095
  let source;
4798
5096
  try {
4799
5097
  source = readFileSync12(absFile, "utf-8");
@@ -5056,8 +5354,8 @@ var init_migration_parser = __esm({
5056
5354
  });
5057
5355
 
5058
5356
  // src/python/migration-indexer.ts
5059
- import { readFileSync as readFileSync13, readdirSync as readdirSync11 } from "fs";
5060
- import { join as join10, relative as relative5 } from "path";
5357
+ import { readFileSync as readFileSync13, readdirSync as readdirSync12 } from "fs";
5358
+ import { join as join10, relative as relative6 } from "path";
5061
5359
  function buildPythonMigrationIndex(dataDb2, alembicDir) {
5062
5360
  const projectRoot = getProjectRoot();
5063
5361
  const absDir = join10(projectRoot, alembicDir);
@@ -5065,10 +5363,10 @@ function buildPythonMigrationIndex(dataDb2, alembicDir) {
5065
5363
  const versionsDir = join10(absDir, "versions");
5066
5364
  let files = [];
5067
5365
  try {
5068
- files = readdirSync11(versionsDir).filter((f) => f.endsWith(".py")).map((f) => join10(versionsDir, f));
5366
+ files = readdirSync12(versionsDir).filter((f) => f.endsWith(".py")).map((f) => join10(versionsDir, f));
5069
5367
  } catch {
5070
5368
  try {
5071
- files = readdirSync11(absDir).filter((f) => f.endsWith(".py") && f !== "env.py").map((f) => join10(absDir, f));
5369
+ files = readdirSync12(absDir).filter((f) => f.endsWith(".py") && f !== "env.py").map((f) => join10(absDir, f));
5072
5370
  } catch {
5073
5371
  }
5074
5372
  }
@@ -5093,7 +5391,7 @@ function buildPythonMigrationIndex(dataDb2, alembicDir) {
5093
5391
  rows.push({
5094
5392
  revision: parsed.revision,
5095
5393
  downRevision: parsed.downRevision,
5096
- file: relative5(projectRoot, absFile),
5394
+ file: relative6(projectRoot, absFile),
5097
5395
  description: parsed.description,
5098
5396
  operations: JSON.stringify(parsed.operations)
5099
5397
  });
@@ -5116,8 +5414,8 @@ var init_migration_indexer = __esm({
5116
5414
  });
5117
5415
 
5118
5416
  // src/python/coupling-detector.ts
5119
- import { readFileSync as readFileSync14, readdirSync as readdirSync12 } from "fs";
5120
- import { join as join11, relative as relative6 } from "path";
5417
+ import { readFileSync as readFileSync14, readdirSync as readdirSync13 } from "fs";
5418
+ import { join as join11, relative as relative7 } from "path";
5121
5419
  function buildPythonCouplingIndex(dataDb2) {
5122
5420
  const projectRoot = getProjectRoot();
5123
5421
  const config = getConfig();
@@ -5150,7 +5448,7 @@ function buildPythonCouplingIndex(dataDb2) {
5150
5448
  ];
5151
5449
  dataDb2.transaction(() => {
5152
5450
  for (const absFile of frontendFiles) {
5153
- const relFile = relative6(projectRoot, absFile);
5451
+ const relFile = relative7(projectRoot, absFile);
5154
5452
  let source;
5155
5453
  try {
5156
5454
  source = readFileSync14(absFile, "utf-8");
@@ -5181,7 +5479,7 @@ function walkFrontendFiles(dir) {
5181
5479
  const files = [];
5182
5480
  const exclude = ["node_modules", ".next", "dist", ".git", "__pycache__", ".venv", "venv"];
5183
5481
  try {
5184
- const entries = readdirSync12(dir, { withFileTypes: true });
5482
+ const entries = readdirSync13(dir, { withFileTypes: true });
5185
5483
  for (const entry of entries) {
5186
5484
  if (entry.isDirectory()) {
5187
5485
  if (exclude.includes(entry.name)) continue;
@@ -5559,7 +5857,7 @@ var init_memory_tools = __esm({
5559
5857
 
5560
5858
  // src/docs-tools.ts
5561
5859
  import { readFileSync as readFileSync15, existsSync as existsSync13 } from "fs";
5562
- import { resolve as resolve12, basename as basename3 } from "path";
5860
+ import { resolve as resolve13, basename as basename4 } from "path";
5563
5861
  function p2(baseName) {
5564
5862
  return `${getConfig().toolPrefix}_${baseName}`;
5565
5863
  }
@@ -5626,7 +5924,7 @@ function matchesPattern(filePath, pattern) {
5626
5924
  function findAffectedMappings(docsMap, changedFiles) {
5627
5925
  const affected = /* @__PURE__ */ new Map();
5628
5926
  for (const file of changedFiles) {
5629
- const fileName = basename3(file);
5927
+ const fileName = basename4(file);
5630
5928
  for (const mapping of docsMap.mappings) {
5631
5929
  let matched = false;
5632
5930
  for (const routePattern of mapping.appRoutes) {
@@ -5687,9 +5985,9 @@ function extractFrontmatter(content) {
5687
5985
  }
5688
5986
  function extractProcedureNames(routerPath) {
5689
5987
  const root = getProjectRoot();
5690
- const absPath = ensureWithinRoot(resolve12(getResolvedPaths().srcDir, "..", routerPath), root);
5988
+ const absPath = ensureWithinRoot(resolve13(getResolvedPaths().srcDir, "..", routerPath), root);
5691
5989
  if (!existsSync13(absPath)) {
5692
- const altPath = ensureWithinRoot(resolve12(getResolvedPaths().srcDir, "../server/api/routers", basename3(routerPath)), root);
5990
+ const altPath = ensureWithinRoot(resolve13(getResolvedPaths().srcDir, "../server/api/routers", basename4(routerPath)), root);
5693
5991
  if (!existsSync13(altPath)) return [];
5694
5992
  return extractProcedureNamesFromContent(readFileSync15(altPath, "utf-8"));
5695
5993
  }
@@ -5733,7 +6031,7 @@ function handleDocsAudit(args2) {
5733
6031
  for (const [mappingId, triggeringFiles] of affectedMappings) {
5734
6032
  const mapping = docsMap.mappings.find((m) => m.id === mappingId);
5735
6033
  if (!mapping) continue;
5736
- const helpPagePath = ensureWithinRoot(resolve12(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
6034
+ const helpPagePath = ensureWithinRoot(resolve13(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
5737
6035
  if (!existsSync13(helpPagePath)) {
5738
6036
  results.push({
5739
6037
  helpPage: mapping.helpPage,
@@ -5751,7 +6049,7 @@ function handleDocsAudit(args2) {
5751
6049
  const frontmatter = extractFrontmatter(content);
5752
6050
  const staleReasons = [];
5753
6051
  for (const file of triggeringFiles) {
5754
- const fileName = basename3(file);
6052
+ const fileName = basename4(file);
5755
6053
  if (mapping.routers.includes(fileName)) {
5756
6054
  const procedures = extractProcedureNames(file);
5757
6055
  const undocumented = procedures.filter((p19) => !contentMentions(content, p19));
@@ -5782,11 +6080,11 @@ function handleDocsAudit(args2) {
5782
6080
  reason: staleReasons.length > 0 ? staleReasons.join("; ") : "Content appears current",
5783
6081
  sections,
5784
6082
  changedFiles: triggeringFiles,
5785
- suggestedAction: status === "STALE" ? `Review and update ${mapping.helpPage} to reflect changes in: ${triggeringFiles.map((f) => basename3(f)).join(", ")}` : "No action needed"
6083
+ suggestedAction: status === "STALE" ? `Review and update ${mapping.helpPage} to reflect changes in: ${triggeringFiles.map((f) => basename4(f)).join(", ")}` : "No action needed"
5786
6084
  });
5787
6085
  for (const [guideName, parentId] of Object.entries(docsMap.userGuideInheritance.examples)) {
5788
6086
  if (parentId === mappingId) {
5789
- const guidePath = ensureWithinRoot(resolve12(getResolvedPaths().helpSitePath, `pages/user-guides/${guideName}/index.mdx`), getProjectRoot());
6087
+ const guidePath = ensureWithinRoot(resolve13(getResolvedPaths().helpSitePath, `pages/user-guides/${guideName}/index.mdx`), getProjectRoot());
5790
6088
  if (existsSync13(guidePath)) {
5791
6089
  const guideContent = readFileSync15(guidePath, "utf-8");
5792
6090
  const guideFrontmatter = extractFrontmatter(guideContent);
@@ -5821,7 +6119,7 @@ function handleDocsCoverage(args2) {
5821
6119
  const gaps = [];
5822
6120
  const mappings = filterDomain ? docsMap.mappings.filter((m) => m.id === filterDomain) : docsMap.mappings;
5823
6121
  for (const mapping of mappings) {
5824
- const helpPagePath = ensureWithinRoot(resolve12(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
6122
+ const helpPagePath = ensureWithinRoot(resolve13(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
5825
6123
  const exists = existsSync13(helpPagePath);
5826
6124
  let hasContent = false;
5827
6125
  let lineCount = 0;
@@ -6170,7 +6468,7 @@ var init_observability_tools = __esm({
6170
6468
 
6171
6469
  // src/sentinel-db.ts
6172
6470
  import { existsSync as existsSync14 } from "fs";
6173
- import { resolve as resolve13 } from "path";
6471
+ import { resolve as resolve14 } from "path";
6174
6472
  function parsePortalScope(raw) {
6175
6473
  if (!raw) return [];
6176
6474
  try {
@@ -6406,22 +6704,22 @@ function validateFeatures(db, domainFilter) {
6406
6704
  const missingProcedures = [];
6407
6705
  const missingPages = [];
6408
6706
  for (const comp of components) {
6409
- const absPath = resolve13(PROJECT_ROOT, comp.component_file);
6707
+ const absPath = resolve14(PROJECT_ROOT, comp.component_file);
6410
6708
  if (!existsSync14(absPath)) {
6411
6709
  missingComponents.push(comp.component_file);
6412
6710
  }
6413
6711
  }
6414
6712
  for (const proc of procedures) {
6415
- const routerPath = resolve13(PROJECT_ROOT, `src/server/api/routers/${proc.router_name}.ts`);
6713
+ const routerPath = resolve14(PROJECT_ROOT, `src/server/api/routers/${proc.router_name}.ts`);
6416
6714
  if (!existsSync14(routerPath)) {
6417
6715
  missingProcedures.push({ router: proc.router_name, procedure: proc.procedure_name });
6418
6716
  }
6419
6717
  }
6420
6718
  for (const page of pages) {
6421
6719
  const routeToPath = page.page_route.replace(/^\/(portal-[^/]+\/)?/, "src/app/").replace(/\/$/, "") + "/page.tsx";
6422
- const absPath = resolve13(PROJECT_ROOT, routeToPath);
6720
+ const absPath = resolve14(PROJECT_ROOT, routeToPath);
6423
6721
  if (page.page_route.startsWith("/") && !existsSync14(absPath)) {
6424
- const altPath = resolve13(PROJECT_ROOT, `src/app${page.page_route}/page.tsx`);
6722
+ const altPath = resolve14(PROJECT_ROOT, `src/app${page.page_route}/page.tsx`);
6425
6723
  if (!existsSync14(altPath)) {
6426
6724
  missingPages.push(page.page_route);
6427
6725
  }
@@ -6946,8 +7244,8 @@ var init_sentinel_tools = __esm({
6946
7244
  });
6947
7245
 
6948
7246
  // src/sentinel-scanner.ts
6949
- import { readFileSync as readFileSync16, existsSync as existsSync15, readdirSync as readdirSync13, statSync as statSync4 } from "fs";
6950
- import { resolve as resolve14, join as join12, basename as basename4, dirname as dirname9, relative as relative7 } from "path";
7247
+ import { readFileSync as readFileSync16, existsSync as existsSync15, readdirSync as readdirSync14, statSync as statSync5 } from "fs";
7248
+ import { resolve as resolve15, join as join12, basename as basename5, dirname as dirname9, relative as relative8 } from "path";
6951
7249
  function inferDomain(filePath) {
6952
7250
  const domains = getConfig().domains;
6953
7251
  const path = filePath.toLowerCase();
@@ -7076,10 +7374,10 @@ function scanComponentExports(dataDb2) {
7076
7374
  const projectRoot = getProjectRoot();
7077
7375
  const componentsBase = config.paths.components ?? config.paths.source + "/components";
7078
7376
  const componentDirs = [];
7079
- const basePath = resolve14(projectRoot, componentsBase);
7377
+ const basePath = resolve15(projectRoot, componentsBase);
7080
7378
  if (existsSync15(basePath)) {
7081
7379
  try {
7082
- const entries = readdirSync13(basePath, { withFileTypes: true });
7380
+ const entries = readdirSync14(basePath, { withFileTypes: true });
7083
7381
  for (const entry of entries) {
7084
7382
  if (entry.isDirectory()) {
7085
7383
  componentDirs.push(componentsBase + "/" + entry.name);
@@ -7089,11 +7387,11 @@ function scanComponentExports(dataDb2) {
7089
7387
  }
7090
7388
  }
7091
7389
  for (const dir of componentDirs) {
7092
- const absDir = resolve14(projectRoot, dir);
7390
+ const absDir = resolve15(projectRoot, dir);
7093
7391
  if (!existsSync15(absDir)) continue;
7094
7392
  const files = walkDir(absDir).filter((f) => f.endsWith(".tsx") || f.endsWith(".ts"));
7095
7393
  for (const file of files) {
7096
- const relPath = relative7(projectRoot, file);
7394
+ const relPath = relative8(projectRoot, file);
7097
7395
  const source = readFileSync16(file, "utf-8");
7098
7396
  const annotations = parseFeatureAnnotations(source);
7099
7397
  if (annotations.length > 0) {
@@ -7120,7 +7418,7 @@ function scanComponentExports(dataDb2) {
7120
7418
  if (hasHandlers && exportMatch) {
7121
7419
  const componentName = exportMatch[1];
7122
7420
  const domain = inferDomain(relPath);
7123
- const subdomain = basename4(dirname9(relPath));
7421
+ const subdomain = basename5(dirname9(relPath));
7124
7422
  const featureKey = `component.${subdomain}.${componentName.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "")}`;
7125
7423
  if (!annotations.some((a) => a.featureKey === featureKey)) {
7126
7424
  features.push({
@@ -7143,11 +7441,11 @@ function scanComponentExports(dataDb2) {
7143
7441
  function walkDir(dir) {
7144
7442
  const results = [];
7145
7443
  try {
7146
- const entries = readdirSync13(dir);
7444
+ const entries = readdirSync14(dir);
7147
7445
  for (const entry of entries) {
7148
7446
  const fullPath = join12(dir, entry);
7149
7447
  try {
7150
- const stat = statSync4(fullPath);
7448
+ const stat = statSync5(fullPath);
7151
7449
  if (stat.isDirectory()) {
7152
7450
  results.push(...walkDir(fullPath));
7153
7451
  } else {
@@ -8937,7 +9235,7 @@ var init_security_scorer = __esm({
8937
9235
 
8938
9236
  // src/dependency-scorer.ts
8939
9237
  import { existsSync as existsSync18, readFileSync as readFileSync19 } from "fs";
8940
- import { resolve as resolve15 } from "path";
9238
+ import { resolve as resolve16 } from "path";
8941
9239
  function p12(baseName) {
8942
9240
  return `${getConfig().toolPrefix}_${baseName}`;
8943
9241
  }
@@ -8969,7 +9267,7 @@ function calculateDepRisk(factors) {
8969
9267
  return Math.min(100, risk);
8970
9268
  }
8971
9269
  function getInstalledPackages(projectRoot) {
8972
- const pkgPath = resolve15(projectRoot, "package.json");
9270
+ const pkgPath = resolve16(projectRoot, "package.json");
8973
9271
  if (!existsSync18(pkgPath)) return /* @__PURE__ */ new Map();
8974
9272
  try {
8975
9273
  const pkg = JSON.parse(readFileSync19(pkgPath, "utf-8"));
@@ -9575,8 +9873,8 @@ var init_regression_detector = __esm({
9575
9873
 
9576
9874
  // src/knowledge-indexer.ts
9577
9875
  import { createHash as createHash2 } from "crypto";
9578
- import { readFileSync as readFileSync20, readdirSync as readdirSync14, statSync as statSync5, existsSync as existsSync19 } from "fs";
9579
- import { resolve as resolve16, relative as relative8, basename as basename5, extname } from "path";
9876
+ import { readFileSync as readFileSync20, readdirSync as readdirSync15, statSync as statSync6, existsSync as existsSync19 } from "fs";
9877
+ import { resolve as resolve17, relative as relative9, basename as basename6, extname } from "path";
9580
9878
  function getKnowledgePaths() {
9581
9879
  const resolved = getResolvedPaths();
9582
9880
  const config = getConfig();
@@ -9602,9 +9900,9 @@ function discoverMarkdownFiles(baseDir) {
9602
9900
  const files = [];
9603
9901
  function walk(dir) {
9604
9902
  try {
9605
- const entries = readdirSync14(dir, { withFileTypes: true });
9903
+ const entries = readdirSync15(dir, { withFileTypes: true });
9606
9904
  for (const entry of entries) {
9607
- const fullPath = resolve16(dir, entry.name);
9905
+ const fullPath = resolve17(dir, entry.name);
9608
9906
  if (entry.isDirectory()) {
9609
9907
  if (entry.name === "archive" && dir.includes("session-state")) continue;
9610
9908
  if (entry.name === "archive" && dir.includes("status")) continue;
@@ -9624,7 +9922,7 @@ function categorizeFile(filePath) {
9624
9922
  const paths = getKnowledgePaths();
9625
9923
  if (filePath.startsWith(paths.plansDir)) return "plan";
9626
9924
  if (filePath.startsWith(paths.docsDir)) {
9627
- const relFromDocs = relative8(paths.docsDir, filePath).replace(/\\/g, "/").toLowerCase();
9925
+ const relFromDocs = relative9(paths.docsDir, filePath).replace(/\\/g, "/").toLowerCase();
9628
9926
  if (relFromDocs.startsWith("plans/")) return "plan";
9629
9927
  if (relFromDocs.includes("architecture")) return "architecture";
9630
9928
  if (relFromDocs.includes("security")) return "security";
@@ -9640,7 +9938,7 @@ function categorizeFile(filePath) {
9640
9938
  }
9641
9939
  const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
9642
9940
  if (filePath.includes(`${claudeDirName}/projects/`) && filePath.includes("/memory/")) return "memory";
9643
- const rel = relative8(paths.claudeDir, filePath).replace(/\\/g, "/");
9941
+ const rel = relative9(paths.claudeDir, filePath).replace(/\\/g, "/");
9644
9942
  const firstDir = rel.split("/")[0];
9645
9943
  const knownCategories = getConfig().conventions?.knowledgeCategories ?? [
9646
9944
  "patterns",
@@ -9781,7 +10079,7 @@ function parseCorrections(content) {
9781
10079
  function extractTitle(content, filePath) {
9782
10080
  const h1Match = content.match(/^#\s+(.+)/m);
9783
10081
  if (h1Match) return h1Match[1].trim();
9784
- return basename5(filePath, ".md");
10082
+ return basename6(filePath, ".md");
9785
10083
  }
9786
10084
  function extractDescription2(content) {
9787
10085
  const fmMatch = content.match(/^---\s*\n[\s\S]*?description:\s*"?([^"\n]+)"?\s*\n[\s\S]*?---/);
@@ -9810,7 +10108,7 @@ function buildCrossReferences(db) {
9810
10108
  }
9811
10109
  }
9812
10110
  if (rule.reference_path) {
9813
- const patternName = basename5(rule.reference_path, ".md");
10111
+ const patternName = basename6(rule.reference_path, ".md");
9814
10112
  insertEdge.run("cr", rule.rule_id, "pattern", patternName, "references");
9815
10113
  edgeCount++;
9816
10114
  }
@@ -9932,7 +10230,7 @@ function indexAllKnowledge(db) {
9932
10230
  if (!existsSync19(filePath)) continue;
9933
10231
  const content = readFileSync20(filePath, "utf-8");
9934
10232
  const hash = hashContent(content);
9935
- 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);
10233
+ const relPath = filePath.startsWith(paths.claudeDir) ? relative9(paths.claudeDir, filePath) : filePath.startsWith(paths.plansDir) ? "plans/" + relative9(paths.plansDir, filePath) : filePath.startsWith(paths.docsDir) ? "docs/" + relative9(paths.docsDir, filePath) : filePath.startsWith(paths.memoryDir) ? `memory/${relative9(paths.memoryDir, filePath)}` : basename6(filePath);
9936
10234
  const category = categorizeFile(filePath);
9937
10235
  const title = extractTitle(content, filePath);
9938
10236
  const description = extractDescription2(content);
@@ -9946,10 +10244,10 @@ function indexAllKnowledge(db) {
9946
10244
  stats.chunksCreated++;
9947
10245
  }
9948
10246
  }
9949
- const fileName = basename5(filePath);
10247
+ const fileName = basename6(filePath);
9950
10248
  const fileNameLower = fileName.toLowerCase();
9951
10249
  const relPathLower = relPath.toLowerCase();
9952
- const claudeMdName = basename5(getResolvedPaths().claudeMdPath).toLowerCase();
10250
+ const claudeMdName = basename6(getResolvedPaths().claudeMdPath).toLowerCase();
9953
10251
  if (fileNameLower === claudeMdName || relPathLower.includes(claudeMdName)) {
9954
10252
  const crRules = parseCRTable(content);
9955
10253
  for (const rule of crRules) {
@@ -9983,12 +10281,12 @@ function indexAllKnowledge(db) {
9983
10281
  }
9984
10282
  }
9985
10283
  if (category === "commands" && fileName !== "_shared-preamble.md") {
9986
- const cmdName = basename5(filePath, ".md");
10284
+ const cmdName = basename6(filePath, ".md");
9987
10285
  insertChunk.run(docId, "command", cmdName, content.substring(0, 1e3), 1, null, JSON.stringify({ command_name: cmdName }));
9988
10286
  stats.chunksCreated++;
9989
10287
  }
9990
10288
  if (category === "plan") {
9991
- const planItemRegex = /^###\s+(P\d+-\d+):\s+(.+)$/gm;
10289
+ const planItemRegex = /^###\s+(P[-A-Z]*\d*-?\w+):\s+(.+)$/gm;
9992
10290
  let planMatch;
9993
10291
  while ((planMatch = planItemRegex.exec(content)) !== null) {
9994
10292
  insertChunk.run(docId, "pattern", planMatch[1], `${planMatch[1]}: ${planMatch[2]}`, null, null, JSON.stringify({ plan_item_id: planMatch[1] }));
@@ -10060,7 +10358,7 @@ function isKnowledgeStale(db) {
10060
10358
  }
10061
10359
  for (const filePath of files) {
10062
10360
  try {
10063
- const stat = statSync5(filePath);
10361
+ const stat = statSync6(filePath);
10064
10362
  if (stat.mtimeMs > lastIndexTime) return true;
10065
10363
  } catch {
10066
10364
  continue;
@@ -10082,8 +10380,8 @@ var init_knowledge_indexer = __esm({
10082
10380
  });
10083
10381
 
10084
10382
  // src/knowledge-tools.ts
10085
- import { readFileSync as readFileSync21, writeFileSync as writeFileSync3, appendFileSync, readdirSync as readdirSync15 } from "fs";
10086
- import { resolve as resolve17, basename as basename6 } from "path";
10383
+ import { readFileSync as readFileSync21, writeFileSync as writeFileSync3, appendFileSync, readdirSync as readdirSync16 } from "fs";
10384
+ import { resolve as resolve18, basename as basename7 } from "path";
10087
10385
  function p15(baseName) {
10088
10386
  return `${getConfig().toolPrefix}_${baseName}`;
10089
10387
  }
@@ -10820,7 +11118,7 @@ function handleCorrect(db, args2) {
10820
11118
  if (!wrong || !correction || !rule) {
10821
11119
  return text15("Error: wrong, correction, and rule are all required.");
10822
11120
  }
10823
- const correctionsPath = resolve17(getResolvedPaths().memoryDir, "corrections.md");
11121
+ const correctionsPath = resolve18(getResolvedPaths().memoryDir, "corrections.md");
10824
11122
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
10825
11123
  const title = rule.slice(0, 60);
10826
11124
  const entry = `
@@ -10882,7 +11180,7 @@ function handlePlan(db, args2) {
10882
11180
  AND kc.content LIKE ?
10883
11181
  ORDER BY kd.file_path DESC
10884
11182
  LIMIT 20
10885
- `).all(`%${basename6(file)}%`);
11183
+ `).all(`%${basename7(file)}%`);
10886
11184
  lines.push(`## Plans referencing \`${file}\` (${results.length} found)`);
10887
11185
  lines.push("");
10888
11186
  for (const r of results) {
@@ -11015,7 +11313,7 @@ function handleGaps(db, args2) {
11015
11313
  } else if (checkType === "routers") {
11016
11314
  try {
11017
11315
  const routersDir = getResolvedPaths().routersDir;
11018
- const routerFiles = readdirSync15(routersDir).filter((f) => f.endsWith(".ts") && !f.startsWith("_"));
11316
+ const routerFiles = readdirSync16(routersDir).filter((f) => f.endsWith(".ts") && !f.startsWith("_"));
11019
11317
  lines.push(`| Router | Knowledge Hits | Status |`);
11020
11318
  lines.push(`|--------|----------------|--------|`);
11021
11319
  for (const file of routerFiles) {
@@ -11912,7 +12210,7 @@ var init_python_tools = __esm({
11912
12210
  // src/mcp-bridge-tools.ts
11913
12211
  import { spawn } from "child_process";
11914
12212
  import { readFileSync as readFileSync22, existsSync as existsSync22 } from "fs";
11915
- import { resolve as resolve18 } from "path";
12213
+ import { resolve as resolve19 } from "path";
11916
12214
  function p17(baseName) {
11917
12215
  return `${getConfig().toolPrefix}_${baseName}`;
11918
12216
  }
@@ -11933,7 +12231,7 @@ function buildSubprocessEnv() {
11933
12231
  }
11934
12232
  function loadMcpConfig() {
11935
12233
  const root = getProjectRoot();
11936
- const mcpPath = resolve18(root, ".mcp.json");
12234
+ const mcpPath = resolve19(root, ".mcp.json");
11937
12235
  if (!existsSync22(mcpPath)) return {};
11938
12236
  try {
11939
12237
  const raw = JSON.parse(readFileSync22(mcpPath, "utf-8"));
@@ -11961,7 +12259,7 @@ async function mcpRequest(conn, method, params = {}) {
11961
12259
  method,
11962
12260
  params
11963
12261
  };
11964
- return new Promise((resolve22, reject) => {
12262
+ return new Promise((resolve23, reject) => {
11965
12263
  const timeout = setTimeout(() => {
11966
12264
  conn.process?.stdout?.removeListener("data", onData);
11967
12265
  reject(new Error(`MCP request timed out: ${method}`));
@@ -11981,7 +12279,7 @@ async function mcpRequest(conn, method, params = {}) {
11981
12279
  if (response.error) {
11982
12280
  reject(new Error(`MCP error ${response.error.code}: ${response.error.message}`));
11983
12281
  } else {
11984
- resolve22(response.result || {});
12282
+ resolve23(response.result || {});
11985
12283
  }
11986
12284
  return;
11987
12285
  }
@@ -12003,7 +12301,7 @@ async function connectToServer(name, config) {
12003
12301
  return existing;
12004
12302
  }
12005
12303
  const root = getProjectRoot();
12006
- const cwd = config.cwd ? resolve18(root, config.cwd) : root;
12304
+ const cwd = config.cwd ? resolve19(root, config.cwd) : root;
12007
12305
  const args2 = config.args || [];
12008
12306
  const proc = spawn(config.command, args2, {
12009
12307
  cwd,
@@ -12241,7 +12539,6 @@ var init_mcp_bridge_tools = __esm({
12241
12539
  });
12242
12540
  process.on("SIGTERM", () => {
12243
12541
  for (const [name] of connections) disconnectServer(name);
12244
- process.exit(0);
12245
12542
  });
12246
12543
  ENV_ALLOW_LIST = /* @__PURE__ */ new Set([
12247
12544
  "PATH",
@@ -12266,7 +12563,7 @@ var init_mcp_bridge_tools = __esm({
12266
12563
 
12267
12564
  // src/tools.ts
12268
12565
  import { readFileSync as readFileSync23, existsSync as existsSync23 } from "fs";
12269
- import { resolve as resolve19, basename as basename7 } from "path";
12566
+ import { resolve as resolve20, basename as basename8 } from "path";
12270
12567
  function prefix() {
12271
12568
  return getConfig().toolPrefix;
12272
12569
  }
@@ -12301,7 +12598,7 @@ function ensureIndexes(dataDb2, codegraphDb2, force = false) {
12301
12598
  if (config.python?.root) {
12302
12599
  const pythonRoot = config.python.root;
12303
12600
  const excludeDirs = config.python.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
12304
- if (force || isPythonDataStale(dataDb2, resolve19(getProjectRoot(), pythonRoot))) {
12601
+ if (force || isPythonDataStale(dataDb2, resolve20(getProjectRoot(), pythonRoot))) {
12305
12602
  const pyImports = buildPythonImportIndex(dataDb2, pythonRoot, excludeDirs);
12306
12603
  results.push(`Python imports: ${pyImports}`);
12307
12604
  const pyRoutes = buildPythonRouteIndex(dataDb2, pythonRoot, excludeDirs);
@@ -12705,7 +13002,7 @@ function handleContext(file, dataDb2, codegraphDb2) {
12705
13002
  try {
12706
13003
  const resolvedPaths = getResolvedPaths();
12707
13004
  const root = getProjectRoot();
12708
- const absFilePath = ensureWithinRoot(resolve19(resolvedPaths.srcDir, "..", file), root);
13005
+ const absFilePath = ensureWithinRoot(resolve20(resolvedPaths.srcDir, "..", file), root);
12709
13006
  if (existsSync23(absFilePath)) {
12710
13007
  const fileContent = readFileSync23(absFilePath, "utf-8").slice(0, 3e3);
12711
13008
  const keywords = [];
@@ -12801,7 +13098,7 @@ function handleContext(file, dataDb2, codegraphDb2) {
12801
13098
  WHERE o.files_involved LIKE ?
12802
13099
  ORDER BY o.importance DESC, o.created_at_epoch DESC
12803
13100
  LIMIT 5
12804
- `).all(`%${basename7(file)}%`);
13101
+ `).all(`%${basename8(file)}%`);
12805
13102
  if (fileObservations.length > 0) {
12806
13103
  lines.push("## Past Observations (This File)");
12807
13104
  for (const obs of fileObservations) {
@@ -12817,7 +13114,7 @@ function handleContext(file, dataDb2, codegraphDb2) {
12817
13114
  WHERE type = 'failed_attempt' AND files_involved LIKE ?
12818
13115
  ORDER BY recurrence_count DESC
12819
13116
  LIMIT 3
12820
- `).all(`%${basename7(file)}%`);
13117
+ `).all(`%${basename8(file)}%`);
12821
13118
  if (failures.length > 0) {
12822
13119
  lines.push("## Failed Attempts (DO NOT RETRY)");
12823
13120
  for (const f of failures) {
@@ -13131,7 +13428,7 @@ function handleSchema(args2) {
13131
13428
  lines.push("Checking all column references against Prisma schema...");
13132
13429
  lines.push("");
13133
13430
  const projectRoot = getProjectRoot();
13134
- const absPath = ensureWithinRoot(resolve19(projectRoot, file), projectRoot);
13431
+ const absPath = ensureWithinRoot(resolve20(projectRoot, file), projectRoot);
13135
13432
  if (!existsSync23(absPath)) {
13136
13433
  return text18(`File not found: ${file}`);
13137
13434
  }
@@ -13215,7 +13512,7 @@ var init_tools = __esm({
13215
13512
  // src/server.ts
13216
13513
  var server_exports = {};
13217
13514
  import { readFileSync as readFileSync24 } from "fs";
13218
- import { resolve as resolve20, dirname as dirname11 } from "path";
13515
+ import { resolve as resolve21, dirname as dirname11 } from "path";
13219
13516
  import { fileURLToPath as fileURLToPath4 } from "url";
13220
13517
  function getDb() {
13221
13518
  if (!codegraphDb) codegraphDb = getCodeGraphDb();
@@ -13310,7 +13607,7 @@ var init_server = __esm({
13310
13607
  __dirname4 = dirname11(fileURLToPath4(import.meta.url));
13311
13608
  PKG_VERSION = (() => {
13312
13609
  try {
13313
- const pkg = JSON.parse(readFileSync24(resolve20(__dirname4, "..", "package.json"), "utf-8"));
13610
+ const pkg = JSON.parse(readFileSync24(resolve21(__dirname4, "..", "package.json"), "utf-8"));
13314
13611
  return pkg.version ?? "0.0.0";
13315
13612
  } catch {
13316
13613
  return "0.0.0";
@@ -13375,7 +13672,7 @@ var init_server = __esm({
13375
13672
 
13376
13673
  // src/cli.ts
13377
13674
  import { readFileSync as readFileSync25 } from "fs";
13378
- import { resolve as resolve21, dirname as dirname12 } from "path";
13675
+ import { resolve as resolve22, dirname as dirname12 } from "path";
13379
13676
  import { fileURLToPath as fileURLToPath5 } from "url";
13380
13677
  var __filename4 = fileURLToPath5(import.meta.url);
13381
13678
  var __dirname5 = dirname12(__filename4);
@@ -13449,7 +13746,7 @@ Documentation: https://massu.ai/docs
13449
13746
  }
13450
13747
  function printVersion() {
13451
13748
  try {
13452
- const pkg = JSON.parse(readFileSync25(resolve21(__dirname5, "../package.json"), "utf-8"));
13749
+ const pkg = JSON.parse(readFileSync25(resolve22(__dirname5, "../package.json"), "utf-8"));
13453
13750
  console.log(`massu v${pkg.version}`);
13454
13751
  } catch {
13455
13752
  console.log("massu v0.1.0");