@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 +479 -182
- package/dist/hooks/post-tool-use.js +9 -4
- package/package.json +1 -1
- package/src/claude-md-templates.ts +342 -0
- package/src/commands/doctor.ts +28 -0
- package/src/commands/init.ts +41 -15
- package/src/knowledge-indexer.ts +1 -1
- package/src/mcp-bridge-tools.ts +0 -1
- package/src/memory-file-ingest.ts +13 -6
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
|
|
1491
|
+
const basename9 = (filePath.split("/").pop() ?? "").replace(".md", "");
|
|
1493
1492
|
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
1494
|
-
let name =
|
|
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 =
|
|
1501
|
-
|
|
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
|
|
1571
|
-
import { resolve as
|
|
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 =
|
|
1839
|
+
const nodeModulesPath = resolve4(cwd, "node_modules/@massu/core", assetName);
|
|
1576
1840
|
if (existsSync4(nodeModulesPath)) {
|
|
1577
1841
|
return nodeModulesPath;
|
|
1578
1842
|
}
|
|
1579
|
-
const distRelPath =
|
|
1843
|
+
const distRelPath = resolve4(__dirname, "..", assetName);
|
|
1580
1844
|
if (existsSync4(distRelPath)) {
|
|
1581
1845
|
return distRelPath;
|
|
1582
1846
|
}
|
|
1583
|
-
const srcRelPath =
|
|
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 =
|
|
1861
|
+
const entries = readdirSync3(sourceDir);
|
|
1598
1862
|
for (const entry of entries) {
|
|
1599
|
-
const sourcePath =
|
|
1600
|
-
const targetPath =
|
|
1601
|
-
const entryStat =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
1720
|
-
import { resolve as
|
|
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 =
|
|
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(
|
|
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 =
|
|
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(
|
|
2054
|
+
if (existsSync5(resolve5(projectRoot, "alembic.ini"))) {
|
|
1790
2055
|
result.hasAlembic = true;
|
|
1791
|
-
if (existsSync5(
|
|
2056
|
+
if (existsSync5(resolve5(projectRoot, "alembic"))) {
|
|
1792
2057
|
result.alembicDir = "alembic";
|
|
1793
2058
|
}
|
|
1794
|
-
} else if (existsSync5(
|
|
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 =
|
|
1801
|
-
if (existsSync5(candidatePath) && existsSync5(
|
|
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 =
|
|
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 =
|
|
2118
|
+
const configPath = resolve5(projectRoot, "massu.config.yaml");
|
|
1854
2119
|
if (existsSync5(configPath)) {
|
|
1855
2120
|
return false;
|
|
1856
2121
|
}
|
|
1857
|
-
const projectName =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
2016
|
-
const settingsPath =
|
|
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 =
|
|
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 =
|
|
2315
|
+
const memoryMdPath = resolve5(memoryDir, "MEMORY.md");
|
|
2049
2316
|
let memoryMdCreated = false;
|
|
2050
2317
|
if (!existsSync5(memoryMdPath)) {
|
|
2051
|
-
const projectName =
|
|
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(
|
|
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(
|
|
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 =
|
|
2127
|
-
const memFiles = existsSync5(computedMemoryDir) ?
|
|
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
|
|
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] ??
|
|
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
|
|
2476
|
-
import { resolve as
|
|
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
|
|
2752
|
+
import { parse as parseYaml2 } from "yaml";
|
|
2479
2753
|
function checkConfig(projectRoot) {
|
|
2480
|
-
const configPath =
|
|
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 =
|
|
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 =
|
|
2815
|
+
const nodeModulesHooksDir = resolve6(projectRoot, "node_modules/@massu/core/dist/hooks");
|
|
2542
2816
|
let hooksDir = nodeModulesHooksDir;
|
|
2543
2817
|
if (!existsSync6(nodeModulesHooksDir)) {
|
|
2544
|
-
const devHooksDir =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
2700
|
-
if (depth <= 2 && !existsSync6(
|
|
2973
|
+
const subdir = resolve6(dir, entry.name);
|
|
2974
|
+
if (depth <= 2 && !existsSync6(resolve6(subdir, "__init__.py"))) {
|
|
2701
2975
|
try {
|
|
2702
|
-
const subEntries =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 (
|
|
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
|
|
3495
|
+
import { resolve as resolve7, normalize } from "path";
|
|
3198
3496
|
function ensureWithinRoot(filePath, projectRoot) {
|
|
3199
|
-
const resolvedRoot =
|
|
3200
|
-
const resolvedPath =
|
|
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
|
|
3274
|
-
import { resolve as
|
|
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 =
|
|
3628
|
+
basePath = resolve8(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
|
|
3331
3629
|
} else {
|
|
3332
|
-
basePath =
|
|
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
|
|
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(
|
|
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
|
|
3423
|
-
import { resolve as
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
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 =
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
4187
|
-
import { resolve as
|
|
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(
|
|
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(
|
|
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
|
|
4506
|
+
return relative3(projectRoot, basePath + ".py");
|
|
4209
4507
|
}
|
|
4210
4508
|
if (existsSync12(join7(basePath, "__init__.py"))) {
|
|
4211
|
-
return
|
|
4509
|
+
return relative3(projectRoot, join7(basePath, "__init__.py"));
|
|
4212
4510
|
}
|
|
4213
4511
|
if (basePath.endsWith(".py") && existsSync12(basePath)) {
|
|
4214
|
-
return
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
4520
|
-
import { join as join8, relative as
|
|
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 =
|
|
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 =
|
|
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
|
|
4764
|
-
import { join as join9, relative as
|
|
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 =
|
|
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 =
|
|
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
|
|
5060
|
-
import { join as join10, relative as
|
|
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 =
|
|
5366
|
+
files = readdirSync12(versionsDir).filter((f) => f.endsWith(".py")).map((f) => join10(versionsDir, f));
|
|
5069
5367
|
} catch {
|
|
5070
5368
|
try {
|
|
5071
|
-
files =
|
|
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:
|
|
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
|
|
5120
|
-
import { join as join11, relative as
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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(
|
|
5988
|
+
const absPath = ensureWithinRoot(resolve13(getResolvedPaths().srcDir, "..", routerPath), root);
|
|
5691
5989
|
if (!existsSync13(absPath)) {
|
|
5692
|
-
const altPath = ensureWithinRoot(
|
|
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(
|
|
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 =
|
|
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) =>
|
|
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(
|
|
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(
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
6720
|
+
const absPath = resolve14(PROJECT_ROOT, routeToPath);
|
|
6423
6721
|
if (page.page_route.startsWith("/") && !existsSync14(absPath)) {
|
|
6424
|
-
const altPath =
|
|
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
|
|
6950
|
-
import { resolve as
|
|
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 =
|
|
7377
|
+
const basePath = resolve15(projectRoot, componentsBase);
|
|
7080
7378
|
if (existsSync15(basePath)) {
|
|
7081
7379
|
try {
|
|
7082
|
-
const entries =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
9579
|
-
import { resolve as
|
|
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 =
|
|
9903
|
+
const entries = readdirSync15(dir, { withFileTypes: true });
|
|
9606
9904
|
for (const entry of entries) {
|
|
9607
|
-
const fullPath =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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) ?
|
|
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 =
|
|
10247
|
+
const fileName = basename6(filePath);
|
|
9950
10248
|
const fileNameLower = fileName.toLowerCase();
|
|
9951
10249
|
const relPathLower = relPath.toLowerCase();
|
|
9952
|
-
const claudeMdName =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
10086
|
-
import { resolve as
|
|
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 =
|
|
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(`%${
|
|
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 =
|
|
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
|
|
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 =
|
|
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((
|
|
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
|
-
|
|
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 ?
|
|
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
|
|
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,
|
|
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(
|
|
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(`%${
|
|
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(`%${
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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");
|