@massu/core 0.8.1 → 0.9.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.
package/dist/cli.js CHANGED
@@ -1488,9 +1488,9 @@ import { join } from "path";
1488
1488
  function ingestMemoryFile(db, sessionId, filePath) {
1489
1489
  if (!existsSync3(filePath)) return "skipped";
1490
1490
  const content = readFileSync2(filePath, "utf-8");
1491
- const basename8 = (filePath.split("/").pop() ?? "").replace(".md", "");
1491
+ const basename9 = (filePath.split("/").pop() ?? "").replace(".md", "");
1492
1492
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
1493
- let name = basename8;
1493
+ let name = basename9;
1494
1494
  let description = "";
1495
1495
  let type = "discovery";
1496
1496
  let confidence;
@@ -1503,7 +1503,7 @@ function ingestMemoryFile(db, sessionId, filePath) {
1503
1503
  fm[line.slice(0, sep).trim()] = line.slice(sep + 1).trim();
1504
1504
  }
1505
1505
  }
1506
- name = fm.name ?? basename8;
1506
+ name = fm.name ?? basename9;
1507
1507
  description = fm.description ?? "";
1508
1508
  type = fm.type ?? "discovery";
1509
1509
  confidence = fm.confidence != null ? Number(fm.confidence) : void 0;
@@ -1563,6 +1563,265 @@ var init_memory_file_ingest = __esm({
1563
1563
  }
1564
1564
  });
1565
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
+
1566
1825
  // src/commands/install-commands.ts
1567
1826
  var install_commands_exports = {};
1568
1827
  __export(install_commands_exports, {
@@ -1572,20 +1831,20 @@ __export(install_commands_exports, {
1572
1831
  resolveCommandsDir: () => resolveCommandsDir,
1573
1832
  runInstallCommands: () => runInstallCommands
1574
1833
  });
1575
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync, mkdirSync as mkdirSync2, readdirSync as readdirSync2, statSync } from "fs";
1576
- 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";
1577
1836
  import { fileURLToPath } from "url";
1578
1837
  function resolveAssetDir(assetName) {
1579
1838
  const cwd = process.cwd();
1580
- const nodeModulesPath = resolve3(cwd, "node_modules/@massu/core", assetName);
1839
+ const nodeModulesPath = resolve4(cwd, "node_modules/@massu/core", assetName);
1581
1840
  if (existsSync4(nodeModulesPath)) {
1582
1841
  return nodeModulesPath;
1583
1842
  }
1584
- const distRelPath = resolve3(__dirname, "..", assetName);
1843
+ const distRelPath = resolve4(__dirname, "..", assetName);
1585
1844
  if (existsSync4(distRelPath)) {
1586
1845
  return distRelPath;
1587
1846
  }
1588
- const srcRelPath = resolve3(__dirname, "../..", assetName);
1847
+ const srcRelPath = resolve4(__dirname, "../..", assetName);
1589
1848
  if (existsSync4(srcRelPath)) {
1590
1849
  return srcRelPath;
1591
1850
  }
@@ -1599,11 +1858,11 @@ function syncDirectory(sourceDir, targetDir) {
1599
1858
  if (!existsSync4(targetDir)) {
1600
1859
  mkdirSync2(targetDir, { recursive: true });
1601
1860
  }
1602
- const entries = readdirSync2(sourceDir);
1861
+ const entries = readdirSync3(sourceDir);
1603
1862
  for (const entry of entries) {
1604
- const sourcePath = resolve3(sourceDir, entry);
1605
- const targetPath = resolve3(targetDir, entry);
1606
- const entryStat = statSync(sourcePath);
1863
+ const sourcePath = resolve4(sourceDir, entry);
1864
+ const targetPath = resolve4(targetDir, entry);
1865
+ const entryStat = statSync2(sourcePath);
1607
1866
  if (entryStat.isDirectory()) {
1608
1867
  const subStats = syncDirectory(sourcePath, targetPath);
1609
1868
  stats.installed += subStats.installed;
@@ -1629,7 +1888,7 @@ function syncDirectory(sourceDir, targetDir) {
1629
1888
  }
1630
1889
  function installCommands(projectRoot) {
1631
1890
  const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
1632
- const targetDir = resolve3(projectRoot, claudeDirName, "commands");
1891
+ const targetDir = resolve4(projectRoot, claudeDirName, "commands");
1633
1892
  if (!existsSync4(targetDir)) {
1634
1893
  mkdirSync2(targetDir, { recursive: true });
1635
1894
  }
@@ -1644,7 +1903,7 @@ function installCommands(projectRoot) {
1644
1903
  }
1645
1904
  function installAll(projectRoot) {
1646
1905
  const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
1647
- const claudeDir = resolve3(projectRoot, claudeDirName);
1906
+ const claudeDir = resolve4(projectRoot, claudeDirName);
1648
1907
  const assets = {};
1649
1908
  let totalInstalled = 0;
1650
1909
  let totalUpdated = 0;
@@ -1654,7 +1913,7 @@ function installAll(projectRoot) {
1654
1913
  if (!sourceDir) {
1655
1914
  continue;
1656
1915
  }
1657
- const targetDir = resolve3(claudeDir, assetType.targetSubdir);
1916
+ const targetDir = resolve4(claudeDir, assetType.targetSubdir);
1658
1917
  const stats = syncDirectory(sourceDir, targetDir);
1659
1918
  assets[assetType.name] = stats;
1660
1919
  totalInstalled += stats.installed;
@@ -1714,6 +1973,7 @@ __export(init_exports, {
1714
1973
  buildHooksConfig: () => buildHooksConfig,
1715
1974
  detectFramework: () => detectFramework,
1716
1975
  detectPython: () => detectPython,
1976
+ generateClaudeMd: () => generateClaudeMd,
1717
1977
  generateConfig: () => generateConfig,
1718
1978
  initMemoryDir: () => initMemoryDir,
1719
1979
  installHooks: () => installHooks,
@@ -1721,8 +1981,8 @@ __export(init_exports, {
1721
1981
  resolveHooksDir: () => resolveHooksDir,
1722
1982
  runInit: () => runInit
1723
1983
  });
1724
- import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, readdirSync as readdirSync3 } from "fs";
1725
- 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";
1726
1986
  import { fileURLToPath as fileURLToPath2 } from "url";
1727
1987
  import { homedir as homedir2 } from "os";
1728
1988
  import { stringify as yamlStringify } from "yaml";
@@ -1733,7 +1993,7 @@ function detectFramework(projectRoot) {
1733
1993
  orm: "none",
1734
1994
  ui: "none"
1735
1995
  };
1736
- const pkgPath = resolve4(projectRoot, "package.json");
1996
+ const pkgPath = resolve5(projectRoot, "package.json");
1737
1997
  if (!existsSync5(pkgPath)) return result;
1738
1998
  try {
1739
1999
  const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
@@ -1770,7 +2030,7 @@ function detectPython(projectRoot) {
1770
2030
  alembicDir: null
1771
2031
  };
1772
2032
  const markers = ["pyproject.toml", "setup.py", "requirements.txt", "Pipfile"];
1773
- const hasMarker = markers.some((m) => existsSync5(resolve4(projectRoot, m)));
2033
+ const hasMarker = markers.some((m) => existsSync5(resolve5(projectRoot, m)));
1774
2034
  if (!hasMarker) return result;
1775
2035
  result.detected = true;
1776
2036
  const depFiles = [
@@ -1780,7 +2040,7 @@ function detectPython(projectRoot) {
1780
2040
  { file: "Pipfile", parser: parsePipfileDeps }
1781
2041
  ];
1782
2042
  for (const { file, parser } of depFiles) {
1783
- const filePath = resolve4(projectRoot, file);
2043
+ const filePath = resolve5(projectRoot, file);
1784
2044
  if (existsSync5(filePath)) {
1785
2045
  try {
1786
2046
  const content = readFileSync4(filePath, "utf-8");
@@ -1791,25 +2051,25 @@ function detectPython(projectRoot) {
1791
2051
  }
1792
2052
  }
1793
2053
  }
1794
- if (existsSync5(resolve4(projectRoot, "alembic.ini"))) {
2054
+ if (existsSync5(resolve5(projectRoot, "alembic.ini"))) {
1795
2055
  result.hasAlembic = true;
1796
- if (existsSync5(resolve4(projectRoot, "alembic"))) {
2056
+ if (existsSync5(resolve5(projectRoot, "alembic"))) {
1797
2057
  result.alembicDir = "alembic";
1798
2058
  }
1799
- } else if (existsSync5(resolve4(projectRoot, "alembic"))) {
2059
+ } else if (existsSync5(resolve5(projectRoot, "alembic"))) {
1800
2060
  result.hasAlembic = true;
1801
2061
  result.alembicDir = "alembic";
1802
2062
  }
1803
2063
  const candidateRoots = ["app", "src", "backend", "api"];
1804
2064
  for (const candidate of candidateRoots) {
1805
- const candidatePath = resolve4(projectRoot, candidate);
1806
- if (existsSync5(candidatePath) && existsSync5(resolve4(candidatePath, "__init__.py"))) {
2065
+ const candidatePath = resolve5(projectRoot, candidate);
2066
+ if (existsSync5(candidatePath) && existsSync5(resolve5(candidatePath, "__init__.py"))) {
1807
2067
  result.root = candidate;
1808
2068
  break;
1809
2069
  }
1810
2070
  if (existsSync5(candidatePath)) {
1811
2071
  try {
1812
- const files = readdirSync3(candidatePath);
2072
+ const files = readdirSync4(candidatePath);
1813
2073
  if (files.some((f) => f.endsWith(".py"))) {
1814
2074
  result.root = candidate;
1815
2075
  break;
@@ -1855,11 +2115,11 @@ function parsePipfileDeps(content) {
1855
2115
  return deps;
1856
2116
  }
1857
2117
  function generateConfig(projectRoot, framework) {
1858
- const configPath = resolve4(projectRoot, "massu.config.yaml");
2118
+ const configPath = resolve5(projectRoot, "massu.config.yaml");
1859
2119
  if (existsSync5(configPath)) {
1860
2120
  return false;
1861
2121
  }
1862
- const projectName = basename2(projectRoot);
2122
+ const projectName = basename3(projectRoot);
1863
2123
  const config = {
1864
2124
  project: {
1865
2125
  name: projectName,
@@ -1905,8 +2165,18 @@ ${yamlStringify(config)}`;
1905
2165
  writeFileSync2(configPath, yamlContent, "utf-8");
1906
2166
  return true;
1907
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
+ }
1908
2178
  function registerMcpServer(projectRoot) {
1909
- const mcpPath = resolve4(projectRoot, ".mcp.json");
2179
+ const mcpPath = resolve5(projectRoot, ".mcp.json");
1910
2180
  let existing = {};
1911
2181
  if (existsSync5(mcpPath)) {
1912
2182
  try {
@@ -2009,8 +2279,8 @@ function buildHooksConfig(hooksDir) {
2009
2279
  }
2010
2280
  function installHooks(projectRoot) {
2011
2281
  const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
2012
- const claudeDir = resolve4(projectRoot, claudeDirName);
2013
- const settingsPath = resolve4(claudeDir, "settings.local.json");
2282
+ const claudeDir = resolve5(projectRoot, claudeDirName);
2283
+ const settingsPath = resolve5(claudeDir, "settings.local.json");
2014
2284
  if (!existsSync5(claudeDir)) {
2015
2285
  mkdirSync3(claudeDir, { recursive: true });
2016
2286
  }
@@ -2036,16 +2306,16 @@ function installHooks(projectRoot) {
2036
2306
  }
2037
2307
  function initMemoryDir(projectRoot) {
2038
2308
  const encodedRoot = "-" + projectRoot.replace(/\//g, "-");
2039
- const memoryDir = resolve4(homedir2(), `.claude/projects/${encodedRoot}/memory`);
2309
+ const memoryDir = resolve5(homedir2(), `.claude/projects/${encodedRoot}/memory`);
2040
2310
  let created = false;
2041
2311
  if (!existsSync5(memoryDir)) {
2042
2312
  mkdirSync3(memoryDir, { recursive: true });
2043
2313
  created = true;
2044
2314
  }
2045
- const memoryMdPath = resolve4(memoryDir, "MEMORY.md");
2315
+ const memoryMdPath = resolve5(memoryDir, "MEMORY.md");
2046
2316
  let memoryMdCreated = false;
2047
2317
  if (!existsSync5(memoryMdPath)) {
2048
- const projectName = basename2(projectRoot);
2318
+ const projectName = basename3(projectRoot);
2049
2319
  const memoryContent = `# ${projectName} - Massu Memory
2050
2320
 
2051
2321
  ## Key Learnings
@@ -2073,9 +2343,9 @@ async function runInit() {
2073
2343
  console.log("");
2074
2344
  const framework = detectFramework(projectRoot);
2075
2345
  const frameworkParts = [];
2076
- if (framework.type !== "javascript") frameworkParts.push(capitalize(framework.type));
2346
+ if (framework.type !== "javascript") frameworkParts.push(capitalize2(framework.type));
2077
2347
  if (framework.ui !== "none") frameworkParts.push(formatName(framework.ui));
2078
- if (framework.orm !== "none") frameworkParts.push(capitalize(framework.orm));
2348
+ if (framework.orm !== "none") frameworkParts.push(capitalize2(framework.orm));
2079
2349
  if (framework.router !== "none") frameworkParts.push(framework.router.toUpperCase());
2080
2350
  const detected = frameworkParts.length > 0 ? frameworkParts.join(", ") : "JavaScript";
2081
2351
  console.log(` Detected: ${detected}`);
@@ -2087,6 +2357,12 @@ async function runInit() {
2087
2357
  if (python.hasAlembic) pyParts.push("Alembic");
2088
2358
  console.log(` Detected: ${pyParts.join(", ")} (root: ${python.root})`);
2089
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
+ }
2090
2366
  const configCreated = generateConfig(projectRoot, framework);
2091
2367
  if (configCreated) {
2092
2368
  console.log(" Created massu.config.yaml");
@@ -2120,8 +2396,8 @@ async function runInit() {
2120
2396
  try {
2121
2397
  const claudeDirName = ".claude";
2122
2398
  const encodedRoot = projectRoot.replace(/\//g, "-");
2123
- const computedMemoryDir = resolve4(homedir2(), claudeDirName, "projects", encodedRoot, "memory");
2124
- 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") : [];
2125
2401
  if (memFiles.length > 0) {
2126
2402
  const { getMemoryDb: getMemoryDb2 } = await Promise.resolve().then(() => (init_memory_db(), memory_db_exports));
2127
2403
  const db = getMemoryDb2();
@@ -2147,7 +2423,7 @@ async function runInit() {
2147
2423
  console.log("Documentation: https://massu.ai/docs");
2148
2424
  console.log("");
2149
2425
  }
2150
- function capitalize(str) {
2426
+ function capitalize2(str) {
2151
2427
  return str.charAt(0).toUpperCase() + str.slice(1);
2152
2428
  }
2153
2429
  function formatName(name) {
@@ -2159,13 +2435,14 @@ function formatName(name) {
2159
2435
  vue: "Vue",
2160
2436
  react: "React"
2161
2437
  };
2162
- return names[name] ?? capitalize(name);
2438
+ return names[name] ?? capitalize2(name);
2163
2439
  }
2164
2440
  var __filename2, __dirname2;
2165
2441
  var init_init = __esm({
2166
2442
  "src/commands/init.ts"() {
2167
2443
  "use strict";
2168
2444
  init_memory_file_ingest();
2445
+ init_claude_md_templates();
2169
2446
  init_config();
2170
2447
  init_install_commands();
2171
2448
  __filename2 = fileURLToPath2(import.meta.url);
@@ -2469,12 +2746,12 @@ __export(doctor_exports, {
2469
2746
  runDoctor: () => runDoctor,
2470
2747
  runValidateConfig: () => runValidateConfig
2471
2748
  });
2472
- import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "fs";
2473
- 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";
2474
2751
  import { fileURLToPath as fileURLToPath3 } from "url";
2475
2752
  import { parse as parseYaml2 } from "yaml";
2476
2753
  function checkConfig(projectRoot) {
2477
- const configPath = resolve5(projectRoot, "massu.config.yaml");
2754
+ const configPath = resolve6(projectRoot, "massu.config.yaml");
2478
2755
  if (!existsSync6(configPath)) {
2479
2756
  return { name: "Configuration", status: "fail", detail: "massu.config.yaml not found. Run: npx massu init" };
2480
2757
  }
@@ -2535,10 +2812,10 @@ function checkHooksConfig(projectRoot) {
2535
2812
  }
2536
2813
  }
2537
2814
  function checkHookFiles(projectRoot) {
2538
- const nodeModulesHooksDir = resolve5(projectRoot, "node_modules/@massu/core/dist/hooks");
2815
+ const nodeModulesHooksDir = resolve6(projectRoot, "node_modules/@massu/core/dist/hooks");
2539
2816
  let hooksDir = nodeModulesHooksDir;
2540
2817
  if (!existsSync6(nodeModulesHooksDir)) {
2541
- const devHooksDir = resolve5(__dirname3, "../../dist/hooks");
2818
+ const devHooksDir = resolve6(__dirname3, "../../dist/hooks");
2542
2819
  if (existsSync6(devHooksDir)) {
2543
2820
  hooksDir = devHooksDir;
2544
2821
  } else {
@@ -2547,7 +2824,7 @@ function checkHookFiles(projectRoot) {
2547
2824
  }
2548
2825
  const missing = [];
2549
2826
  for (const hookFile of EXPECTED_HOOKS) {
2550
- if (!existsSync6(resolve5(hooksDir, hookFile))) {
2827
+ if (!existsSync6(resolve6(hooksDir, hookFile))) {
2551
2828
  missing.push(hookFile);
2552
2829
  }
2553
2830
  }
@@ -2573,7 +2850,7 @@ function checkNodeVersion() {
2573
2850
  return { name: "Node.js", status: "fail", detail: `v${version} \u2014 Node.js 18+ is required` };
2574
2851
  }
2575
2852
  async function checkGitRepo(projectRoot) {
2576
- const gitDir = resolve5(projectRoot, ".git");
2853
+ const gitDir = resolve6(projectRoot, ".git");
2577
2854
  if (!existsSync6(gitDir)) {
2578
2855
  return { name: "Git Repository", status: "warn", detail: "Not a git repository (optional but recommended)" };
2579
2856
  }
@@ -2673,7 +2950,7 @@ async function checkLicenseStatus() {
2673
2950
  function checkPythonHealth(projectRoot) {
2674
2951
  const config = getConfig();
2675
2952
  if (!config.python?.root) return null;
2676
- const pythonRoot = resolve5(projectRoot, config.python.root);
2953
+ const pythonRoot = resolve6(projectRoot, config.python.root);
2677
2954
  if (!existsSync6(pythonRoot)) {
2678
2955
  return {
2679
2956
  name: "Python",
@@ -2688,15 +2965,15 @@ function checkPythonHealth(projectRoot) {
2688
2965
  function scanDir(dir, depth) {
2689
2966
  if (depth > 5) return;
2690
2967
  try {
2691
- const entries = readdirSync4(dir, { withFileTypes: true });
2968
+ const entries = readdirSync5(dir, { withFileTypes: true });
2692
2969
  for (const entry of entries) {
2693
2970
  if (entry.isDirectory()) {
2694
2971
  const excludeDirs = config.python?.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
2695
2972
  if (!excludeDirs.includes(entry.name)) {
2696
- const subdir = resolve5(dir, entry.name);
2697
- if (depth <= 2 && !existsSync6(resolve5(subdir, "__init__.py"))) {
2973
+ const subdir = resolve6(dir, entry.name);
2974
+ if (depth <= 2 && !existsSync6(resolve6(subdir, "__init__.py"))) {
2698
2975
  try {
2699
- const subEntries = readdirSync4(subdir);
2976
+ const subEntries = readdirSync5(subdir);
2700
2977
  if (subEntries.some((f) => f.endsWith(".py") && f !== "__init__.py")) {
2701
2978
  initPyMissing.push(entry.name);
2702
2979
  }
@@ -2743,6 +3020,29 @@ function checkPythonHealth(projectRoot) {
2743
3020
  detail: parts.join(", ")
2744
3021
  };
2745
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
+ }
2746
3046
  async function runDoctor() {
2747
3047
  const projectRoot = process.cwd();
2748
3048
  console.log("");
@@ -2760,7 +3060,8 @@ async function runDoctor() {
2760
3060
  await checkNativeModules(),
2761
3061
  checkNodeVersion(),
2762
3062
  await checkGitRepo(projectRoot),
2763
- await checkLicenseStatus()
3063
+ await checkLicenseStatus(),
3064
+ checkClaudeMd(projectRoot)
2764
3065
  ];
2765
3066
  const pythonCheck = checkPythonHealth(projectRoot);
2766
3067
  if (pythonCheck) checks.push(pythonCheck);
@@ -2789,7 +3090,7 @@ async function runDoctor() {
2789
3090
  }
2790
3091
  async function runValidateConfig() {
2791
3092
  const projectRoot = process.cwd();
2792
- const configPath = resolve5(projectRoot, "massu.config.yaml");
3093
+ const configPath = resolve6(projectRoot, "massu.config.yaml");
2793
3094
  if (!existsSync6(configPath)) {
2794
3095
  console.error("Error: massu.config.yaml not found in current directory");
2795
3096
  console.error("Run: npx massu init");
@@ -2880,7 +3181,7 @@ var init_install_hooks = __esm({
2880
3181
  // src/db.ts
2881
3182
  import Database2 from "better-sqlite3";
2882
3183
  import { dirname as dirname6, join as join3 } from "path";
2883
- 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";
2884
3185
  function getCodeGraphDb() {
2885
3186
  const dbPath = getResolvedPaths().codegraphDbPath;
2886
3187
  if (!existsSync7(dbPath)) {
@@ -3164,14 +3465,14 @@ function isPythonDataStale(dataDb2, pythonRoot) {
3164
3465
  const lastBuildTime = new Date(lastBuild.value).getTime();
3165
3466
  function checkDir(dir) {
3166
3467
  try {
3167
- const entries = readdirSync5(dir, { withFileTypes: true });
3468
+ const entries = readdirSync6(dir, { withFileTypes: true });
3168
3469
  for (const entry of entries) {
3169
3470
  const fullPath = join3(dir, entry.name);
3170
3471
  if (entry.isDirectory()) {
3171
3472
  if (["__pycache__", ".venv", "venv", "node_modules", ".mypy_cache", ".pytest_cache"].includes(entry.name)) continue;
3172
3473
  if (checkDir(fullPath)) return true;
3173
3474
  } else if (entry.name.endsWith(".py")) {
3174
- if (statSync2(fullPath).mtimeMs > lastBuildTime) return true;
3475
+ if (statSync3(fullPath).mtimeMs > lastBuildTime) return true;
3175
3476
  }
3176
3477
  }
3177
3478
  } catch {
@@ -3191,10 +3492,10 @@ var init_db = __esm({
3191
3492
  });
3192
3493
 
3193
3494
  // src/security-utils.ts
3194
- import { resolve as resolve6, normalize } from "path";
3495
+ import { resolve as resolve7, normalize } from "path";
3195
3496
  function ensureWithinRoot(filePath, projectRoot) {
3196
- const resolvedRoot = resolve6(projectRoot);
3197
- const resolvedPath = resolve6(resolvedRoot, filePath);
3497
+ const resolvedRoot = resolve7(projectRoot);
3498
+ const resolvedPath = resolve7(resolvedRoot, filePath);
3198
3499
  const normalizedPath = normalize(resolvedPath);
3199
3500
  const normalizedRoot = normalize(resolvedRoot);
3200
3501
  if (!normalizedPath.startsWith(normalizedRoot + "/") && normalizedPath !== normalizedRoot) {
@@ -3267,8 +3568,8 @@ var init_rules = __esm({
3267
3568
  });
3268
3569
 
3269
3570
  // src/import-resolver.ts
3270
- import { readFileSync as readFileSync6, existsSync as existsSync8, statSync as statSync3 } from "fs";
3271
- 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";
3272
3573
  function parseImports(source) {
3273
3574
  const imports = [];
3274
3575
  const lines = source.split("\n");
@@ -3324,9 +3625,9 @@ function resolveImportPath(specifier, fromFile) {
3324
3625
  let basePath;
3325
3626
  if (specifier.startsWith("@/")) {
3326
3627
  const paths = getResolvedPaths();
3327
- basePath = resolve7(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
3628
+ basePath = resolve8(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
3328
3629
  } else {
3329
- basePath = resolve7(dirname7(fromFile), specifier);
3630
+ basePath = resolve8(dirname7(fromFile), specifier);
3330
3631
  }
3331
3632
  if (existsSync8(basePath) && !isDirectory(basePath)) {
3332
3633
  return toRelative(basePath);
@@ -3348,7 +3649,7 @@ function resolveImportPath(specifier, fromFile) {
3348
3649
  }
3349
3650
  function isDirectory(path) {
3350
3651
  try {
3351
- return statSync3(path).isDirectory();
3652
+ return statSync4(path).isDirectory();
3352
3653
  } catch {
3353
3654
  return false;
3354
3655
  }
@@ -3376,7 +3677,7 @@ function buildImportIndex(dataDb2, codegraphDb2) {
3376
3677
  const batchSize = 500;
3377
3678
  let batch = [];
3378
3679
  for (const file of files) {
3379
- const absPath = ensureWithinRoot(resolve7(projectRoot, file.path), projectRoot);
3680
+ const absPath = ensureWithinRoot(resolve8(projectRoot, file.path), projectRoot);
3380
3681
  if (!existsSync8(absPath)) continue;
3381
3682
  let source;
3382
3683
  try {
@@ -3416,8 +3717,8 @@ var init_import_resolver = __esm({
3416
3717
  });
3417
3718
 
3418
3719
  // src/trpc-index.ts
3419
- import { readFileSync as readFileSync7, existsSync as existsSync9, readdirSync as readdirSync6 } from "fs";
3420
- 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";
3421
3722
  function parseRootRouter() {
3422
3723
  const paths = getResolvedPaths();
3423
3724
  const rootPath = paths.rootRouterPath;
@@ -3432,7 +3733,7 @@ function parseRootRouter() {
3432
3733
  while ((match = importRegex.exec(source)) !== null) {
3433
3734
  const variable = match[1];
3434
3735
  let filePath = match[2];
3435
- const fullPath = resolve8(paths.routersDir, filePath);
3736
+ const fullPath = resolve9(paths.routersDir, filePath);
3436
3737
  for (const ext of [".ts", ".tsx", ""]) {
3437
3738
  const candidate = fullPath + ext;
3438
3739
  const routersRelPath = getConfig().paths.routers ?? "src/server/api/routers";
@@ -3460,7 +3761,7 @@ function parseRootRouter() {
3460
3761
  return mappings;
3461
3762
  }
3462
3763
  function extractProcedures(routerFilePath) {
3463
- const absPath = resolve8(getProjectRoot(), routerFilePath);
3764
+ const absPath = resolve9(getProjectRoot(), routerFilePath);
3464
3765
  if (!existsSync9(absPath)) return [];
3465
3766
  const source = readFileSync7(absPath, "utf-8");
3466
3767
  const procedures = [];
@@ -3485,9 +3786,9 @@ function findUICallSites(routerKey, procedureName) {
3485
3786
  const root = getProjectRoot();
3486
3787
  const src = config.paths.source;
3487
3788
  const searchDirs = [
3488
- resolve8(root, config.paths.pages ?? src + "/app"),
3489
- resolve8(root, config.paths.components ?? src + "/components"),
3490
- 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")
3491
3792
  ];
3492
3793
  const searchPattern = `api.${routerKey}.${procedureName}`;
3493
3794
  for (const dir of searchDirs) {
@@ -3497,7 +3798,7 @@ function findUICallSites(routerKey, procedureName) {
3497
3798
  return callSites;
3498
3799
  }
3499
3800
  function searchDirectory(dir, pattern, results) {
3500
- const entries = readdirSync6(dir, { withFileTypes: true });
3801
+ const entries = readdirSync7(dir, { withFileTypes: true });
3501
3802
  for (const entry of entries) {
3502
3803
  const fullPath = join5(dir, entry.name);
3503
3804
  if (entry.isDirectory()) {
@@ -3569,7 +3870,7 @@ var init_trpc_index = __esm({
3569
3870
 
3570
3871
  // src/page-deps.ts
3571
3872
  import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
3572
- import { resolve as resolve9 } from "path";
3873
+ import { resolve as resolve10 } from "path";
3573
3874
  function deriveRoute(pageFile) {
3574
3875
  let route = pageFile.replace(/^src\/app/, "").replace(/\/page\.tsx?$/, "").replace(/\/page\.jsx?$/, "");
3575
3876
  return route || "/";
@@ -3607,7 +3908,7 @@ function findRouterCalls(files) {
3607
3908
  const routers = /* @__PURE__ */ new Set();
3608
3909
  const projectRoot = getProjectRoot();
3609
3910
  for (const file of files) {
3610
- const absPath = ensureWithinRoot(resolve9(projectRoot, file), projectRoot);
3911
+ const absPath = ensureWithinRoot(resolve10(projectRoot, file), projectRoot);
3611
3912
  if (!existsSync10(absPath)) continue;
3612
3913
  try {
3613
3914
  const source = readFileSync8(absPath, "utf-8");
@@ -3628,7 +3929,7 @@ function findTablesFromRouters(routerNames, dataDb2) {
3628
3929
  "SELECT DISTINCT router_file FROM massu_trpc_procedures WHERE router_name = ?"
3629
3930
  ).all(routerName);
3630
3931
  for (const proc of procs) {
3631
- const absPath = ensureWithinRoot(resolve9(getProjectRoot(), proc.router_file), getProjectRoot());
3932
+ const absPath = ensureWithinRoot(resolve10(getProjectRoot(), proc.router_file), getProjectRoot());
3632
3933
  if (!existsSync10(absPath)) continue;
3633
3934
  try {
3634
3935
  const source = readFileSync8(absPath, "utf-8");
@@ -3900,7 +4201,7 @@ var init_domains = __esm({
3900
4201
  });
3901
4202
 
3902
4203
  // src/schema-mapper.ts
3903
- 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";
3904
4205
  import { join as join6 } from "path";
3905
4206
  function parsePrismaSchema() {
3906
4207
  const schemaPath = getResolvedPaths().prismaSchemaPath;
@@ -3970,7 +4271,7 @@ function findColumnUsageInRouters(tableName) {
3970
4271
  return usage;
3971
4272
  }
3972
4273
  function scanDirectory(dir, tableName, usage) {
3973
- const entries = readdirSync7(dir, { withFileTypes: true });
4274
+ const entries = readdirSync8(dir, { withFileTypes: true });
3974
4275
  for (const entry of entries) {
3975
4276
  const fullPath = join6(dir, entry.name);
3976
4277
  if (entry.isDirectory()) {
@@ -4028,7 +4329,7 @@ function detectMismatches(models) {
4028
4329
  function findFilesUsingColumn(dir, column, tableName) {
4029
4330
  const result = [];
4030
4331
  if (!existsSync11(dir)) return result;
4031
- const entries = readdirSync7(dir, { withFileTypes: true });
4332
+ const entries = readdirSync8(dir, { withFileTypes: true });
4032
4333
  for (const entry of entries) {
4033
4334
  const fullPath = join6(dir, entry.name);
4034
4335
  if (entry.isDirectory()) {
@@ -4180,12 +4481,12 @@ var init_import_parser = __esm({
4180
4481
  });
4181
4482
 
4182
4483
  // src/python/import-resolver.ts
4183
- import { readFileSync as readFileSync10, existsSync as existsSync12, readdirSync as readdirSync8 } from "fs";
4184
- 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";
4185
4486
  function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
4186
4487
  const projectRoot = getProjectRoot();
4187
4488
  if (level > 0) {
4188
- let baseDir = dirname8(resolve11(projectRoot, fromFile));
4489
+ let baseDir = dirname8(resolve12(projectRoot, fromFile));
4189
4490
  for (let i = 1; i < level; i++) {
4190
4491
  baseDir = dirname8(baseDir);
4191
4492
  }
@@ -4197,25 +4498,25 @@ function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
4197
4498
  return tryResolvePythonPath(baseDir, projectRoot);
4198
4499
  }
4199
4500
  const parts = module.split(".");
4200
- const candidate = join7(resolve11(projectRoot, pythonRoot), ...parts);
4501
+ const candidate = join7(resolve12(projectRoot, pythonRoot), ...parts);
4201
4502
  return tryResolvePythonPath(candidate, projectRoot);
4202
4503
  }
4203
4504
  function tryResolvePythonPath(basePath, projectRoot) {
4204
4505
  if (existsSync12(basePath + ".py")) {
4205
- return relative2(projectRoot, basePath + ".py");
4506
+ return relative3(projectRoot, basePath + ".py");
4206
4507
  }
4207
4508
  if (existsSync12(join7(basePath, "__init__.py"))) {
4208
- return relative2(projectRoot, join7(basePath, "__init__.py"));
4509
+ return relative3(projectRoot, join7(basePath, "__init__.py"));
4209
4510
  }
4210
4511
  if (basePath.endsWith(".py") && existsSync12(basePath)) {
4211
- return relative2(projectRoot, basePath);
4512
+ return relative3(projectRoot, basePath);
4212
4513
  }
4213
4514
  return null;
4214
4515
  }
4215
4516
  function walkPythonFiles(dir, excludeDirs) {
4216
4517
  const files = [];
4217
4518
  try {
4218
- const entries = readdirSync8(dir, { withFileTypes: true });
4519
+ const entries = readdirSync9(dir, { withFileTypes: true });
4219
4520
  for (const entry of entries) {
4220
4521
  if (entry.isDirectory()) {
4221
4522
  if (excludeDirs.includes(entry.name)) continue;
@@ -4230,7 +4531,7 @@ function walkPythonFiles(dir, excludeDirs) {
4230
4531
  }
4231
4532
  function buildPythonImportIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
4232
4533
  const projectRoot = getProjectRoot();
4233
- const absRoot = resolve11(projectRoot, pythonRoot);
4534
+ const absRoot = resolve12(projectRoot, pythonRoot);
4234
4535
  dataDb2.exec("DELETE FROM massu_py_imports");
4235
4536
  const insertStmt = dataDb2.prepare(
4236
4537
  "INSERT INTO massu_py_imports (source_file, target_file, import_type, imported_names, line) VALUES (?, ?, ?, ?, ?)"
@@ -4244,7 +4545,7 @@ function buildPythonImportIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__
4244
4545
  });
4245
4546
  const batch = [];
4246
4547
  for (const absFile of files) {
4247
- const relFile = relative2(projectRoot, absFile);
4548
+ const relFile = relative3(projectRoot, absFile);
4248
4549
  let source;
4249
4550
  try {
4250
4551
  source = readFileSync10(absFile, "utf-8");
@@ -4513,12 +4814,12 @@ var init_route_parser = __esm({
4513
4814
  });
4514
4815
 
4515
4816
  // src/python/route-indexer.ts
4516
- import { readFileSync as readFileSync11, readdirSync as readdirSync9 } from "fs";
4517
- 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";
4518
4819
  function walkPyFiles(dir, excludeDirs) {
4519
4820
  const files = [];
4520
4821
  try {
4521
- const entries = readdirSync9(dir, { withFileTypes: true });
4822
+ const entries = readdirSync10(dir, { withFileTypes: true });
4522
4823
  for (const entry of entries) {
4523
4824
  if (entry.isDirectory()) {
4524
4825
  if (excludeDirs.includes(entry.name)) continue;
@@ -4543,7 +4844,7 @@ function buildPythonRouteIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__"
4543
4844
  let count = 0;
4544
4845
  dataDb2.transaction(() => {
4545
4846
  for (const absFile of files) {
4546
- const relFile = relative3(projectRoot, absFile);
4847
+ const relFile = relative4(projectRoot, absFile);
4547
4848
  let source;
4548
4849
  try {
4549
4850
  source = readFileSync11(absFile, "utf-8");
@@ -4757,12 +5058,12 @@ var init_model_parser = __esm({
4757
5058
  });
4758
5059
 
4759
5060
  // src/python/model-indexer.ts
4760
- import { readFileSync as readFileSync12, readdirSync as readdirSync10 } from "fs";
4761
- 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";
4762
5063
  function walkPyFiles2(dir, excludeDirs) {
4763
5064
  const files = [];
4764
5065
  try {
4765
- const entries = readdirSync10(dir, { withFileTypes: true });
5066
+ const entries = readdirSync11(dir, { withFileTypes: true });
4766
5067
  for (const entry of entries) {
4767
5068
  if (entry.isDirectory()) {
4768
5069
  if (excludeDirs.includes(entry.name)) continue;
@@ -4790,7 +5091,7 @@ function buildPythonModelIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__"
4790
5091
  let count = 0;
4791
5092
  dataDb2.transaction(() => {
4792
5093
  for (const absFile of files) {
4793
- const relFile = relative4(projectRoot, absFile);
5094
+ const relFile = relative5(projectRoot, absFile);
4794
5095
  let source;
4795
5096
  try {
4796
5097
  source = readFileSync12(absFile, "utf-8");
@@ -5053,8 +5354,8 @@ var init_migration_parser = __esm({
5053
5354
  });
5054
5355
 
5055
5356
  // src/python/migration-indexer.ts
5056
- import { readFileSync as readFileSync13, readdirSync as readdirSync11 } from "fs";
5057
- 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";
5058
5359
  function buildPythonMigrationIndex(dataDb2, alembicDir) {
5059
5360
  const projectRoot = getProjectRoot();
5060
5361
  const absDir = join10(projectRoot, alembicDir);
@@ -5062,10 +5363,10 @@ function buildPythonMigrationIndex(dataDb2, alembicDir) {
5062
5363
  const versionsDir = join10(absDir, "versions");
5063
5364
  let files = [];
5064
5365
  try {
5065
- 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));
5066
5367
  } catch {
5067
5368
  try {
5068
- 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));
5069
5370
  } catch {
5070
5371
  }
5071
5372
  }
@@ -5090,7 +5391,7 @@ function buildPythonMigrationIndex(dataDb2, alembicDir) {
5090
5391
  rows.push({
5091
5392
  revision: parsed.revision,
5092
5393
  downRevision: parsed.downRevision,
5093
- file: relative5(projectRoot, absFile),
5394
+ file: relative6(projectRoot, absFile),
5094
5395
  description: parsed.description,
5095
5396
  operations: JSON.stringify(parsed.operations)
5096
5397
  });
@@ -5113,8 +5414,8 @@ var init_migration_indexer = __esm({
5113
5414
  });
5114
5415
 
5115
5416
  // src/python/coupling-detector.ts
5116
- import { readFileSync as readFileSync14, readdirSync as readdirSync12 } from "fs";
5117
- 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";
5118
5419
  function buildPythonCouplingIndex(dataDb2) {
5119
5420
  const projectRoot = getProjectRoot();
5120
5421
  const config = getConfig();
@@ -5147,7 +5448,7 @@ function buildPythonCouplingIndex(dataDb2) {
5147
5448
  ];
5148
5449
  dataDb2.transaction(() => {
5149
5450
  for (const absFile of frontendFiles) {
5150
- const relFile = relative6(projectRoot, absFile);
5451
+ const relFile = relative7(projectRoot, absFile);
5151
5452
  let source;
5152
5453
  try {
5153
5454
  source = readFileSync14(absFile, "utf-8");
@@ -5178,7 +5479,7 @@ function walkFrontendFiles(dir) {
5178
5479
  const files = [];
5179
5480
  const exclude = ["node_modules", ".next", "dist", ".git", "__pycache__", ".venv", "venv"];
5180
5481
  try {
5181
- const entries = readdirSync12(dir, { withFileTypes: true });
5482
+ const entries = readdirSync13(dir, { withFileTypes: true });
5182
5483
  for (const entry of entries) {
5183
5484
  if (entry.isDirectory()) {
5184
5485
  if (exclude.includes(entry.name)) continue;
@@ -5556,7 +5857,7 @@ var init_memory_tools = __esm({
5556
5857
 
5557
5858
  // src/docs-tools.ts
5558
5859
  import { readFileSync as readFileSync15, existsSync as existsSync13 } from "fs";
5559
- import { resolve as resolve12, basename as basename3 } from "path";
5860
+ import { resolve as resolve13, basename as basename4 } from "path";
5560
5861
  function p2(baseName) {
5561
5862
  return `${getConfig().toolPrefix}_${baseName}`;
5562
5863
  }
@@ -5623,7 +5924,7 @@ function matchesPattern(filePath, pattern) {
5623
5924
  function findAffectedMappings(docsMap, changedFiles) {
5624
5925
  const affected = /* @__PURE__ */ new Map();
5625
5926
  for (const file of changedFiles) {
5626
- const fileName = basename3(file);
5927
+ const fileName = basename4(file);
5627
5928
  for (const mapping of docsMap.mappings) {
5628
5929
  let matched = false;
5629
5930
  for (const routePattern of mapping.appRoutes) {
@@ -5684,9 +5985,9 @@ function extractFrontmatter(content) {
5684
5985
  }
5685
5986
  function extractProcedureNames(routerPath) {
5686
5987
  const root = getProjectRoot();
5687
- const absPath = ensureWithinRoot(resolve12(getResolvedPaths().srcDir, "..", routerPath), root);
5988
+ const absPath = ensureWithinRoot(resolve13(getResolvedPaths().srcDir, "..", routerPath), root);
5688
5989
  if (!existsSync13(absPath)) {
5689
- 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);
5690
5991
  if (!existsSync13(altPath)) return [];
5691
5992
  return extractProcedureNamesFromContent(readFileSync15(altPath, "utf-8"));
5692
5993
  }
@@ -5730,7 +6031,7 @@ function handleDocsAudit(args2) {
5730
6031
  for (const [mappingId, triggeringFiles] of affectedMappings) {
5731
6032
  const mapping = docsMap.mappings.find((m) => m.id === mappingId);
5732
6033
  if (!mapping) continue;
5733
- const helpPagePath = ensureWithinRoot(resolve12(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
6034
+ const helpPagePath = ensureWithinRoot(resolve13(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
5734
6035
  if (!existsSync13(helpPagePath)) {
5735
6036
  results.push({
5736
6037
  helpPage: mapping.helpPage,
@@ -5748,7 +6049,7 @@ function handleDocsAudit(args2) {
5748
6049
  const frontmatter = extractFrontmatter(content);
5749
6050
  const staleReasons = [];
5750
6051
  for (const file of triggeringFiles) {
5751
- const fileName = basename3(file);
6052
+ const fileName = basename4(file);
5752
6053
  if (mapping.routers.includes(fileName)) {
5753
6054
  const procedures = extractProcedureNames(file);
5754
6055
  const undocumented = procedures.filter((p19) => !contentMentions(content, p19));
@@ -5779,11 +6080,11 @@ function handleDocsAudit(args2) {
5779
6080
  reason: staleReasons.length > 0 ? staleReasons.join("; ") : "Content appears current",
5780
6081
  sections,
5781
6082
  changedFiles: triggeringFiles,
5782
- 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"
5783
6084
  });
5784
6085
  for (const [guideName, parentId] of Object.entries(docsMap.userGuideInheritance.examples)) {
5785
6086
  if (parentId === mappingId) {
5786
- 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());
5787
6088
  if (existsSync13(guidePath)) {
5788
6089
  const guideContent = readFileSync15(guidePath, "utf-8");
5789
6090
  const guideFrontmatter = extractFrontmatter(guideContent);
@@ -5818,7 +6119,7 @@ function handleDocsCoverage(args2) {
5818
6119
  const gaps = [];
5819
6120
  const mappings = filterDomain ? docsMap.mappings.filter((m) => m.id === filterDomain) : docsMap.mappings;
5820
6121
  for (const mapping of mappings) {
5821
- const helpPagePath = ensureWithinRoot(resolve12(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
6122
+ const helpPagePath = ensureWithinRoot(resolve13(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
5822
6123
  const exists = existsSync13(helpPagePath);
5823
6124
  let hasContent = false;
5824
6125
  let lineCount = 0;
@@ -6167,7 +6468,7 @@ var init_observability_tools = __esm({
6167
6468
 
6168
6469
  // src/sentinel-db.ts
6169
6470
  import { existsSync as existsSync14 } from "fs";
6170
- import { resolve as resolve13 } from "path";
6471
+ import { resolve as resolve14 } from "path";
6171
6472
  function parsePortalScope(raw) {
6172
6473
  if (!raw) return [];
6173
6474
  try {
@@ -6403,22 +6704,22 @@ function validateFeatures(db, domainFilter) {
6403
6704
  const missingProcedures = [];
6404
6705
  const missingPages = [];
6405
6706
  for (const comp of components) {
6406
- const absPath = resolve13(PROJECT_ROOT, comp.component_file);
6707
+ const absPath = resolve14(PROJECT_ROOT, comp.component_file);
6407
6708
  if (!existsSync14(absPath)) {
6408
6709
  missingComponents.push(comp.component_file);
6409
6710
  }
6410
6711
  }
6411
6712
  for (const proc of procedures) {
6412
- 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`);
6413
6714
  if (!existsSync14(routerPath)) {
6414
6715
  missingProcedures.push({ router: proc.router_name, procedure: proc.procedure_name });
6415
6716
  }
6416
6717
  }
6417
6718
  for (const page of pages) {
6418
6719
  const routeToPath = page.page_route.replace(/^\/(portal-[^/]+\/)?/, "src/app/").replace(/\/$/, "") + "/page.tsx";
6419
- const absPath = resolve13(PROJECT_ROOT, routeToPath);
6720
+ const absPath = resolve14(PROJECT_ROOT, routeToPath);
6420
6721
  if (page.page_route.startsWith("/") && !existsSync14(absPath)) {
6421
- 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`);
6422
6723
  if (!existsSync14(altPath)) {
6423
6724
  missingPages.push(page.page_route);
6424
6725
  }
@@ -6943,8 +7244,8 @@ var init_sentinel_tools = __esm({
6943
7244
  });
6944
7245
 
6945
7246
  // src/sentinel-scanner.ts
6946
- import { readFileSync as readFileSync16, existsSync as existsSync15, readdirSync as readdirSync13, statSync as statSync4 } from "fs";
6947
- 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";
6948
7249
  function inferDomain(filePath) {
6949
7250
  const domains = getConfig().domains;
6950
7251
  const path = filePath.toLowerCase();
@@ -7073,10 +7374,10 @@ function scanComponentExports(dataDb2) {
7073
7374
  const projectRoot = getProjectRoot();
7074
7375
  const componentsBase = config.paths.components ?? config.paths.source + "/components";
7075
7376
  const componentDirs = [];
7076
- const basePath = resolve14(projectRoot, componentsBase);
7377
+ const basePath = resolve15(projectRoot, componentsBase);
7077
7378
  if (existsSync15(basePath)) {
7078
7379
  try {
7079
- const entries = readdirSync13(basePath, { withFileTypes: true });
7380
+ const entries = readdirSync14(basePath, { withFileTypes: true });
7080
7381
  for (const entry of entries) {
7081
7382
  if (entry.isDirectory()) {
7082
7383
  componentDirs.push(componentsBase + "/" + entry.name);
@@ -7086,11 +7387,11 @@ function scanComponentExports(dataDb2) {
7086
7387
  }
7087
7388
  }
7088
7389
  for (const dir of componentDirs) {
7089
- const absDir = resolve14(projectRoot, dir);
7390
+ const absDir = resolve15(projectRoot, dir);
7090
7391
  if (!existsSync15(absDir)) continue;
7091
7392
  const files = walkDir(absDir).filter((f) => f.endsWith(".tsx") || f.endsWith(".ts"));
7092
7393
  for (const file of files) {
7093
- const relPath = relative7(projectRoot, file);
7394
+ const relPath = relative8(projectRoot, file);
7094
7395
  const source = readFileSync16(file, "utf-8");
7095
7396
  const annotations = parseFeatureAnnotations(source);
7096
7397
  if (annotations.length > 0) {
@@ -7117,7 +7418,7 @@ function scanComponentExports(dataDb2) {
7117
7418
  if (hasHandlers && exportMatch) {
7118
7419
  const componentName = exportMatch[1];
7119
7420
  const domain = inferDomain(relPath);
7120
- const subdomain = basename4(dirname9(relPath));
7421
+ const subdomain = basename5(dirname9(relPath));
7121
7422
  const featureKey = `component.${subdomain}.${componentName.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "")}`;
7122
7423
  if (!annotations.some((a) => a.featureKey === featureKey)) {
7123
7424
  features.push({
@@ -7140,11 +7441,11 @@ function scanComponentExports(dataDb2) {
7140
7441
  function walkDir(dir) {
7141
7442
  const results = [];
7142
7443
  try {
7143
- const entries = readdirSync13(dir);
7444
+ const entries = readdirSync14(dir);
7144
7445
  for (const entry of entries) {
7145
7446
  const fullPath = join12(dir, entry);
7146
7447
  try {
7147
- const stat = statSync4(fullPath);
7448
+ const stat = statSync5(fullPath);
7148
7449
  if (stat.isDirectory()) {
7149
7450
  results.push(...walkDir(fullPath));
7150
7451
  } else {
@@ -8934,7 +9235,7 @@ var init_security_scorer = __esm({
8934
9235
 
8935
9236
  // src/dependency-scorer.ts
8936
9237
  import { existsSync as existsSync18, readFileSync as readFileSync19 } from "fs";
8937
- import { resolve as resolve15 } from "path";
9238
+ import { resolve as resolve16 } from "path";
8938
9239
  function p12(baseName) {
8939
9240
  return `${getConfig().toolPrefix}_${baseName}`;
8940
9241
  }
@@ -8966,7 +9267,7 @@ function calculateDepRisk(factors) {
8966
9267
  return Math.min(100, risk);
8967
9268
  }
8968
9269
  function getInstalledPackages(projectRoot) {
8969
- const pkgPath = resolve15(projectRoot, "package.json");
9270
+ const pkgPath = resolve16(projectRoot, "package.json");
8970
9271
  if (!existsSync18(pkgPath)) return /* @__PURE__ */ new Map();
8971
9272
  try {
8972
9273
  const pkg = JSON.parse(readFileSync19(pkgPath, "utf-8"));
@@ -9572,8 +9873,8 @@ var init_regression_detector = __esm({
9572
9873
 
9573
9874
  // src/knowledge-indexer.ts
9574
9875
  import { createHash as createHash2 } from "crypto";
9575
- import { readFileSync as readFileSync20, readdirSync as readdirSync14, statSync as statSync5, existsSync as existsSync19 } from "fs";
9576
- 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";
9577
9878
  function getKnowledgePaths() {
9578
9879
  const resolved = getResolvedPaths();
9579
9880
  const config = getConfig();
@@ -9599,9 +9900,9 @@ function discoverMarkdownFiles(baseDir) {
9599
9900
  const files = [];
9600
9901
  function walk(dir) {
9601
9902
  try {
9602
- const entries = readdirSync14(dir, { withFileTypes: true });
9903
+ const entries = readdirSync15(dir, { withFileTypes: true });
9603
9904
  for (const entry of entries) {
9604
- const fullPath = resolve16(dir, entry.name);
9905
+ const fullPath = resolve17(dir, entry.name);
9605
9906
  if (entry.isDirectory()) {
9606
9907
  if (entry.name === "archive" && dir.includes("session-state")) continue;
9607
9908
  if (entry.name === "archive" && dir.includes("status")) continue;
@@ -9621,7 +9922,7 @@ function categorizeFile(filePath) {
9621
9922
  const paths = getKnowledgePaths();
9622
9923
  if (filePath.startsWith(paths.plansDir)) return "plan";
9623
9924
  if (filePath.startsWith(paths.docsDir)) {
9624
- const relFromDocs = relative8(paths.docsDir, filePath).replace(/\\/g, "/").toLowerCase();
9925
+ const relFromDocs = relative9(paths.docsDir, filePath).replace(/\\/g, "/").toLowerCase();
9625
9926
  if (relFromDocs.startsWith("plans/")) return "plan";
9626
9927
  if (relFromDocs.includes("architecture")) return "architecture";
9627
9928
  if (relFromDocs.includes("security")) return "security";
@@ -9637,7 +9938,7 @@ function categorizeFile(filePath) {
9637
9938
  }
9638
9939
  const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
9639
9940
  if (filePath.includes(`${claudeDirName}/projects/`) && filePath.includes("/memory/")) return "memory";
9640
- const rel = relative8(paths.claudeDir, filePath).replace(/\\/g, "/");
9941
+ const rel = relative9(paths.claudeDir, filePath).replace(/\\/g, "/");
9641
9942
  const firstDir = rel.split("/")[0];
9642
9943
  const knownCategories = getConfig().conventions?.knowledgeCategories ?? [
9643
9944
  "patterns",
@@ -9778,7 +10079,7 @@ function parseCorrections(content) {
9778
10079
  function extractTitle(content, filePath) {
9779
10080
  const h1Match = content.match(/^#\s+(.+)/m);
9780
10081
  if (h1Match) return h1Match[1].trim();
9781
- return basename5(filePath, ".md");
10082
+ return basename6(filePath, ".md");
9782
10083
  }
9783
10084
  function extractDescription2(content) {
9784
10085
  const fmMatch = content.match(/^---\s*\n[\s\S]*?description:\s*"?([^"\n]+)"?\s*\n[\s\S]*?---/);
@@ -9807,7 +10108,7 @@ function buildCrossReferences(db) {
9807
10108
  }
9808
10109
  }
9809
10110
  if (rule.reference_path) {
9810
- const patternName = basename5(rule.reference_path, ".md");
10111
+ const patternName = basename6(rule.reference_path, ".md");
9811
10112
  insertEdge.run("cr", rule.rule_id, "pattern", patternName, "references");
9812
10113
  edgeCount++;
9813
10114
  }
@@ -9929,7 +10230,7 @@ function indexAllKnowledge(db) {
9929
10230
  if (!existsSync19(filePath)) continue;
9930
10231
  const content = readFileSync20(filePath, "utf-8");
9931
10232
  const hash = hashContent(content);
9932
- 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);
9933
10234
  const category = categorizeFile(filePath);
9934
10235
  const title = extractTitle(content, filePath);
9935
10236
  const description = extractDescription2(content);
@@ -9943,10 +10244,10 @@ function indexAllKnowledge(db) {
9943
10244
  stats.chunksCreated++;
9944
10245
  }
9945
10246
  }
9946
- const fileName = basename5(filePath);
10247
+ const fileName = basename6(filePath);
9947
10248
  const fileNameLower = fileName.toLowerCase();
9948
10249
  const relPathLower = relPath.toLowerCase();
9949
- const claudeMdName = basename5(getResolvedPaths().claudeMdPath).toLowerCase();
10250
+ const claudeMdName = basename6(getResolvedPaths().claudeMdPath).toLowerCase();
9950
10251
  if (fileNameLower === claudeMdName || relPathLower.includes(claudeMdName)) {
9951
10252
  const crRules = parseCRTable(content);
9952
10253
  for (const rule of crRules) {
@@ -9980,12 +10281,12 @@ function indexAllKnowledge(db) {
9980
10281
  }
9981
10282
  }
9982
10283
  if (category === "commands" && fileName !== "_shared-preamble.md") {
9983
- const cmdName = basename5(filePath, ".md");
10284
+ const cmdName = basename6(filePath, ".md");
9984
10285
  insertChunk.run(docId, "command", cmdName, content.substring(0, 1e3), 1, null, JSON.stringify({ command_name: cmdName }));
9985
10286
  stats.chunksCreated++;
9986
10287
  }
9987
10288
  if (category === "plan") {
9988
- const planItemRegex = /^###\s+(P\d+-\d+):\s+(.+)$/gm;
10289
+ const planItemRegex = /^###\s+(P[-A-Z]*\d*-?\w+):\s+(.+)$/gm;
9989
10290
  let planMatch;
9990
10291
  while ((planMatch = planItemRegex.exec(content)) !== null) {
9991
10292
  insertChunk.run(docId, "pattern", planMatch[1], `${planMatch[1]}: ${planMatch[2]}`, null, null, JSON.stringify({ plan_item_id: planMatch[1] }));
@@ -10057,7 +10358,7 @@ function isKnowledgeStale(db) {
10057
10358
  }
10058
10359
  for (const filePath of files) {
10059
10360
  try {
10060
- const stat = statSync5(filePath);
10361
+ const stat = statSync6(filePath);
10061
10362
  if (stat.mtimeMs > lastIndexTime) return true;
10062
10363
  } catch {
10063
10364
  continue;
@@ -10079,8 +10380,8 @@ var init_knowledge_indexer = __esm({
10079
10380
  });
10080
10381
 
10081
10382
  // src/knowledge-tools.ts
10082
- import { readFileSync as readFileSync21, writeFileSync as writeFileSync3, appendFileSync, readdirSync as readdirSync15 } from "fs";
10083
- 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";
10084
10385
  function p15(baseName) {
10085
10386
  return `${getConfig().toolPrefix}_${baseName}`;
10086
10387
  }
@@ -10817,7 +11118,7 @@ function handleCorrect(db, args2) {
10817
11118
  if (!wrong || !correction || !rule) {
10818
11119
  return text15("Error: wrong, correction, and rule are all required.");
10819
11120
  }
10820
- const correctionsPath = resolve17(getResolvedPaths().memoryDir, "corrections.md");
11121
+ const correctionsPath = resolve18(getResolvedPaths().memoryDir, "corrections.md");
10821
11122
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
10822
11123
  const title = rule.slice(0, 60);
10823
11124
  const entry = `
@@ -10879,7 +11180,7 @@ function handlePlan(db, args2) {
10879
11180
  AND kc.content LIKE ?
10880
11181
  ORDER BY kd.file_path DESC
10881
11182
  LIMIT 20
10882
- `).all(`%${basename6(file)}%`);
11183
+ `).all(`%${basename7(file)}%`);
10883
11184
  lines.push(`## Plans referencing \`${file}\` (${results.length} found)`);
10884
11185
  lines.push("");
10885
11186
  for (const r of results) {
@@ -11012,7 +11313,7 @@ function handleGaps(db, args2) {
11012
11313
  } else if (checkType === "routers") {
11013
11314
  try {
11014
11315
  const routersDir = getResolvedPaths().routersDir;
11015
- const routerFiles = readdirSync15(routersDir).filter((f) => f.endsWith(".ts") && !f.startsWith("_"));
11316
+ const routerFiles = readdirSync16(routersDir).filter((f) => f.endsWith(".ts") && !f.startsWith("_"));
11016
11317
  lines.push(`| Router | Knowledge Hits | Status |`);
11017
11318
  lines.push(`|--------|----------------|--------|`);
11018
11319
  for (const file of routerFiles) {
@@ -11909,7 +12210,7 @@ var init_python_tools = __esm({
11909
12210
  // src/mcp-bridge-tools.ts
11910
12211
  import { spawn } from "child_process";
11911
12212
  import { readFileSync as readFileSync22, existsSync as existsSync22 } from "fs";
11912
- import { resolve as resolve18 } from "path";
12213
+ import { resolve as resolve19 } from "path";
11913
12214
  function p17(baseName) {
11914
12215
  return `${getConfig().toolPrefix}_${baseName}`;
11915
12216
  }
@@ -11930,7 +12231,7 @@ function buildSubprocessEnv() {
11930
12231
  }
11931
12232
  function loadMcpConfig() {
11932
12233
  const root = getProjectRoot();
11933
- const mcpPath = resolve18(root, ".mcp.json");
12234
+ const mcpPath = resolve19(root, ".mcp.json");
11934
12235
  if (!existsSync22(mcpPath)) return {};
11935
12236
  try {
11936
12237
  const raw = JSON.parse(readFileSync22(mcpPath, "utf-8"));
@@ -11958,7 +12259,7 @@ async function mcpRequest(conn, method, params = {}) {
11958
12259
  method,
11959
12260
  params
11960
12261
  };
11961
- return new Promise((resolve22, reject) => {
12262
+ return new Promise((resolve23, reject) => {
11962
12263
  const timeout = setTimeout(() => {
11963
12264
  conn.process?.stdout?.removeListener("data", onData);
11964
12265
  reject(new Error(`MCP request timed out: ${method}`));
@@ -11978,7 +12279,7 @@ async function mcpRequest(conn, method, params = {}) {
11978
12279
  if (response.error) {
11979
12280
  reject(new Error(`MCP error ${response.error.code}: ${response.error.message}`));
11980
12281
  } else {
11981
- resolve22(response.result || {});
12282
+ resolve23(response.result || {});
11982
12283
  }
11983
12284
  return;
11984
12285
  }
@@ -12000,7 +12301,7 @@ async function connectToServer(name, config) {
12000
12301
  return existing;
12001
12302
  }
12002
12303
  const root = getProjectRoot();
12003
- const cwd = config.cwd ? resolve18(root, config.cwd) : root;
12304
+ const cwd = config.cwd ? resolve19(root, config.cwd) : root;
12004
12305
  const args2 = config.args || [];
12005
12306
  const proc = spawn(config.command, args2, {
12006
12307
  cwd,
@@ -12262,7 +12563,7 @@ var init_mcp_bridge_tools = __esm({
12262
12563
 
12263
12564
  // src/tools.ts
12264
12565
  import { readFileSync as readFileSync23, existsSync as existsSync23 } from "fs";
12265
- import { resolve as resolve19, basename as basename7 } from "path";
12566
+ import { resolve as resolve20, basename as basename8 } from "path";
12266
12567
  function prefix() {
12267
12568
  return getConfig().toolPrefix;
12268
12569
  }
@@ -12297,7 +12598,7 @@ function ensureIndexes(dataDb2, codegraphDb2, force = false) {
12297
12598
  if (config.python?.root) {
12298
12599
  const pythonRoot = config.python.root;
12299
12600
  const excludeDirs = config.python.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
12300
- if (force || isPythonDataStale(dataDb2, resolve19(getProjectRoot(), pythonRoot))) {
12601
+ if (force || isPythonDataStale(dataDb2, resolve20(getProjectRoot(), pythonRoot))) {
12301
12602
  const pyImports = buildPythonImportIndex(dataDb2, pythonRoot, excludeDirs);
12302
12603
  results.push(`Python imports: ${pyImports}`);
12303
12604
  const pyRoutes = buildPythonRouteIndex(dataDb2, pythonRoot, excludeDirs);
@@ -12701,7 +13002,7 @@ function handleContext(file, dataDb2, codegraphDb2) {
12701
13002
  try {
12702
13003
  const resolvedPaths = getResolvedPaths();
12703
13004
  const root = getProjectRoot();
12704
- const absFilePath = ensureWithinRoot(resolve19(resolvedPaths.srcDir, "..", file), root);
13005
+ const absFilePath = ensureWithinRoot(resolve20(resolvedPaths.srcDir, "..", file), root);
12705
13006
  if (existsSync23(absFilePath)) {
12706
13007
  const fileContent = readFileSync23(absFilePath, "utf-8").slice(0, 3e3);
12707
13008
  const keywords = [];
@@ -12797,7 +13098,7 @@ function handleContext(file, dataDb2, codegraphDb2) {
12797
13098
  WHERE o.files_involved LIKE ?
12798
13099
  ORDER BY o.importance DESC, o.created_at_epoch DESC
12799
13100
  LIMIT 5
12800
- `).all(`%${basename7(file)}%`);
13101
+ `).all(`%${basename8(file)}%`);
12801
13102
  if (fileObservations.length > 0) {
12802
13103
  lines.push("## Past Observations (This File)");
12803
13104
  for (const obs of fileObservations) {
@@ -12813,7 +13114,7 @@ function handleContext(file, dataDb2, codegraphDb2) {
12813
13114
  WHERE type = 'failed_attempt' AND files_involved LIKE ?
12814
13115
  ORDER BY recurrence_count DESC
12815
13116
  LIMIT 3
12816
- `).all(`%${basename7(file)}%`);
13117
+ `).all(`%${basename8(file)}%`);
12817
13118
  if (failures.length > 0) {
12818
13119
  lines.push("## Failed Attempts (DO NOT RETRY)");
12819
13120
  for (const f of failures) {
@@ -13127,7 +13428,7 @@ function handleSchema(args2) {
13127
13428
  lines.push("Checking all column references against Prisma schema...");
13128
13429
  lines.push("");
13129
13430
  const projectRoot = getProjectRoot();
13130
- const absPath = ensureWithinRoot(resolve19(projectRoot, file), projectRoot);
13431
+ const absPath = ensureWithinRoot(resolve20(projectRoot, file), projectRoot);
13131
13432
  if (!existsSync23(absPath)) {
13132
13433
  return text18(`File not found: ${file}`);
13133
13434
  }
@@ -13211,7 +13512,7 @@ var init_tools = __esm({
13211
13512
  // src/server.ts
13212
13513
  var server_exports = {};
13213
13514
  import { readFileSync as readFileSync24 } from "fs";
13214
- import { resolve as resolve20, dirname as dirname11 } from "path";
13515
+ import { resolve as resolve21, dirname as dirname11 } from "path";
13215
13516
  import { fileURLToPath as fileURLToPath4 } from "url";
13216
13517
  function getDb() {
13217
13518
  if (!codegraphDb) codegraphDb = getCodeGraphDb();
@@ -13306,7 +13607,7 @@ var init_server = __esm({
13306
13607
  __dirname4 = dirname11(fileURLToPath4(import.meta.url));
13307
13608
  PKG_VERSION = (() => {
13308
13609
  try {
13309
- const pkg = JSON.parse(readFileSync24(resolve20(__dirname4, "..", "package.json"), "utf-8"));
13610
+ const pkg = JSON.parse(readFileSync24(resolve21(__dirname4, "..", "package.json"), "utf-8"));
13310
13611
  return pkg.version ?? "0.0.0";
13311
13612
  } catch {
13312
13613
  return "0.0.0";
@@ -13371,7 +13672,7 @@ var init_server = __esm({
13371
13672
 
13372
13673
  // src/cli.ts
13373
13674
  import { readFileSync as readFileSync25 } from "fs";
13374
- import { resolve as resolve21, dirname as dirname12 } from "path";
13675
+ import { resolve as resolve22, dirname as dirname12 } from "path";
13375
13676
  import { fileURLToPath as fileURLToPath5 } from "url";
13376
13677
  var __filename4 = fileURLToPath5(import.meta.url);
13377
13678
  var __dirname5 = dirname12(__filename4);
@@ -13445,7 +13746,7 @@ Documentation: https://massu.ai/docs
13445
13746
  }
13446
13747
  function printVersion() {
13447
13748
  try {
13448
- const pkg = JSON.parse(readFileSync25(resolve21(__dirname5, "../package.json"), "utf-8"));
13749
+ const pkg = JSON.parse(readFileSync25(resolve22(__dirname5, "../package.json"), "utf-8"));
13449
13750
  console.log(`massu v${pkg.version}`);
13450
13751
  } catch {
13451
13752
  console.log("massu v0.1.0");