codebyplan 1.13.24 → 1.13.26
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 +1890 -1481
- package/package.json +2 -2
- package/templates/agents/cbp-cc-executor.md +7 -7
- package/templates/agents/cbp-improve-round.md +2 -2
- package/templates/agents/cbp-round-executor.md +20 -4
- package/templates/agents/cbp-testing-qa-agent.md +3 -3
- package/templates/hooks/validate-structure-patterns.sh +1 -1
- package/templates/settings.project.base.json +2 -0
- package/templates/skills/cbp-checkpoint-check/SKILL.md +2 -2
- package/templates/skills/cbp-checkpoint-complete/SKILL.md +2 -2
- package/templates/skills/cbp-merge-main/SKILL.md +1 -1
- package/templates/skills/cbp-round-end/SKILL.md +12 -35
- package/templates/skills/cbp-round-end/reference/findings-presentation.md +76 -3
- package/templates/skills/cbp-round-execute/SKILL.md +13 -60
- package/templates/skills/cbp-round-start/SKILL.md +3 -1
- package/templates/skills/cbp-round-update/SKILL.md +1 -1
- package/templates/skills/cbp-session-start/SKILL.md +14 -2
- package/templates/skills/cbp-ship-configure/SKILL.md +1 -1
- package/templates/skills/cbp-ship-configure/reference/supabase.md +2 -2
- package/templates/skills/cbp-ship-main/SKILL.md +2 -0
- package/templates/skills/cbp-standalone-task-create/SKILL.md +1 -1
- package/templates/skills/cbp-task-check/SKILL.md +1 -1
- package/templates/skills/cbp-task-complete/SKILL.md +1 -1
- package/templates/skills/cbp-task-create/SKILL.md +50 -1
- package/templates/skills/cbp-task-start/SKILL.md +2 -2
- package/templates/skills/cbp-task-testing/SKILL.md +2 -2
- package/templates/skills/cbp-todo/SKILL.md +36 -3
- package/templates/skills/cbp-todo/qa-regression.md +8 -1
package/dist/cli.js
CHANGED
|
@@ -14,7 +14,7 @@ var VERSION, PACKAGE_NAME;
|
|
|
14
14
|
var init_version = __esm({
|
|
15
15
|
"src/lib/version.ts"() {
|
|
16
16
|
"use strict";
|
|
17
|
-
VERSION = "1.13.
|
|
17
|
+
VERSION = "1.13.26";
|
|
18
18
|
PACKAGE_NAME = "codebyplan";
|
|
19
19
|
}
|
|
20
20
|
});
|
|
@@ -149,6 +149,7 @@ var init_gitignore_block = __esm({
|
|
|
149
149
|
".codebyplan/device.local.json",
|
|
150
150
|
".codebyplan/statusline.local.json",
|
|
151
151
|
".codebyplan/worktree.local.json",
|
|
152
|
+
".codebyplan/todo/",
|
|
152
153
|
".codebyplan/claude-status.local.json",
|
|
153
154
|
".codebyplan.local.json"
|
|
154
155
|
];
|
|
@@ -1568,6 +1569,18 @@ function stripBaseSettingsFromSettings(settings, base) {
|
|
|
1568
1569
|
}
|
|
1569
1570
|
return settings;
|
|
1570
1571
|
}
|
|
1572
|
+
function mergeEnabledPluginsIntoSettings(settings, plugins) {
|
|
1573
|
+
if (!settings.enabledPlugins) {
|
|
1574
|
+
settings.enabledPlugins = {};
|
|
1575
|
+
}
|
|
1576
|
+
for (const plugin of plugins) {
|
|
1577
|
+
const key = `${plugin}@claude-plugins-official`;
|
|
1578
|
+
if (!(key in settings.enabledPlugins)) {
|
|
1579
|
+
settings.enabledPlugins[key] = true;
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
return settings;
|
|
1583
|
+
}
|
|
1571
1584
|
function stripOwnedHooksFromSettings(settings) {
|
|
1572
1585
|
if (!settings.hooks) {
|
|
1573
1586
|
return settings;
|
|
@@ -1636,1518 +1649,1903 @@ var init_settings_merge = __esm({
|
|
|
1636
1649
|
}
|
|
1637
1650
|
});
|
|
1638
1651
|
|
|
1639
|
-
// src/
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
const candidates = [
|
|
1652
|
-
path4.resolve(here, "..", "templates"),
|
|
1653
|
-
path4.resolve(here, "..", "..", "templates"),
|
|
1654
|
-
path4.resolve(here, "..", "..", "..", "templates")
|
|
1655
|
-
];
|
|
1656
|
-
for (const c of candidates) {
|
|
1657
|
-
if (fs3.existsSync(c) && fs3.statSync(c).isDirectory()) {
|
|
1658
|
-
return c;
|
|
1652
|
+
// src/lib/flags.ts
|
|
1653
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
1654
|
+
import { join as join8, resolve } from "node:path";
|
|
1655
|
+
async function findCodebyplanConfig(startDir, maxDepth = 20) {
|
|
1656
|
+
let cursor = resolve(startDir);
|
|
1657
|
+
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1658
|
+
const sentinelPath2 = join8(cursor, ".codebyplan", "repo.json");
|
|
1659
|
+
try {
|
|
1660
|
+
const raw = await readFile6(sentinelPath2, "utf-8");
|
|
1661
|
+
const parsed = JSON.parse(raw);
|
|
1662
|
+
return { path: sentinelPath2, contents: parsed };
|
|
1663
|
+
} catch {
|
|
1659
1664
|
}
|
|
1665
|
+
const legacyPath = join8(cursor, ".codebyplan.json");
|
|
1666
|
+
try {
|
|
1667
|
+
const raw = await readFile6(legacyPath, "utf-8");
|
|
1668
|
+
const parsed = JSON.parse(raw);
|
|
1669
|
+
return { path: legacyPath, contents: parsed };
|
|
1670
|
+
} catch {
|
|
1671
|
+
}
|
|
1672
|
+
const parent = resolve(cursor, "..");
|
|
1673
|
+
if (parent === cursor) return null;
|
|
1674
|
+
cursor = parent;
|
|
1660
1675
|
}
|
|
1661
|
-
|
|
1662
|
-
`codebyplan: could not locate templates/ directory. Probed:
|
|
1663
|
-
${candidates.join(
|
|
1664
|
-
"\n "
|
|
1665
|
-
)}`
|
|
1666
|
-
);
|
|
1676
|
+
return null;
|
|
1667
1677
|
}
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
const
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1678
|
+
function parseFlags(startIndex) {
|
|
1679
|
+
const flags = {};
|
|
1680
|
+
const args = process.argv.slice(startIndex);
|
|
1681
|
+
for (let i = 0; i < args.length; i++) {
|
|
1682
|
+
const arg = args[i];
|
|
1683
|
+
if (arg.startsWith("--") && i + 1 < args.length) {
|
|
1684
|
+
const key = arg.slice(2);
|
|
1685
|
+
flags[key] = args[++i];
|
|
1676
1686
|
}
|
|
1677
|
-
runInstallUser(opts, deps);
|
|
1678
|
-
return;
|
|
1679
1687
|
}
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1688
|
+
return flags;
|
|
1689
|
+
}
|
|
1690
|
+
function hasFlag(name, startIndex) {
|
|
1691
|
+
return process.argv.slice(startIndex).includes(`--${name}`);
|
|
1692
|
+
}
|
|
1693
|
+
async function resolveConfig(flags) {
|
|
1694
|
+
const projectPath = flags["path"] ?? process.cwd();
|
|
1695
|
+
let repoId = flags["repo-id"] ?? process.env.CODEBYPLAN_REPO_ID;
|
|
1696
|
+
let worktreeId = flags["worktree-id"] ?? process.env.CODEBYPLAN_WORKTREE_ID;
|
|
1697
|
+
if (!repoId) {
|
|
1698
|
+
const found = await findCodebyplanConfig(projectPath);
|
|
1699
|
+
if (found) {
|
|
1700
|
+
repoId = found.contents.repo_id;
|
|
1701
|
+
if (!worktreeId) worktreeId = found.contents.worktree_id;
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
if (!repoId) {
|
|
1705
|
+
throw new Error(
|
|
1706
|
+
`Could not determine repo_id.
|
|
1707
|
+
|
|
1708
|
+
Provide it via one of:
|
|
1709
|
+
--repo-id <uuid> CLI flag
|
|
1710
|
+
CODEBYPLAN_REPO_ID=<uuid> environment variable
|
|
1711
|
+
.codebyplan/repo.json { "repo_id": "<uuid>" } in project root
|
|
1712
|
+
Run 'codebyplan setup' to initialize this project`
|
|
1687
1713
|
);
|
|
1688
|
-
process.exitCode = 1;
|
|
1689
|
-
return;
|
|
1690
1714
|
}
|
|
1715
|
+
return { repoId, worktreeId, projectPath };
|
|
1716
|
+
}
|
|
1717
|
+
var init_flags = __esm({
|
|
1718
|
+
"src/lib/flags.ts"() {
|
|
1719
|
+
"use strict";
|
|
1720
|
+
}
|
|
1721
|
+
});
|
|
1722
|
+
|
|
1723
|
+
// src/lib/tech-detect.ts
|
|
1724
|
+
import { readFile as readFile7, access, readdir } from "node:fs/promises";
|
|
1725
|
+
import { join as join9, relative as relative2 } from "node:path";
|
|
1726
|
+
async function fileExists(filePath) {
|
|
1691
1727
|
try {
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
console.log(`copied ${f.src} \u2192 .claude/${f.dest}`);
|
|
1706
|
-
}
|
|
1707
|
-
}
|
|
1708
|
-
manifestEntries.push({ src: f.src, dest: f.dest, hash: f.hash });
|
|
1709
|
-
}
|
|
1710
|
-
const hooksJsonPath = path4.join(templatesDir, "hooks", "hooks.json");
|
|
1711
|
-
const baseSettingsPath = path4.join(
|
|
1712
|
-
templatesDir,
|
|
1713
|
-
"settings.project.base.json"
|
|
1714
|
-
);
|
|
1715
|
-
const hasHooks = fs3.existsSync(hooksJsonPath);
|
|
1716
|
-
const hasBase = fs3.existsSync(baseSettingsPath);
|
|
1717
|
-
const settingsPath = path4.join(projectDir, ".claude", "settings.json");
|
|
1718
|
-
const existingSettings = fs3.existsSync(settingsPath) ? JSON.parse(fs3.readFileSync(settingsPath, "utf8")) : {};
|
|
1719
|
-
if (hasBase) {
|
|
1720
|
-
const base = JSON.parse(
|
|
1721
|
-
fs3.readFileSync(baseSettingsPath, "utf8")
|
|
1722
|
-
);
|
|
1723
|
-
mergeBaseSettingsIntoSettings(existingSettings, base);
|
|
1724
|
-
}
|
|
1725
|
-
if (hasHooks) {
|
|
1726
|
-
const hooksJson = JSON.parse(
|
|
1727
|
-
fs3.readFileSync(hooksJsonPath, "utf8")
|
|
1728
|
-
);
|
|
1729
|
-
mergeHooksIntoSettings(existingSettings, hooksJson);
|
|
1730
|
-
}
|
|
1731
|
-
if (!opts.dryRun) {
|
|
1732
|
-
fs3.mkdirSync(path4.dirname(settingsPath), { recursive: true });
|
|
1733
|
-
fs3.writeFileSync(
|
|
1734
|
-
settingsPath,
|
|
1735
|
-
JSON.stringify(existingSettings, null, 2) + "\n",
|
|
1736
|
-
"utf8"
|
|
1737
|
-
);
|
|
1738
|
-
} else if (opts.verbose) {
|
|
1739
|
-
console.log(
|
|
1740
|
-
`[dry-run] would merge settings into ${path4.relative(projectDir, settingsPath)}`
|
|
1741
|
-
);
|
|
1742
|
-
}
|
|
1743
|
-
const gitignoreAction = await ensureManagedGitignoreBlock(
|
|
1744
|
-
projectDir,
|
|
1745
|
-
opts.dryRun
|
|
1728
|
+
await access(filePath);
|
|
1729
|
+
return true;
|
|
1730
|
+
} catch {
|
|
1731
|
+
return false;
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
async function discoverMonorepoApps(projectPath) {
|
|
1735
|
+
const apps = [];
|
|
1736
|
+
const patterns = [];
|
|
1737
|
+
try {
|
|
1738
|
+
const raw = await readFile7(
|
|
1739
|
+
join9(projectPath, "pnpm-workspace.yaml"),
|
|
1740
|
+
"utf-8"
|
|
1746
1741
|
);
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1742
|
+
const matches = raw.match(/^\s*-\s*['"]?([^'"#\n]+)['"]?/gm);
|
|
1743
|
+
if (matches) {
|
|
1744
|
+
for (const m of matches) {
|
|
1745
|
+
const pattern = m.replace(/^\s*-\s*['"]?/, "").replace(/['"]?\s*$/, "").trim();
|
|
1746
|
+
if (pattern) patterns.push(pattern);
|
|
1747
|
+
}
|
|
1751
1748
|
}
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1749
|
+
} catch {
|
|
1750
|
+
}
|
|
1751
|
+
if (patterns.length === 0) {
|
|
1752
|
+
try {
|
|
1753
|
+
const raw = await readFile7(join9(projectPath, "package.json"), "utf-8");
|
|
1754
|
+
const pkg = JSON.parse(raw);
|
|
1755
|
+
const ws = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces?.packages;
|
|
1756
|
+
if (ws) patterns.push(...ws);
|
|
1757
|
+
} catch {
|
|
1756
1758
|
}
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
)
|
|
1760
|
-
|
|
1761
|
-
|
|
1759
|
+
}
|
|
1760
|
+
for (const pattern of patterns) {
|
|
1761
|
+
if (pattern.endsWith("/*")) {
|
|
1762
|
+
const dir = pattern.slice(0, -2);
|
|
1763
|
+
const absDir = join9(projectPath, dir);
|
|
1764
|
+
try {
|
|
1765
|
+
const entries = await readdir(absDir, { withFileTypes: true });
|
|
1766
|
+
for (const entry of entries) {
|
|
1767
|
+
if (entry.isDirectory()) {
|
|
1768
|
+
const relPath = join9(dir, entry.name);
|
|
1769
|
+
const absPath = join9(absDir, entry.name);
|
|
1770
|
+
if (await fileExists(join9(absPath, "package.json"))) {
|
|
1771
|
+
apps.push({ name: entry.name, path: relPath, absPath });
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
} catch {
|
|
1776
|
+
}
|
|
1762
1777
|
}
|
|
1763
|
-
} catch (err) {
|
|
1764
|
-
console.error(
|
|
1765
|
-
`codebyplan claude install failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1766
|
-
);
|
|
1767
|
-
process.exitCode = 1;
|
|
1768
1778
|
}
|
|
1779
|
+
return apps;
|
|
1769
1780
|
}
|
|
1770
|
-
function
|
|
1771
|
-
|
|
1781
|
+
async function hasJsxFile(dir, depth = 0) {
|
|
1782
|
+
if (depth > 6) return false;
|
|
1772
1783
|
try {
|
|
1773
|
-
|
|
1784
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
1785
|
+
for (const entry of entries) {
|
|
1786
|
+
const name = entry.name;
|
|
1787
|
+
if (entry.isDirectory()) {
|
|
1788
|
+
if (SKIP_DIRS.has(name) || JSX_SKIP_DIRS.has(name)) continue;
|
|
1789
|
+
if (await hasJsxFile(join9(dir, name), depth + 1)) return true;
|
|
1790
|
+
} else if (entry.isFile()) {
|
|
1791
|
+
if (JSX_TEST_PATTERN.test(name)) continue;
|
|
1792
|
+
if (name.endsWith(".tsx") || name.endsWith(".jsx")) return true;
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1774
1795
|
} catch (err) {
|
|
1796
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
1797
|
+
return false;
|
|
1798
|
+
}
|
|
1775
1799
|
console.error(
|
|
1776
|
-
err instanceof Error ? err.message :
|
|
1800
|
+
`detectCapabilities: readdir failed for ${dir}: ${err instanceof Error ? err.message : String(err)}`
|
|
1777
1801
|
);
|
|
1778
|
-
process.exitCode = 1;
|
|
1779
|
-
return;
|
|
1780
1802
|
}
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
console.error(
|
|
1790
|
-
"codebyplan claude install: settings.user.base.json not found in templates."
|
|
1791
|
-
);
|
|
1792
|
-
process.exitCode = 1;
|
|
1793
|
-
return;
|
|
1794
|
-
}
|
|
1795
|
-
const userBase = JSON.parse(
|
|
1796
|
-
fs3.readFileSync(userBaseSettingsPath, "utf8")
|
|
1797
|
-
);
|
|
1798
|
-
const existingSettings = fs3.existsSync(settingsPath) ? JSON.parse(fs3.readFileSync(settingsPath, "utf8")) : {};
|
|
1799
|
-
mergeBaseSettingsIntoSettings(existingSettings, userBase);
|
|
1800
|
-
if (!opts.dryRun) {
|
|
1801
|
-
fs3.mkdirSync(userDir, { recursive: true });
|
|
1802
|
-
fs3.writeFileSync(
|
|
1803
|
-
settingsPath,
|
|
1804
|
-
JSON.stringify(existingSettings, null, 2) + "\n",
|
|
1805
|
-
"utf8"
|
|
1806
|
-
);
|
|
1807
|
-
const manifest = defaultManifest();
|
|
1808
|
-
manifest.files = [];
|
|
1809
|
-
writeManifestForScope("user", manifest, userDir);
|
|
1810
|
-
} else if (opts.verbose) {
|
|
1811
|
-
console.log(
|
|
1812
|
-
`[dry-run] would merge user base settings into ${settingsPath}`
|
|
1813
|
-
);
|
|
1803
|
+
return false;
|
|
1804
|
+
}
|
|
1805
|
+
async function detectCapabilities(dirPath, pkgJson) {
|
|
1806
|
+
const caps = /* @__PURE__ */ new Set();
|
|
1807
|
+
for (const sub of JSX_SCAN_DIRS) {
|
|
1808
|
+
if (await hasJsxFile(join9(dirPath, sub))) {
|
|
1809
|
+
caps.add("jsx");
|
|
1810
|
+
break;
|
|
1814
1811
|
}
|
|
1815
|
-
console.log(
|
|
1816
|
-
`codebyplan claude install --scope user${opts.dryRun ? " (dry-run)" : ""}: settings.json updated, 0 template files copied.`
|
|
1817
|
-
);
|
|
1818
|
-
} catch (err) {
|
|
1819
|
-
console.error(
|
|
1820
|
-
`codebyplan claude install failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1821
|
-
);
|
|
1822
|
-
process.exitCode = 1;
|
|
1823
1812
|
}
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
const
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1813
|
+
if (pkgJson) {
|
|
1814
|
+
const allDeps = {
|
|
1815
|
+
...pkgJson.dependencies ?? {},
|
|
1816
|
+
...pkgJson.devDependencies ?? {}
|
|
1817
|
+
};
|
|
1818
|
+
for (const dep of Object.keys(allDeps)) {
|
|
1819
|
+
if (SERVER_FRAMEWORK_DEPS.has(dep)) {
|
|
1820
|
+
caps.add("node-server");
|
|
1821
|
+
break;
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
if (!caps.has("node-server") && typeof pkgJson.main === "string") {
|
|
1825
|
+
if (SERVER_MAIN_ENTRIES.has(pkgJson.main.trim())) {
|
|
1826
|
+
caps.add("node-server");
|
|
1827
|
+
}
|
|
1833
1828
|
}
|
|
1834
|
-
return n;
|
|
1835
|
-
} catch (err) {
|
|
1836
|
-
console.error(
|
|
1837
|
-
`codebyplan: could not count hook entries in hooks.json: ${err instanceof Error ? err.message : String(err)}`
|
|
1838
|
-
);
|
|
1839
|
-
return 0;
|
|
1840
1829
|
}
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
"src/cli/claude/install.ts"() {
|
|
1844
|
-
"use strict";
|
|
1845
|
-
init_template_walker();
|
|
1846
|
-
init_gitignore_block();
|
|
1847
|
-
init_manifest();
|
|
1848
|
-
init_settings_merge();
|
|
1849
|
-
init_statusline_config();
|
|
1830
|
+
if (!caps.has("node-server") && await fileExists(join9(dirPath, "src", "main.ts"))) {
|
|
1831
|
+
caps.add("node-server");
|
|
1850
1832
|
}
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
}
|
|
1858
|
-
|
|
1859
|
-
import { homedir as homedir4 } from "node:os";
|
|
1860
|
-
import { join as join9 } from "node:path";
|
|
1861
|
-
import { stdin, stdout as stdout2 } from "node:process";
|
|
1862
|
-
import { createInterface } from "node:readline/promises";
|
|
1863
|
-
function getConfigPath(scope) {
|
|
1864
|
-
return scope === "user" ? join9(homedir4(), ".claude.json") : join9(process.cwd(), ".mcp.json");
|
|
1833
|
+
if (pkgJson && pkgJson.bin) {
|
|
1834
|
+
if (typeof pkgJson.bin === "string" && pkgJson.bin.trim().length > 0) {
|
|
1835
|
+
caps.add("cli-bin");
|
|
1836
|
+
} else if (typeof pkgJson.bin === "object" && pkgJson.bin !== null && Object.keys(pkgJson.bin).length > 0) {
|
|
1837
|
+
caps.add("cli-bin");
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
return caps;
|
|
1865
1841
|
}
|
|
1866
|
-
async function
|
|
1842
|
+
async function detectFromDirectory(dirPath) {
|
|
1843
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1844
|
+
let pkgJson = null;
|
|
1867
1845
|
try {
|
|
1868
|
-
const raw = await
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1846
|
+
const raw = await readFile7(join9(dirPath, "package.json"), "utf-8");
|
|
1847
|
+
pkgJson = JSON.parse(raw);
|
|
1848
|
+
const allDeps = {
|
|
1849
|
+
...pkgJson.dependencies ?? {},
|
|
1850
|
+
...pkgJson.devDependencies ?? {}
|
|
1851
|
+
};
|
|
1852
|
+
for (const depName of Object.keys(allDeps)) {
|
|
1853
|
+
const rule = PACKAGE_MAP[depName];
|
|
1854
|
+
if (rule) {
|
|
1855
|
+
const key = rule.name.toLowerCase();
|
|
1856
|
+
if (!seen.has(key)) {
|
|
1857
|
+
seen.set(key, { name: rule.name, category: rule.category });
|
|
1858
|
+
}
|
|
1859
|
+
continue;
|
|
1860
|
+
}
|
|
1861
|
+
for (const { prefix, rule: prefixRule } of PACKAGE_PREFIX_MAP) {
|
|
1862
|
+
if (depName.startsWith(prefix)) {
|
|
1863
|
+
const key = prefixRule.name.toLowerCase();
|
|
1864
|
+
if (!seen.has(key)) {
|
|
1865
|
+
seen.set(key, {
|
|
1866
|
+
name: prefixRule.name,
|
|
1867
|
+
category: prefixRule.category
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1870
|
+
break;
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1872
1873
|
}
|
|
1873
|
-
return {};
|
|
1874
1874
|
} catch {
|
|
1875
|
-
return {};
|
|
1876
1875
|
}
|
|
1877
|
-
}
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
}
|
|
1881
|
-
async function writeMcpConfig(scope) {
|
|
1882
|
-
const configPath = getConfigPath(scope);
|
|
1883
|
-
const config = await readConfig(configPath);
|
|
1884
|
-
if (typeof config.mcpServers !== "object" || config.mcpServers === null || Array.isArray(config.mcpServers)) {
|
|
1885
|
-
config.mcpServers = {};
|
|
1886
|
-
}
|
|
1887
|
-
config.mcpServers.codebyplan = buildMcpEntry();
|
|
1888
|
-
await writeFile6(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
1889
|
-
return configPath;
|
|
1890
|
-
}
|
|
1891
|
-
async function fetchRepos(auth) {
|
|
1892
|
-
const baseUrl3 = (process.env.CODEBYPLAN_API_URL ?? "https://www.codebyplan.com").replace(/\/$/, "");
|
|
1893
|
-
const headers = auth.kind === "oauth" ? { Authorization: `Bearer ${await getAccessToken()}` } : { "x-api-key": auth.apiKey };
|
|
1894
|
-
const res = await fetch(`${baseUrl3}/api/repos`, {
|
|
1895
|
-
headers,
|
|
1896
|
-
signal: AbortSignal.timeout(1e4)
|
|
1897
|
-
});
|
|
1898
|
-
if (res.status === 401) {
|
|
1899
|
-
if (auth.kind === "oauth") {
|
|
1900
|
-
throw new Error(
|
|
1901
|
-
"Session rejected. Run `codebyplan logout && codebyplan login`."
|
|
1902
|
-
);
|
|
1876
|
+
for (const { file, rule } of CONFIG_FILE_MAP) {
|
|
1877
|
+
const key = rule.name.toLowerCase();
|
|
1878
|
+
if (!seen.has(key) && await fileExists(join9(dirPath, file))) {
|
|
1879
|
+
seen.set(key, { name: rule.name, category: rule.category });
|
|
1903
1880
|
}
|
|
1904
|
-
throw new Error("Invalid API key. Please check and try again.");
|
|
1905
1881
|
}
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1882
|
+
const capabilities = await detectCapabilities(dirPath, pkgJson);
|
|
1883
|
+
const capsArray = Array.from(capabilities).sort();
|
|
1884
|
+
const entries = Array.from(seen.values()).map((entry) => {
|
|
1885
|
+
const isBearer = CAPABILITY_BEARER_NAMES.has(entry.name.toLowerCase());
|
|
1886
|
+
return {
|
|
1887
|
+
...entry,
|
|
1888
|
+
capabilities: isBearer ? capsArray : []
|
|
1889
|
+
};
|
|
1890
|
+
});
|
|
1891
|
+
if (capsArray.length > 0) {
|
|
1892
|
+
const hasBearerWithCaps = entries.some(
|
|
1893
|
+
(e) => CAPABILITY_BEARER_NAMES.has(e.name.toLowerCase()) && (e.capabilities?.some((c) => c.length > 0) ?? false)
|
|
1910
1894
|
);
|
|
1911
|
-
|
|
1895
|
+
if (!hasBearerWithCaps) {
|
|
1896
|
+
entries.push({
|
|
1897
|
+
name: SYNTHETIC_CARRIER_NAME,
|
|
1898
|
+
category: "tool",
|
|
1899
|
+
capabilities: capsArray
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1912
1902
|
}
|
|
1913
|
-
|
|
1914
|
-
return body.data ?? [];
|
|
1903
|
+
return entries.sort(compareByCategoryThenName);
|
|
1915
1904
|
}
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
try {
|
|
1925
|
-
await runLogin();
|
|
1926
|
-
return { kind: "oauth" };
|
|
1927
|
-
} catch (err) {
|
|
1928
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1929
|
-
console.log(`
|
|
1930
|
-
OAuth login failed: ${msg}
|
|
1931
|
-
`);
|
|
1932
|
-
return null;
|
|
1905
|
+
function collectCapabilities(entries) {
|
|
1906
|
+
const set = /* @__PURE__ */ new Set();
|
|
1907
|
+
for (const entry of entries) {
|
|
1908
|
+
if (!entry.capabilities) continue;
|
|
1909
|
+
for (const cap of entry.capabilities) {
|
|
1910
|
+
if (typeof cap === "string" && cap.length > 0) {
|
|
1911
|
+
set.add(cap.toLowerCase());
|
|
1912
|
+
}
|
|
1933
1913
|
}
|
|
1934
1914
|
}
|
|
1935
|
-
|
|
1936
|
-
if (!apiKey) {
|
|
1937
|
-
console.log("\n No API key provided. Aborting setup.\n");
|
|
1938
|
-
return null;
|
|
1939
|
-
}
|
|
1940
|
-
return { kind: "legacy", apiKey };
|
|
1915
|
+
return Array.from(set).sort();
|
|
1941
1916
|
}
|
|
1942
|
-
async function
|
|
1943
|
-
const
|
|
1944
|
-
|
|
1945
|
-
const
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1917
|
+
async function detectTechStack(projectPath) {
|
|
1918
|
+
const repo = await detectFromDirectory(projectPath);
|
|
1919
|
+
const discoveredApps = await discoverMonorepoApps(projectPath);
|
|
1920
|
+
const apps = [];
|
|
1921
|
+
for (const app of discoveredApps) {
|
|
1922
|
+
const stack = await detectFromDirectory(app.absPath);
|
|
1923
|
+
if (stack.length > 0) {
|
|
1924
|
+
apps.push({ name: app.name, path: app.path, stack });
|
|
1925
|
+
}
|
|
1951
1926
|
}
|
|
1952
|
-
|
|
1953
|
-
|
|
1927
|
+
const flatMap = /* @__PURE__ */ new Map();
|
|
1928
|
+
for (const entry of repo) {
|
|
1929
|
+
flatMap.set(entry.name.toLowerCase(), entry);
|
|
1954
1930
|
}
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1931
|
+
for (const app of apps) {
|
|
1932
|
+
for (const entry of app.stack) {
|
|
1933
|
+
const key = entry.name.toLowerCase();
|
|
1934
|
+
const existing = flatMap.get(key);
|
|
1935
|
+
if (!existing) {
|
|
1936
|
+
flatMap.set(key, entry);
|
|
1937
|
+
} else if (entry.capabilities?.length) {
|
|
1938
|
+
const merged = Array.from(
|
|
1939
|
+
/* @__PURE__ */ new Set([...existing.capabilities ?? [], ...entry.capabilities])
|
|
1940
|
+
).sort();
|
|
1941
|
+
flatMap.set(key, { ...existing, capabilities: merged });
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
const repoCleaned = stripSyntheticIfCovered(repo).sort(
|
|
1946
|
+
compareByCategoryThenName
|
|
1959
1947
|
);
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
auto_push_enabled: false,
|
|
1967
|
-
port_allocations: []
|
|
1968
|
-
},
|
|
1969
|
-
null,
|
|
1970
|
-
2
|
|
1971
|
-
) + "\n",
|
|
1972
|
-
"utf-8"
|
|
1973
|
-
);
|
|
1974
|
-
await writeFile6(
|
|
1975
|
-
join9(codebyplanDir, "git.json"),
|
|
1976
|
-
JSON.stringify({ git_branch: null, branch_config: null }, null, 2) + "\n",
|
|
1977
|
-
"utf-8"
|
|
1978
|
-
);
|
|
1979
|
-
await writeFile6(
|
|
1980
|
-
join9(codebyplanDir, "shipment.json"),
|
|
1981
|
-
JSON.stringify({ shipment: null }, null, 2) + "\n",
|
|
1982
|
-
"utf-8"
|
|
1983
|
-
);
|
|
1984
|
-
await writeFile6(
|
|
1985
|
-
join9(codebyplanDir, "vendor.json"),
|
|
1986
|
-
JSON.stringify({}, null, 2) + "\n",
|
|
1987
|
-
"utf-8"
|
|
1988
|
-
);
|
|
1989
|
-
await writeFile6(
|
|
1990
|
-
join9(codebyplanDir, "e2e.json"),
|
|
1991
|
-
JSON.stringify({}, null, 2) + "\n",
|
|
1992
|
-
"utf-8"
|
|
1993
|
-
);
|
|
1994
|
-
await writeFile6(
|
|
1995
|
-
join9(codebyplanDir, "eslint.json"),
|
|
1996
|
-
JSON.stringify({}, null, 2) + "\n",
|
|
1997
|
-
"utf-8"
|
|
1948
|
+
const appsCleaned = apps.map((a) => ({
|
|
1949
|
+
...a,
|
|
1950
|
+
stack: stripSyntheticIfCovered(a.stack).sort(compareByCategoryThenName)
|
|
1951
|
+
}));
|
|
1952
|
+
const flat = stripSyntheticIfCovered(Array.from(flatMap.values())).sort(
|
|
1953
|
+
compareByCategoryThenName
|
|
1998
1954
|
);
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
1955
|
+
return { repo: repoCleaned, apps: appsCleaned, flat };
|
|
1956
|
+
}
|
|
1957
|
+
function stripSyntheticIfCovered(entries) {
|
|
1958
|
+
const synth = entries.find((e) => e.name === SYNTHETIC_CARRIER_NAME);
|
|
1959
|
+
if (!synth?.capabilities?.length) return entries;
|
|
1960
|
+
const realBearerCaps = /* @__PURE__ */ new Set();
|
|
1961
|
+
for (const e of entries) {
|
|
1962
|
+
if (e.name === SYNTHETIC_CARRIER_NAME) continue;
|
|
1963
|
+
for (const c of e.capabilities ?? []) realBearerCaps.add(c);
|
|
2005
1964
|
}
|
|
2006
|
-
if (
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
1965
|
+
if (synth.capabilities.every((c) => realBearerCaps.has(c))) {
|
|
1966
|
+
return entries.filter((e) => e.name !== SYNTHETIC_CARRIER_NAME);
|
|
1967
|
+
}
|
|
1968
|
+
return entries;
|
|
1969
|
+
}
|
|
1970
|
+
function mergeTechStack(remote, detected) {
|
|
1971
|
+
const stripCarrier = (e) => e.name !== SYNTHETIC_CARRIER_NAME;
|
|
1972
|
+
const cleanDetected = {
|
|
1973
|
+
repo: detected.repo.filter(stripCarrier),
|
|
1974
|
+
apps: detected.apps.map((a) => ({
|
|
1975
|
+
...a,
|
|
1976
|
+
stack: a.stack.filter(stripCarrier)
|
|
1977
|
+
})),
|
|
1978
|
+
flat: detected.flat.filter(stripCarrier)
|
|
1979
|
+
};
|
|
1980
|
+
const remoteResult = Array.isArray(remote) ? {
|
|
1981
|
+
repo: remote.filter(stripCarrier),
|
|
1982
|
+
apps: [],
|
|
1983
|
+
flat: remote.filter(stripCarrier)
|
|
1984
|
+
} : {
|
|
1985
|
+
repo: remote.repo.filter(stripCarrier),
|
|
1986
|
+
apps: remote.apps.map((a) => ({
|
|
1987
|
+
...a,
|
|
1988
|
+
stack: a.stack.filter(stripCarrier)
|
|
1989
|
+
})),
|
|
1990
|
+
flat: remote.flat.filter(stripCarrier)
|
|
1991
|
+
};
|
|
1992
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1993
|
+
for (const entry of remoteResult.flat) {
|
|
1994
|
+
seen.set(entry.name.toLowerCase(), entry);
|
|
1995
|
+
}
|
|
1996
|
+
const added = [];
|
|
1997
|
+
for (const entry of cleanDetected.flat) {
|
|
1998
|
+
const key = entry.name.toLowerCase();
|
|
1999
|
+
if (!seen.has(key)) {
|
|
2000
|
+
seen.set(key, entry);
|
|
2001
|
+
added.push(entry);
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
const flat = Array.from(seen.values()).sort(compareByCategoryThenName);
|
|
2005
|
+
const merged = {
|
|
2006
|
+
repo: cleanDetected.repo,
|
|
2007
|
+
apps: cleanDetected.apps,
|
|
2008
|
+
flat
|
|
2009
|
+
};
|
|
2010
|
+
return { merged, added };
|
|
2011
|
+
}
|
|
2012
|
+
function parseTechStack(raw) {
|
|
2013
|
+
if (Array.isArray(raw)) {
|
|
2014
|
+
return raw.filter(
|
|
2015
|
+
(item) => typeof item === "object" && item !== null && typeof item.name === "string" && typeof item.category === "string"
|
|
2011
2016
|
);
|
|
2012
2017
|
}
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
console.log(
|
|
2016
|
-
` repo.json, server.json, git.json, shipment.json, vendor.json, e2e.json, eslint.json, statusline.json`
|
|
2017
|
-
);
|
|
2018
|
-
console.log(` device.local.json (gitignored)`);
|
|
2019
|
-
const gitignoreAction = await ensureManagedGitignoreBlock(projectPath);
|
|
2020
|
-
if (gitignoreAction !== "unchanged") {
|
|
2021
|
-
console.log(" Updated .gitignore (codebyplan managed block)");
|
|
2018
|
+
if (typeof raw === "object" && raw !== null && "flat" in raw) {
|
|
2019
|
+
return parseTechStack(raw.flat);
|
|
2022
2020
|
}
|
|
2021
|
+
return [];
|
|
2023
2022
|
}
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2023
|
+
function parseAppTechStacks(raw) {
|
|
2024
|
+
if (!Array.isArray(raw)) return [];
|
|
2025
|
+
return raw.filter(
|
|
2026
|
+
(item) => typeof item === "object" && item !== null && typeof item.name === "string" && typeof item.path === "string" && Array.isArray(item.stack)
|
|
2027
|
+
).map((item) => ({
|
|
2028
|
+
name: item.name,
|
|
2029
|
+
path: item.path,
|
|
2030
|
+
stack: parseTechStack(item.stack)
|
|
2031
|
+
}));
|
|
2032
|
+
}
|
|
2033
|
+
function parseTechStackResult(raw) {
|
|
2034
|
+
if (typeof raw === "object" && raw !== null && !Array.isArray(raw) && "flat" in raw) {
|
|
2035
|
+
const obj = raw;
|
|
2036
|
+
return {
|
|
2037
|
+
repo: parseTechStack(obj.repo),
|
|
2038
|
+
apps: parseAppTechStacks(obj.apps),
|
|
2039
|
+
flat: parseTechStack(obj.flat)
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2042
|
+
const flat = parseTechStack(raw);
|
|
2043
|
+
return { repo: flat, apps: [], flat };
|
|
2044
|
+
}
|
|
2045
|
+
function categorizeDependency(depName) {
|
|
2046
|
+
const rule = PACKAGE_MAP[depName];
|
|
2047
|
+
if (rule) return rule.category;
|
|
2048
|
+
for (const { prefix, rule: prefixRule } of PACKAGE_PREFIX_MAP) {
|
|
2049
|
+
if (depName.startsWith(prefix)) return prefixRule.category;
|
|
2050
|
+
}
|
|
2051
|
+
return "other";
|
|
2052
|
+
}
|
|
2053
|
+
async function findPackageJsonFiles(dir, projectPath, depth = 0) {
|
|
2054
|
+
if (depth > 4) return [];
|
|
2055
|
+
const results = [];
|
|
2056
|
+
const pkgPath = join9(dir, "package.json");
|
|
2057
|
+
if (await fileExists(pkgPath)) {
|
|
2058
|
+
results.push(pkgPath);
|
|
2059
|
+
}
|
|
2028
2060
|
try {
|
|
2029
|
-
const
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
console.log("\n Configuring MCP server...");
|
|
2037
|
-
const configPath = await writeMcpConfig(scope);
|
|
2038
|
-
console.log(` Done! Config written to ${configPath}
|
|
2039
|
-
`);
|
|
2040
|
-
let repos = [];
|
|
2041
|
-
try {
|
|
2042
|
-
repos = await fetchRepos(auth);
|
|
2043
|
-
} catch (err) {
|
|
2044
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
2045
|
-
console.log(` ${msg}
|
|
2046
|
-
`);
|
|
2047
|
-
return;
|
|
2048
|
-
}
|
|
2049
|
-
if (repos.length === 0) {
|
|
2050
|
-
console.log(
|
|
2051
|
-
" No repositories found. Create one at https://codebyplan.com after setup.\n"
|
|
2052
|
-
);
|
|
2053
|
-
console.log(
|
|
2054
|
-
" Setup complete! Start a new Claude Code session to begin.\n"
|
|
2055
|
-
);
|
|
2056
|
-
return;
|
|
2057
|
-
}
|
|
2058
|
-
console.log(" Initialize this project?\n");
|
|
2059
|
-
const initAnswer = (await rl.question(" Link to a repository? (Y/n): ")).trim().toLowerCase();
|
|
2060
|
-
if (initAnswer !== "" && initAnswer !== "y" && initAnswer !== "yes") {
|
|
2061
|
-
console.log(
|
|
2062
|
-
" Setup complete! Start a new Claude Code session to begin.\n"
|
|
2061
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
2062
|
+
for (const entry of entries) {
|
|
2063
|
+
if (!entry.isDirectory() || SKIP_DIRS.has(entry.name)) continue;
|
|
2064
|
+
const subResults = await findPackageJsonFiles(
|
|
2065
|
+
join9(dir, entry.name),
|
|
2066
|
+
projectPath,
|
|
2067
|
+
depth + 1
|
|
2063
2068
|
);
|
|
2064
|
-
|
|
2065
|
-
}
|
|
2066
|
-
if (auth.kind === "legacy") {
|
|
2067
|
-
process.env.CODEBYPLAN_API_KEY = auth.apiKey;
|
|
2068
|
-
}
|
|
2069
|
-
console.log("\n Your repositories:\n");
|
|
2070
|
-
for (let i = 0; i < repos.length; i++) {
|
|
2071
|
-
console.log(` ${i + 1}. ${repos[i].name}`);
|
|
2072
|
-
}
|
|
2073
|
-
console.log();
|
|
2074
|
-
const choice = (await rl.question(" Select a repository (number): ")).trim();
|
|
2075
|
-
const index = parseInt(choice, 10) - 1;
|
|
2076
|
-
if (isNaN(index) || index < 0 || index >= repos.length) {
|
|
2077
|
-
console.log(" Invalid selection. Skipping project init.\n");
|
|
2078
|
-
return;
|
|
2079
|
-
}
|
|
2080
|
-
const selectedRepo = repos[index];
|
|
2081
|
-
console.log(`
|
|
2082
|
-
Selected: ${selectedRepo.name}
|
|
2083
|
-
`);
|
|
2084
|
-
const projectPath = process.cwd();
|
|
2085
|
-
const pathBasedId = await resolveAndCacheWorktreeId(
|
|
2086
|
-
selectedRepo.id,
|
|
2087
|
-
projectPath,
|
|
2088
|
-
{ skipWrite: true }
|
|
2089
|
-
);
|
|
2090
|
-
const deviceId = await getOrCreateDeviceId(projectPath);
|
|
2091
|
-
let branch = "main";
|
|
2092
|
-
try {
|
|
2093
|
-
const { execSync: execSync9 } = await import("node:child_process");
|
|
2094
|
-
branch = execSync9("git symbolic-ref --short HEAD", {
|
|
2095
|
-
cwd: projectPath,
|
|
2096
|
-
encoding: "utf-8"
|
|
2097
|
-
}).trim();
|
|
2098
|
-
} catch {
|
|
2069
|
+
results.push(...subResults);
|
|
2099
2070
|
}
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
await writeCodebyplanDirectory(projectPath, selectedRepo, deviceId);
|
|
2109
|
-
const priorExitCode = process.exitCode;
|
|
2110
|
-
let installFailed = false;
|
|
2071
|
+
} catch {
|
|
2072
|
+
}
|
|
2073
|
+
return results;
|
|
2074
|
+
}
|
|
2075
|
+
async function scanAllDependencies(projectPath) {
|
|
2076
|
+
const packageJsonPaths = await findPackageJsonFiles(projectPath, projectPath);
|
|
2077
|
+
const dependencies = [];
|
|
2078
|
+
for (const pkgPath of packageJsonPaths) {
|
|
2111
2079
|
try {
|
|
2112
|
-
const
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2080
|
+
const raw = await readFile7(pkgPath, "utf-8");
|
|
2081
|
+
const pkg = JSON.parse(raw);
|
|
2082
|
+
const sourcePath = relative2(projectPath, pkgPath);
|
|
2083
|
+
const depSections = [
|
|
2084
|
+
{ deps: pkg.dependencies, depType: "production", isDev: false },
|
|
2085
|
+
{ deps: pkg.devDependencies, depType: "dev", isDev: true },
|
|
2086
|
+
{ deps: pkg.peerDependencies, depType: "peer", isDev: false },
|
|
2087
|
+
{ deps: pkg.optionalDependencies, depType: "optional", isDev: false }
|
|
2088
|
+
];
|
|
2089
|
+
for (const { deps, depType, isDev } of depSections) {
|
|
2090
|
+
if (!deps) continue;
|
|
2091
|
+
for (const [name, version] of Object.entries(deps)) {
|
|
2092
|
+
dependencies.push({
|
|
2093
|
+
name,
|
|
2094
|
+
version,
|
|
2095
|
+
category: categorizeDependency(name),
|
|
2096
|
+
source_path: sourcePath,
|
|
2097
|
+
is_dev: isDev,
|
|
2098
|
+
dep_type: depType
|
|
2099
|
+
});
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2118
2102
|
} catch {
|
|
2119
|
-
installFailed = true;
|
|
2120
2103
|
}
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2104
|
+
}
|
|
2105
|
+
return { dependencies };
|
|
2106
|
+
}
|
|
2107
|
+
var PACKAGE_MAP, PACKAGE_PREFIX_MAP, CONFIG_FILE_MAP, SYNTHETIC_CARRIER_NAME, CAPABILITY_BEARER_NAMES, JSX_TEST_PATTERN, JSX_SKIP_DIRS, JSX_SCAN_DIRS, SERVER_MAIN_ENTRIES, SERVER_FRAMEWORK_DEPS, compareByCategoryThenName, SKIP_DIRS;
|
|
2108
|
+
var init_tech_detect = __esm({
|
|
2109
|
+
"src/lib/tech-detect.ts"() {
|
|
2110
|
+
"use strict";
|
|
2111
|
+
PACKAGE_MAP = {
|
|
2112
|
+
// Frameworks
|
|
2113
|
+
next: { name: "Next.js", category: "framework" },
|
|
2114
|
+
nuxt: { name: "Nuxt", category: "framework" },
|
|
2115
|
+
gatsby: { name: "Gatsby", category: "framework" },
|
|
2116
|
+
express: { name: "Express", category: "framework" },
|
|
2117
|
+
fastify: { name: "Fastify", category: "framework" },
|
|
2118
|
+
hono: { name: "Hono", category: "framework" },
|
|
2119
|
+
"@remix-run/node": { name: "Remix", category: "framework" },
|
|
2120
|
+
svelte: { name: "Svelte", category: "framework" },
|
|
2121
|
+
astro: { name: "Astro", category: "framework" },
|
|
2122
|
+
"@angular/core": { name: "Angular", category: "framework" },
|
|
2123
|
+
"@nestjs/core": { name: "NestJS", category: "framework" },
|
|
2124
|
+
// Libraries (UI)
|
|
2125
|
+
react: { name: "React", category: "framework" },
|
|
2126
|
+
vue: { name: "Vue", category: "framework" },
|
|
2127
|
+
"solid-js": { name: "Solid", category: "framework" },
|
|
2128
|
+
preact: { name: "Preact", category: "framework" },
|
|
2129
|
+
// Languages (detected via devDeps)
|
|
2130
|
+
typescript: { name: "TypeScript", category: "language" },
|
|
2131
|
+
// Styling
|
|
2132
|
+
tailwindcss: { name: "Tailwind CSS", category: "styling" },
|
|
2133
|
+
sass: { name: "SCSS", category: "styling" },
|
|
2134
|
+
"styled-components": { name: "styled-components", category: "styling" },
|
|
2135
|
+
"@emotion/react": { name: "Emotion", category: "styling" },
|
|
2136
|
+
// Database
|
|
2137
|
+
prisma: { name: "Prisma", category: "database" },
|
|
2138
|
+
"@prisma/client": { name: "Prisma", category: "database" },
|
|
2139
|
+
"drizzle-orm": { name: "Drizzle", category: "database" },
|
|
2140
|
+
"@supabase/supabase-js": { name: "Supabase", category: "database" },
|
|
2141
|
+
mongoose: { name: "MongoDB", category: "database" },
|
|
2142
|
+
typeorm: { name: "TypeORM", category: "database" },
|
|
2143
|
+
knex: { name: "Knex", category: "database" },
|
|
2144
|
+
// Testing
|
|
2145
|
+
jest: { name: "Jest", category: "testing" },
|
|
2146
|
+
vitest: { name: "Vitest", category: "testing" },
|
|
2147
|
+
mocha: { name: "Mocha", category: "testing" },
|
|
2148
|
+
playwright: { name: "Playwright", category: "testing" },
|
|
2149
|
+
"@playwright/test": { name: "Playwright", category: "testing" },
|
|
2150
|
+
cypress: { name: "Cypress", category: "testing" },
|
|
2151
|
+
supertest: { name: "Supertest", category: "testing" },
|
|
2152
|
+
webdriverio: { name: "WebdriverIO", category: "testing" },
|
|
2153
|
+
"@wdio/cli": { name: "WebdriverIO", category: "testing" },
|
|
2154
|
+
"@vscode/test-cli": { name: "VS Code Test CLI", category: "testing" },
|
|
2155
|
+
// Build tools
|
|
2156
|
+
turbo: { name: "Turborepo", category: "build" },
|
|
2157
|
+
vite: { name: "Vite", category: "build" },
|
|
2158
|
+
webpack: { name: "Webpack", category: "build" },
|
|
2159
|
+
esbuild: { name: "esbuild", category: "build" },
|
|
2160
|
+
rollup: { name: "Rollup", category: "build" },
|
|
2161
|
+
nx: { name: "Nx", category: "build" },
|
|
2162
|
+
lerna: { name: "Lerna", category: "build" },
|
|
2163
|
+
tsup: { name: "tsup", category: "build" },
|
|
2164
|
+
"@swc/core": { name: "SWC", category: "build" },
|
|
2165
|
+
parcel: { name: "Parcel", category: "build" },
|
|
2166
|
+
// Tools
|
|
2167
|
+
eslint: { name: "ESLint", category: "tool" },
|
|
2168
|
+
prettier: { name: "Prettier", category: "tool" },
|
|
2169
|
+
"@biomejs/biome": { name: "Biome", category: "tool" },
|
|
2170
|
+
storybook: { name: "Storybook", category: "tool" },
|
|
2171
|
+
// Component libs
|
|
2172
|
+
"@mui/material": { name: "MUI", category: "component-lib" },
|
|
2173
|
+
"@chakra-ui/react": { name: "Chakra UI", category: "component-lib" },
|
|
2174
|
+
"@mantine/core": { name: "Mantine", category: "component-lib" },
|
|
2175
|
+
// GraphQL
|
|
2176
|
+
graphql: { name: "GraphQL", category: "graphql" },
|
|
2177
|
+
"@apollo/client": { name: "Apollo Client", category: "graphql" },
|
|
2178
|
+
urql: { name: "urql", category: "graphql" },
|
|
2179
|
+
"graphql-request": { name: "graphql-request", category: "graphql" },
|
|
2180
|
+
// Documentation
|
|
2181
|
+
typedoc: { name: "TypeDoc", category: "documentation" },
|
|
2182
|
+
"@docusaurus/core": { name: "Docusaurus", category: "documentation" },
|
|
2183
|
+
vitepress: { name: "VitePress", category: "documentation" },
|
|
2184
|
+
// Code quality
|
|
2185
|
+
husky: { name: "Husky", category: "quality" },
|
|
2186
|
+
"lint-staged": { name: "lint-staged", category: "quality" },
|
|
2187
|
+
commitlint: { name: "commitlint", category: "quality" },
|
|
2188
|
+
"@commitlint/cli": { name: "commitlint", category: "quality" },
|
|
2189
|
+
// Mobile
|
|
2190
|
+
"react-native": { name: "React Native", category: "mobile" },
|
|
2191
|
+
expo: { name: "Expo", category: "mobile" }
|
|
2192
|
+
};
|
|
2193
|
+
PACKAGE_PREFIX_MAP = [
|
|
2194
|
+
{
|
|
2195
|
+
prefix: "@radix-ui/",
|
|
2196
|
+
rule: { name: "Radix UI", category: "component-lib" }
|
|
2197
|
+
},
|
|
2198
|
+
{ prefix: "@storybook/", rule: { name: "Storybook", category: "tool" } },
|
|
2199
|
+
{
|
|
2200
|
+
prefix: "@testing-library/",
|
|
2201
|
+
rule: { name: "Testing Library", category: "testing" }
|
|
2202
|
+
}
|
|
2203
|
+
];
|
|
2204
|
+
CONFIG_FILE_MAP = [
|
|
2205
|
+
{ file: "tsconfig.json", rule: { name: "TypeScript", category: "language" } },
|
|
2206
|
+
{ file: "next.config.js", rule: { name: "Next.js", category: "framework" } },
|
|
2207
|
+
{ file: "next.config.mjs", rule: { name: "Next.js", category: "framework" } },
|
|
2208
|
+
{ file: "next.config.ts", rule: { name: "Next.js", category: "framework" } },
|
|
2209
|
+
{
|
|
2210
|
+
file: "tailwind.config.js",
|
|
2211
|
+
rule: { name: "Tailwind CSS", category: "styling" }
|
|
2212
|
+
},
|
|
2213
|
+
{
|
|
2214
|
+
file: "tailwind.config.ts",
|
|
2215
|
+
rule: { name: "Tailwind CSS", category: "styling" }
|
|
2216
|
+
},
|
|
2217
|
+
{ file: "turbo.json", rule: { name: "Turborepo", category: "build" } },
|
|
2218
|
+
{
|
|
2219
|
+
file: "docker-compose.yml",
|
|
2220
|
+
rule: { name: "Docker", category: "deployment" }
|
|
2221
|
+
},
|
|
2222
|
+
{
|
|
2223
|
+
file: "docker-compose.yaml",
|
|
2224
|
+
rule: { name: "Docker", category: "deployment" }
|
|
2225
|
+
},
|
|
2226
|
+
{ file: "Dockerfile", rule: { name: "Docker", category: "deployment" } },
|
|
2227
|
+
{ file: "vercel.json", rule: { name: "Vercel", category: "deployment" } },
|
|
2228
|
+
{ file: ".storybook/main.js", rule: { name: "Storybook", category: "tool" } },
|
|
2229
|
+
{ file: ".storybook/main.ts", rule: { name: "Storybook", category: "tool" } },
|
|
2230
|
+
{
|
|
2231
|
+
file: ".storybook/main.mjs",
|
|
2232
|
+
rule: { name: "Storybook", category: "tool" }
|
|
2233
|
+
},
|
|
2234
|
+
{
|
|
2235
|
+
file: "components.json",
|
|
2236
|
+
rule: { name: "shadcn/ui", category: "component-lib" }
|
|
2237
|
+
},
|
|
2238
|
+
{ file: "nx.json", rule: { name: "Nx", category: "build" } },
|
|
2239
|
+
{ file: "lerna.json", rule: { name: "Lerna", category: "build" } },
|
|
2240
|
+
{
|
|
2241
|
+
file: "playwright.config.ts",
|
|
2242
|
+
rule: { name: "Playwright", category: "testing" }
|
|
2243
|
+
},
|
|
2244
|
+
{
|
|
2245
|
+
file: "playwright.config.js",
|
|
2246
|
+
rule: { name: "Playwright", category: "testing" }
|
|
2247
|
+
},
|
|
2248
|
+
{
|
|
2249
|
+
file: "playwright.config.mjs",
|
|
2250
|
+
rule: { name: "Playwright", category: "testing" }
|
|
2251
|
+
},
|
|
2252
|
+
{ file: "wdio.conf.ts", rule: { name: "WebdriverIO", category: "testing" } },
|
|
2253
|
+
{
|
|
2254
|
+
file: "wdio.conf.js",
|
|
2255
|
+
rule: { name: "WebdriverIO", category: "testing" }
|
|
2256
|
+
},
|
|
2257
|
+
{
|
|
2258
|
+
file: "maestro/config.yaml",
|
|
2259
|
+
rule: { name: "Maestro", category: "testing" }
|
|
2150
2260
|
}
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
"
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2261
|
+
];
|
|
2262
|
+
SYNTHETIC_CARRIER_NAME = "__capabilities__";
|
|
2263
|
+
CAPABILITY_BEARER_NAMES = /* @__PURE__ */ new Set([
|
|
2264
|
+
"react",
|
|
2265
|
+
"next.js",
|
|
2266
|
+
"vue",
|
|
2267
|
+
"svelte",
|
|
2268
|
+
"solid",
|
|
2269
|
+
"preact",
|
|
2270
|
+
"remix",
|
|
2271
|
+
"astro",
|
|
2272
|
+
"angular",
|
|
2273
|
+
"nestjs",
|
|
2274
|
+
"nuxt",
|
|
2275
|
+
"gatsby",
|
|
2276
|
+
"express",
|
|
2277
|
+
"fastify",
|
|
2278
|
+
"hono",
|
|
2279
|
+
"react native",
|
|
2280
|
+
"expo"
|
|
2281
|
+
]);
|
|
2282
|
+
JSX_TEST_PATTERN = /\.(test|spec)\.(tsx|jsx)$/;
|
|
2283
|
+
JSX_SKIP_DIRS = /* @__PURE__ */ new Set(["__tests__", "test", "tests"]);
|
|
2284
|
+
JSX_SCAN_DIRS = ["src", "app", "pages"];
|
|
2285
|
+
SERVER_MAIN_ENTRIES = /* @__PURE__ */ new Set([
|
|
2286
|
+
"dist/main.js",
|
|
2287
|
+
"dist/server.js",
|
|
2288
|
+
"dist/index.js",
|
|
2289
|
+
"main.js",
|
|
2290
|
+
"server.js"
|
|
2291
|
+
]);
|
|
2292
|
+
SERVER_FRAMEWORK_DEPS = /* @__PURE__ */ new Set([
|
|
2293
|
+
"@nestjs/core",
|
|
2294
|
+
"express",
|
|
2295
|
+
"fastify",
|
|
2296
|
+
"hono"
|
|
2297
|
+
]);
|
|
2298
|
+
compareByCategoryThenName = (a, b) => {
|
|
2299
|
+
const catCmp = a.category.localeCompare(b.category);
|
|
2300
|
+
if (catCmp !== 0) return catCmp;
|
|
2301
|
+
return a.name.localeCompare(b.name);
|
|
2302
|
+
};
|
|
2303
|
+
SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
2304
|
+
"node_modules",
|
|
2305
|
+
".next",
|
|
2306
|
+
"dist",
|
|
2307
|
+
".turbo",
|
|
2308
|
+
".git",
|
|
2309
|
+
"coverage",
|
|
2310
|
+
"build",
|
|
2311
|
+
"out",
|
|
2312
|
+
".vercel",
|
|
2313
|
+
".expo"
|
|
2314
|
+
]);
|
|
2172
2315
|
}
|
|
2173
2316
|
});
|
|
2174
2317
|
|
|
2175
|
-
// src/lib/
|
|
2176
|
-
import {
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
const
|
|
2189
|
-
|
|
2190
|
-
const raw = await readFile7(legacyPath, "utf-8");
|
|
2191
|
-
const parsed = JSON.parse(raw);
|
|
2192
|
-
return { path: legacyPath, contents: parsed };
|
|
2193
|
-
} catch {
|
|
2194
|
-
}
|
|
2195
|
-
const parent = resolve2(cursor, "..");
|
|
2196
|
-
if (parent === cursor) return null;
|
|
2197
|
-
cursor = parent;
|
|
2198
|
-
}
|
|
2199
|
-
return null;
|
|
2200
|
-
}
|
|
2201
|
-
function parseFlags(startIndex) {
|
|
2202
|
-
const flags = {};
|
|
2203
|
-
const args = process.argv.slice(startIndex);
|
|
2204
|
-
for (let i = 0; i < args.length; i++) {
|
|
2205
|
-
const arg = args[i];
|
|
2206
|
-
if (arg.startsWith("--") && i + 1 < args.length) {
|
|
2207
|
-
const key = arg.slice(2);
|
|
2208
|
-
flags[key] = args[++i];
|
|
2209
|
-
}
|
|
2318
|
+
// src/lib/lsp-detect.ts
|
|
2319
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
2320
|
+
function mapTechStackToLsp(detectedNames) {
|
|
2321
|
+
const pluginIndex = new Map(
|
|
2322
|
+
LSP_TABLE.map((s) => [s.plugin, s])
|
|
2323
|
+
);
|
|
2324
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2325
|
+
const result = [];
|
|
2326
|
+
for (const name of detectedNames) {
|
|
2327
|
+
const pluginId = ALIAS_MAP[name.toLowerCase()];
|
|
2328
|
+
if (!pluginId) continue;
|
|
2329
|
+
if (seen.has(pluginId)) continue;
|
|
2330
|
+
seen.add(pluginId);
|
|
2331
|
+
const server = pluginIndex.get(pluginId);
|
|
2332
|
+
if (server) result.push(server);
|
|
2210
2333
|
}
|
|
2211
|
-
return
|
|
2334
|
+
return result;
|
|
2212
2335
|
}
|
|
2213
|
-
function
|
|
2214
|
-
return
|
|
2336
|
+
function binaryOnPath(binary) {
|
|
2337
|
+
return probeWith("which", binary) || probeWith("where", binary);
|
|
2215
2338
|
}
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
if (
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
}
|
|
2227
|
-
if (!repoId) {
|
|
2228
|
-
throw new Error(
|
|
2229
|
-
`Could not determine repo_id.
|
|
2230
|
-
|
|
2231
|
-
Provide it via one of:
|
|
2232
|
-
--repo-id <uuid> CLI flag
|
|
2233
|
-
CODEBYPLAN_REPO_ID=<uuid> environment variable
|
|
2234
|
-
.codebyplan/repo.json { "repo_id": "<uuid>" } in project root
|
|
2235
|
-
Run 'codebyplan setup' to initialize this project`
|
|
2236
|
-
);
|
|
2339
|
+
function probeWith(probe, binary) {
|
|
2340
|
+
try {
|
|
2341
|
+
const result = spawnSync2(probe, [binary], {
|
|
2342
|
+
encoding: "utf-8",
|
|
2343
|
+
timeout: 5e3
|
|
2344
|
+
});
|
|
2345
|
+
if (result.error) return false;
|
|
2346
|
+
return result.status === 0;
|
|
2347
|
+
} catch {
|
|
2348
|
+
return false;
|
|
2237
2349
|
}
|
|
2238
|
-
return { repoId, worktreeId, projectPath };
|
|
2239
2350
|
}
|
|
2240
|
-
var
|
|
2241
|
-
|
|
2351
|
+
var LSP_TABLE, ALIAS_MAP;
|
|
2352
|
+
var init_lsp_detect = __esm({
|
|
2353
|
+
"src/lib/lsp-detect.ts"() {
|
|
2242
2354
|
"use strict";
|
|
2355
|
+
LSP_TABLE = [
|
|
2356
|
+
{
|
|
2357
|
+
language: "TypeScript",
|
|
2358
|
+
plugin: "typescript-lsp",
|
|
2359
|
+
binary: "typescript-language-server",
|
|
2360
|
+
npmPackage: "typescript-language-server",
|
|
2361
|
+
installHint: "npm install -g typescript-language-server"
|
|
2362
|
+
},
|
|
2363
|
+
{
|
|
2364
|
+
language: "Python",
|
|
2365
|
+
plugin: "pyright-lsp",
|
|
2366
|
+
binary: "pyright-langserver",
|
|
2367
|
+
npmPackage: "pyright",
|
|
2368
|
+
installHint: "npm install -g pyright"
|
|
2369
|
+
},
|
|
2370
|
+
{
|
|
2371
|
+
language: "PHP",
|
|
2372
|
+
plugin: "php-lsp",
|
|
2373
|
+
binary: "intelephense",
|
|
2374
|
+
npmPackage: "intelephense",
|
|
2375
|
+
installHint: "npm install -g intelephense"
|
|
2376
|
+
},
|
|
2377
|
+
{
|
|
2378
|
+
language: "Rust",
|
|
2379
|
+
plugin: "rust-analyzer-lsp",
|
|
2380
|
+
binary: "rust-analyzer",
|
|
2381
|
+
npmPackage: null,
|
|
2382
|
+
installHint: "rustup component add rust-analyzer"
|
|
2383
|
+
},
|
|
2384
|
+
{
|
|
2385
|
+
language: "Go",
|
|
2386
|
+
plugin: "gopls-lsp",
|
|
2387
|
+
binary: "gopls",
|
|
2388
|
+
npmPackage: null,
|
|
2389
|
+
installHint: "go install golang.org/x/tools/gopls@latest"
|
|
2390
|
+
},
|
|
2391
|
+
{
|
|
2392
|
+
language: "Swift",
|
|
2393
|
+
plugin: "swift-lsp",
|
|
2394
|
+
binary: "sourcekit-lsp",
|
|
2395
|
+
npmPackage: null,
|
|
2396
|
+
installHint: "Install Xcode and the Swift toolchain; sourcekit-lsp is included in the toolchain"
|
|
2397
|
+
},
|
|
2398
|
+
{
|
|
2399
|
+
language: "C/C++",
|
|
2400
|
+
plugin: "clangd-lsp",
|
|
2401
|
+
binary: "clangd",
|
|
2402
|
+
npmPackage: null,
|
|
2403
|
+
installHint: "brew install llvm # macOS\napt install clangd # Debian/Ubuntu"
|
|
2404
|
+
},
|
|
2405
|
+
{
|
|
2406
|
+
language: "C#",
|
|
2407
|
+
plugin: "csharp-lsp",
|
|
2408
|
+
binary: "csharp-ls",
|
|
2409
|
+
npmPackage: null,
|
|
2410
|
+
installHint: "dotnet tool install -g csharp-ls"
|
|
2411
|
+
},
|
|
2412
|
+
{
|
|
2413
|
+
language: "Java",
|
|
2414
|
+
plugin: "jdtls-lsp",
|
|
2415
|
+
binary: "jdtls",
|
|
2416
|
+
npmPackage: null,
|
|
2417
|
+
installHint: "Download Eclipse JDT LS from https://github.com/eclipse-jdtls/eclipse.jdt.ls/releases"
|
|
2418
|
+
},
|
|
2419
|
+
{
|
|
2420
|
+
language: "Kotlin",
|
|
2421
|
+
plugin: "kotlin-lsp",
|
|
2422
|
+
binary: "kotlin-language-server",
|
|
2423
|
+
npmPackage: null,
|
|
2424
|
+
installHint: "Download from https://github.com/fwcd/kotlin-language-server/releases"
|
|
2425
|
+
},
|
|
2426
|
+
{
|
|
2427
|
+
language: "Lua",
|
|
2428
|
+
plugin: "lua-lsp",
|
|
2429
|
+
binary: "lua-language-server",
|
|
2430
|
+
npmPackage: null,
|
|
2431
|
+
installHint: "brew install lua-language-server # macOS"
|
|
2432
|
+
}
|
|
2433
|
+
];
|
|
2434
|
+
ALIAS_MAP = {
|
|
2435
|
+
// TypeScript / JavaScript
|
|
2436
|
+
typescript: "typescript-lsp",
|
|
2437
|
+
javascript: "typescript-lsp",
|
|
2438
|
+
"next.js": "typescript-lsp",
|
|
2439
|
+
react: "typescript-lsp",
|
|
2440
|
+
vue: "typescript-lsp",
|
|
2441
|
+
svelte: "typescript-lsp",
|
|
2442
|
+
angular: "typescript-lsp",
|
|
2443
|
+
nestjs: "typescript-lsp",
|
|
2444
|
+
// Python
|
|
2445
|
+
python: "pyright-lsp",
|
|
2446
|
+
// PHP
|
|
2447
|
+
php: "php-lsp",
|
|
2448
|
+
// Rust
|
|
2449
|
+
rust: "rust-analyzer-lsp",
|
|
2450
|
+
// Go
|
|
2451
|
+
go: "gopls-lsp",
|
|
2452
|
+
golang: "gopls-lsp",
|
|
2453
|
+
// Swift
|
|
2454
|
+
swift: "swift-lsp",
|
|
2455
|
+
// C/C++
|
|
2456
|
+
"c++": "clangd-lsp",
|
|
2457
|
+
"c/c++": "clangd-lsp",
|
|
2458
|
+
c: "clangd-lsp",
|
|
2459
|
+
clang: "clangd-lsp",
|
|
2460
|
+
cpp: "clangd-lsp",
|
|
2461
|
+
// C#
|
|
2462
|
+
"c#": "csharp-lsp",
|
|
2463
|
+
csharp: "csharp-lsp",
|
|
2464
|
+
".net": "csharp-lsp",
|
|
2465
|
+
dotnet: "csharp-lsp",
|
|
2466
|
+
// Java
|
|
2467
|
+
java: "jdtls-lsp",
|
|
2468
|
+
// Kotlin
|
|
2469
|
+
kotlin: "kotlin-lsp",
|
|
2470
|
+
// Lua
|
|
2471
|
+
lua: "lua-lsp"
|
|
2472
|
+
};
|
|
2243
2473
|
}
|
|
2244
2474
|
});
|
|
2245
2475
|
|
|
2246
|
-
// src/cli/
|
|
2247
|
-
var
|
|
2248
|
-
__export(
|
|
2249
|
-
|
|
2476
|
+
// src/cli/lsp.ts
|
|
2477
|
+
var lsp_exports = {};
|
|
2478
|
+
__export(lsp_exports, {
|
|
2479
|
+
runLsp: () => runLsp,
|
|
2480
|
+
runLspFull: () => runLspFull
|
|
2250
2481
|
});
|
|
2251
|
-
import {
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2482
|
+
import { readFile as readFile8, writeFile as writeFile6, mkdir as mkdir4, unlink as unlink2, access as access2 } from "node:fs/promises";
|
|
2483
|
+
import { join as join10 } from "node:path";
|
|
2484
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
2485
|
+
async function fileExists2(filePath) {
|
|
2486
|
+
try {
|
|
2487
|
+
await access2(filePath);
|
|
2488
|
+
return true;
|
|
2489
|
+
} catch {
|
|
2490
|
+
return false;
|
|
2256
2491
|
}
|
|
2257
|
-
return process.cwd();
|
|
2258
2492
|
}
|
|
2259
|
-
function
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
return
|
|
2493
|
+
async function readJsonFile(filePath) {
|
|
2494
|
+
try {
|
|
2495
|
+
const raw = await readFile8(filePath, "utf-8");
|
|
2496
|
+
return JSON.parse(raw);
|
|
2497
|
+
} catch {
|
|
2498
|
+
return null;
|
|
2263
2499
|
}
|
|
2264
|
-
return null;
|
|
2265
2500
|
}
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2501
|
+
function tryNpmInstallGlobal(pkg) {
|
|
2502
|
+
try {
|
|
2503
|
+
const result = spawnSync3("npm", ["i", "-g", pkg], {
|
|
2504
|
+
encoding: "utf-8",
|
|
2505
|
+
timeout: 6e4,
|
|
2506
|
+
// "pipe" (not "inherit"): inheriting the parent's fds can surface EPIPE/EIO
|
|
2507
|
+
// in piped/CI contexts as result.error even when npm itself succeeded, and
|
|
2508
|
+
// interleaves npm output with our own console.log lines unpredictably.
|
|
2509
|
+
stdio: "pipe"
|
|
2510
|
+
});
|
|
2511
|
+
if (result.error) return false;
|
|
2512
|
+
return result.status === 0;
|
|
2513
|
+
} catch {
|
|
2514
|
+
return false;
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
async function runLspFull(projectPath, opts = {}) {
|
|
2518
|
+
const dryRun = opts.dryRun ?? false;
|
|
2519
|
+
console.log("\n CodeByPlan LSP");
|
|
2520
|
+
console.log(` Path: ${projectPath}
|
|
2521
|
+
`);
|
|
2522
|
+
const rootDetected = await detectTechStack(projectPath);
|
|
2523
|
+
const allTechNames = /* @__PURE__ */ new Set();
|
|
2524
|
+
for (const entry of rootDetected.flat) {
|
|
2525
|
+
allTechNames.add(entry.name);
|
|
2526
|
+
}
|
|
2527
|
+
const servers = mapTechStackToLsp(Array.from(allTechNames));
|
|
2528
|
+
if (servers.length === 0) {
|
|
2529
|
+
console.log(" No LSP-relevant tech detected. Nothing to enable.\n");
|
|
2530
|
+
return;
|
|
2531
|
+
}
|
|
2532
|
+
console.log(
|
|
2533
|
+
` Detected LSP servers: ${servers.map((s) => s.plugin).join(", ")}
|
|
2534
|
+
`
|
|
2271
2535
|
);
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2536
|
+
const settingsPath = join10(projectPath, ".claude", "settings.json");
|
|
2537
|
+
let settings = {};
|
|
2538
|
+
const existingSettingsRaw = await readJsonFile(settingsPath);
|
|
2539
|
+
if (existingSettingsRaw) {
|
|
2540
|
+
settings = existingSettingsRaw;
|
|
2541
|
+
}
|
|
2542
|
+
mergeEnabledPluginsIntoSettings(
|
|
2543
|
+
settings,
|
|
2544
|
+
servers.map((s) => s.plugin)
|
|
2545
|
+
);
|
|
2546
|
+
if (dryRun) {
|
|
2547
|
+
console.log(` [dry-run] would update ${settingsPath}`);
|
|
2548
|
+
} else {
|
|
2549
|
+
await mkdir4(join10(projectPath, ".claude"), { recursive: true });
|
|
2550
|
+
await writeFile6(
|
|
2551
|
+
settingsPath,
|
|
2552
|
+
JSON.stringify(settings, null, 2) + "\n",
|
|
2553
|
+
"utf-8"
|
|
2275
2554
|
);
|
|
2276
|
-
|
|
2277
|
-
return;
|
|
2555
|
+
console.log(` Updated ${settingsPath}`);
|
|
2278
2556
|
}
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2557
|
+
const lspJsonPath = join10(projectPath, ".codebyplan", "lsp.json");
|
|
2558
|
+
const lspJsonContent = {
|
|
2559
|
+
servers: servers.map((s) => ({
|
|
2560
|
+
plugin: s.plugin,
|
|
2561
|
+
binary: s.binary,
|
|
2562
|
+
install: s.npmPackage ? `npm i -g ${s.npmPackage}` : s.installHint
|
|
2563
|
+
}))
|
|
2564
|
+
};
|
|
2565
|
+
if (dryRun) {
|
|
2566
|
+
console.log(` [dry-run] would write ${lspJsonPath}`);
|
|
2567
|
+
} else {
|
|
2568
|
+
await mkdir4(join10(projectPath, ".codebyplan"), { recursive: true });
|
|
2569
|
+
await writeFile6(
|
|
2570
|
+
lspJsonPath,
|
|
2571
|
+
JSON.stringify(lspJsonContent, null, 2) + "\n",
|
|
2572
|
+
"utf-8"
|
|
2573
|
+
);
|
|
2574
|
+
console.log(` Wrote ${lspJsonPath}`);
|
|
2575
|
+
}
|
|
2576
|
+
const todoDir = join10(projectPath, ".codebyplan", "todo", "session-start");
|
|
2577
|
+
const autoInstalled = [];
|
|
2578
|
+
const nudged = [];
|
|
2579
|
+
for (const server of servers) {
|
|
2580
|
+
if (binaryOnPath(server.binary)) {
|
|
2581
|
+
continue;
|
|
2582
|
+
}
|
|
2583
|
+
if (server.npmPackage !== null) {
|
|
2584
|
+
if (dryRun) {
|
|
2585
|
+
console.log(` [dry-run] would install ${server.npmPackage}`);
|
|
2586
|
+
autoInstalled.push(server.plugin);
|
|
2587
|
+
} else {
|
|
2588
|
+
console.log(` Installing ${server.npmPackage} globally...`);
|
|
2589
|
+
const success = tryNpmInstallGlobal(server.npmPackage);
|
|
2590
|
+
if (success && binaryOnPath(server.binary)) {
|
|
2591
|
+
autoInstalled.push(server.plugin);
|
|
2592
|
+
console.log(` Installed ${server.npmPackage}.`);
|
|
2593
|
+
continue;
|
|
2594
|
+
}
|
|
2595
|
+
console.log(
|
|
2596
|
+
` npm install failed for ${server.npmPackage}. Writing nudge file.`
|
|
2597
|
+
);
|
|
2598
|
+
await mkdir4(todoDir, { recursive: true });
|
|
2599
|
+
const nudgeFile = join10(todoDir, `install-${server.binary}.md`);
|
|
2600
|
+
const nudgeContent = `Run \`${server.installHint}\` to enable ${server.language} LSP support in Claude Code.
|
|
2601
|
+
`;
|
|
2602
|
+
await writeFile6(nudgeFile, nudgeContent, "utf-8");
|
|
2603
|
+
nudged.push(server.binary);
|
|
2604
|
+
}
|
|
2605
|
+
} else {
|
|
2606
|
+
if (dryRun) {
|
|
2607
|
+
console.log(` [dry-run] would write nudge for ${server.binary}`);
|
|
2608
|
+
nudged.push(server.binary);
|
|
2609
|
+
} else {
|
|
2610
|
+
await mkdir4(todoDir, { recursive: true });
|
|
2611
|
+
const nudgeFile = join10(todoDir, `install-${server.binary}.md`);
|
|
2612
|
+
const nudgeContent = `Run \`${server.installHint}\` to enable ${server.language} LSP support in Claude Code.
|
|
2613
|
+
`;
|
|
2614
|
+
await writeFile6(nudgeFile, nudgeContent, "utf-8");
|
|
2615
|
+
nudged.push(server.binary);
|
|
2616
|
+
}
|
|
2285
2617
|
}
|
|
2286
2618
|
}
|
|
2287
|
-
const
|
|
2288
|
-
|
|
2619
|
+
const pluginNames = servers.map((s) => s.plugin);
|
|
2620
|
+
console.log(`
|
|
2621
|
+
Summary:`);
|
|
2622
|
+
console.log(
|
|
2623
|
+
` Enabled plugins: ${pluginNames.length > 0 ? pluginNames.join(", ") : "none"}`
|
|
2289
2624
|
);
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
codebyplan statusline bash|node|python Set the renderer
|
|
2297
|
-
codebyplan statusline --bash|--node|--python Set the renderer (flag form)
|
|
2298
|
-
`
|
|
2625
|
+
console.log(
|
|
2626
|
+
` (settings.json key format: <plugin>@claude-plugins-official)`
|
|
2627
|
+
);
|
|
2628
|
+
if (autoInstalled.length > 0) {
|
|
2629
|
+
console.log(
|
|
2630
|
+
` ${dryRun ? "Would auto-install:" : "Auto-installed: "} ${autoInstalled.join(", ")}`
|
|
2299
2631
|
);
|
|
2300
|
-
process.exitCode = 1;
|
|
2301
|
-
return;
|
|
2302
2632
|
}
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
`codebyplan statusline: unknown flag '${unknownFlags[0]}'.
|
|
2307
|
-
|
|
2308
|
-
Usage:
|
|
2309
|
-
codebyplan statusline Show current renderer and line toggles
|
|
2310
|
-
codebyplan statusline bash|node|python Set the renderer
|
|
2311
|
-
codebyplan statusline --bash|--node|--python Set the renderer (flag form)
|
|
2312
|
-
`
|
|
2633
|
+
if (nudged.length > 0) {
|
|
2634
|
+
console.log(
|
|
2635
|
+
` ${dryRun ? "Would write nudge for:" : "Manual install required for:"} ${nudged.join(", ")}`
|
|
2313
2636
|
);
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
const projectPath = await resolveProjectPath();
|
|
2318
|
-
if (rendererArg !== void 0) {
|
|
2319
|
-
const chosen = parseRendererArg(rendererArg);
|
|
2320
|
-
await writeStatuslineLocalConfig(projectPath, { renderer: chosen });
|
|
2321
|
-
const availability2 = detectAvailableRenderers();
|
|
2322
|
-
if (!availability2[chosen]) {
|
|
2323
|
-
process.stderr.write(
|
|
2324
|
-
`Warning: '${chosen}' was not detected on this machine. The renderer is saved but may not run.
|
|
2325
|
-
`
|
|
2637
|
+
if (!dryRun) {
|
|
2638
|
+
console.log(
|
|
2639
|
+
` See .codebyplan/todo/session-start/ for install instructions.`
|
|
2326
2640
|
);
|
|
2327
2641
|
}
|
|
2328
|
-
console.log(`Statusline renderer set to '${chosen}'.`);
|
|
2329
|
-
return;
|
|
2330
2642
|
}
|
|
2331
|
-
const availability = detectAvailableRenderers();
|
|
2332
|
-
const [renderer, config] = await Promise.all([
|
|
2333
|
-
resolveRenderer(projectPath),
|
|
2334
|
-
readStatuslineConfig(projectPath)
|
|
2335
|
-
]);
|
|
2336
|
-
const availStr = Object.entries(availability).map(([k, v]) => `${k}: ${v ? "yes" : "no"}`).join(", ");
|
|
2337
|
-
console.log(`
|
|
2338
|
-
Statusline configuration`);
|
|
2339
|
-
console.log(` Renderer: ${renderer}`);
|
|
2340
|
-
console.log(` Available: ${availStr}`);
|
|
2341
|
-
console.log(` no_color: ${config.no_color}`);
|
|
2342
2643
|
console.log(`
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2644
|
+
LSP setup complete.
|
|
2645
|
+
`);
|
|
2646
|
+
}
|
|
2647
|
+
async function runLspCheck(projectPath) {
|
|
2648
|
+
const lspJsonPath = join10(projectPath, ".codebyplan", "lsp.json");
|
|
2649
|
+
const lspJson = await readJsonFile(lspJsonPath);
|
|
2650
|
+
if (!lspJson || !Array.isArray(lspJson.servers)) {
|
|
2651
|
+
return;
|
|
2652
|
+
}
|
|
2653
|
+
const todoDir = join10(projectPath, ".codebyplan", "todo", "session-start");
|
|
2654
|
+
const stillMissing = [];
|
|
2655
|
+
for (const entry of lspJson.servers) {
|
|
2656
|
+
const { binary, plugin } = entry;
|
|
2657
|
+
const found = binaryOnPath(binary);
|
|
2658
|
+
const fullServer = LSP_TABLE.find((s) => s.plugin === plugin);
|
|
2659
|
+
if (found) {
|
|
2660
|
+
const nudgeFile = join10(todoDir, `install-${binary}.md`);
|
|
2661
|
+
if (await fileExists2(nudgeFile)) {
|
|
2662
|
+
try {
|
|
2663
|
+
await unlink2(nudgeFile);
|
|
2664
|
+
} catch {
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
} else {
|
|
2668
|
+
const hint = fullServer?.installHint ?? entry.install;
|
|
2669
|
+
stillMissing.push(hint);
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
for (const hint of stillMissing) {
|
|
2673
|
+
console.log(hint);
|
|
2346
2674
|
}
|
|
2347
|
-
console.log();
|
|
2348
2675
|
}
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
"
|
|
2676
|
+
async function runLsp() {
|
|
2677
|
+
const checkMode = hasFlag("check", 3);
|
|
2678
|
+
const dryRun = hasFlag("dry-run", 3);
|
|
2679
|
+
const pathIdx = process.argv.indexOf("--path");
|
|
2680
|
+
const pathVal = pathIdx !== -1 ? process.argv[pathIdx + 1] : void 0;
|
|
2681
|
+
const projectPath = pathVal != null && !pathVal.startsWith("--") ? pathVal : process.cwd();
|
|
2682
|
+
if (checkMode) {
|
|
2683
|
+
await runLspCheck(projectPath);
|
|
2684
|
+
} else {
|
|
2685
|
+
await runLspFull(projectPath, { dryRun });
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
var init_lsp = __esm({
|
|
2689
|
+
"src/cli/lsp.ts"() {
|
|
2352
2690
|
"use strict";
|
|
2353
2691
|
init_flags();
|
|
2354
|
-
|
|
2355
|
-
|
|
2692
|
+
init_tech_detect();
|
|
2693
|
+
init_lsp_detect();
|
|
2694
|
+
init_settings_merge();
|
|
2356
2695
|
}
|
|
2357
2696
|
});
|
|
2358
2697
|
|
|
2359
|
-
// src/cli/
|
|
2360
|
-
var
|
|
2361
|
-
__export(
|
|
2362
|
-
|
|
2698
|
+
// src/cli/claude/install.ts
|
|
2699
|
+
var install_exports = {};
|
|
2700
|
+
__export(install_exports, {
|
|
2701
|
+
resolveTemplatesDir: () => resolveTemplatesDir,
|
|
2702
|
+
runInstall: () => runInstall
|
|
2363
2703
|
});
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2704
|
+
import * as fs3 from "node:fs";
|
|
2705
|
+
import * as os2 from "node:os";
|
|
2706
|
+
import * as path4 from "node:path";
|
|
2707
|
+
import { fileURLToPath } from "node:url";
|
|
2708
|
+
function resolveTemplatesDir() {
|
|
2709
|
+
const here = path4.dirname(fileURLToPath(import.meta.url));
|
|
2710
|
+
const candidates = [
|
|
2711
|
+
path4.resolve(here, "..", "templates"),
|
|
2712
|
+
path4.resolve(here, "..", "..", "templates"),
|
|
2713
|
+
path4.resolve(here, "..", "..", "..", "templates")
|
|
2714
|
+
];
|
|
2715
|
+
for (const c of candidates) {
|
|
2716
|
+
if (fs3.existsSync(c) && fs3.statSync(c).isDirectory()) {
|
|
2717
|
+
return c;
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
throw new Error(
|
|
2721
|
+
`codebyplan: could not locate templates/ directory. Probed:
|
|
2722
|
+
${candidates.join(
|
|
2723
|
+
"\n "
|
|
2724
|
+
)}`
|
|
2725
|
+
);
|
|
2368
2726
|
}
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2727
|
+
async function runInstall(opts, deps = {}) {
|
|
2728
|
+
await Promise.resolve();
|
|
2729
|
+
const scope = opts.scope ?? "project";
|
|
2730
|
+
if (scope === "user") {
|
|
2731
|
+
if (opts.renderer) {
|
|
2732
|
+
console.warn(
|
|
2733
|
+
"codebyplan claude install: --bash/--node/--python is ignored for --scope user (no project root for statusline.local.json)."
|
|
2734
|
+
);
|
|
2735
|
+
}
|
|
2736
|
+
runInstallUser(opts, deps);
|
|
2737
|
+
return;
|
|
2374
2738
|
}
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
// src/cli/whoami.ts
|
|
2378
|
-
var whoami_exports = {};
|
|
2379
|
-
__export(whoami_exports, {
|
|
2380
|
-
runWhoami: () => runWhoami
|
|
2381
|
-
});
|
|
2382
|
-
async function fetchMe(accessToken) {
|
|
2739
|
+
const projectDir = deps.projectDir ?? process.cwd();
|
|
2740
|
+
let templatesDir;
|
|
2383
2741
|
try {
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
return
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2742
|
+
templatesDir = deps.templatesDir ?? resolveTemplatesDir();
|
|
2743
|
+
} catch (err) {
|
|
2744
|
+
console.error(
|
|
2745
|
+
err instanceof Error ? err.message : `codebyplan claude install: ${String(err)}`
|
|
2746
|
+
);
|
|
2747
|
+
process.exitCode = 1;
|
|
2748
|
+
return;
|
|
2749
|
+
}
|
|
2750
|
+
try {
|
|
2751
|
+
const files = walkTemplates(templatesDir);
|
|
2752
|
+
const manifestEntries = [];
|
|
2753
|
+
for (const f of files) {
|
|
2754
|
+
const absDest = path4.join(projectDir, ".claude", f.dest);
|
|
2755
|
+
const absSrc = path4.join(templatesDir, f.src);
|
|
2756
|
+
if (opts.dryRun) {
|
|
2757
|
+
if (opts.verbose) {
|
|
2758
|
+
console.log(`[dry-run] would copy ${f.src} \u2192 .claude/${f.dest}`);
|
|
2759
|
+
}
|
|
2760
|
+
} else {
|
|
2761
|
+
fs3.mkdirSync(path4.dirname(absDest), { recursive: true });
|
|
2762
|
+
fs3.copyFileSync(absSrc, absDest);
|
|
2763
|
+
if (opts.verbose) {
|
|
2764
|
+
console.log(`copied ${f.src} \u2192 .claude/${f.dest}`);
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
manifestEntries.push({ src: f.src, dest: f.dest, hash: f.hash });
|
|
2768
|
+
}
|
|
2769
|
+
const hooksJsonPath = path4.join(templatesDir, "hooks", "hooks.json");
|
|
2770
|
+
const baseSettingsPath = path4.join(
|
|
2771
|
+
templatesDir,
|
|
2772
|
+
"settings.project.base.json"
|
|
2773
|
+
);
|
|
2774
|
+
const hasHooks = fs3.existsSync(hooksJsonPath);
|
|
2775
|
+
const hasBase = fs3.existsSync(baseSettingsPath);
|
|
2776
|
+
const settingsPath = path4.join(projectDir, ".claude", "settings.json");
|
|
2777
|
+
const existingSettings = fs3.existsSync(settingsPath) ? JSON.parse(fs3.readFileSync(settingsPath, "utf8")) : {};
|
|
2778
|
+
if (hasBase) {
|
|
2779
|
+
const base = JSON.parse(
|
|
2780
|
+
fs3.readFileSync(baseSettingsPath, "utf8")
|
|
2781
|
+
);
|
|
2782
|
+
mergeBaseSettingsIntoSettings(existingSettings, base);
|
|
2783
|
+
}
|
|
2784
|
+
if (hasHooks) {
|
|
2785
|
+
const hooksJson = JSON.parse(
|
|
2786
|
+
fs3.readFileSync(hooksJsonPath, "utf8")
|
|
2787
|
+
);
|
|
2788
|
+
mergeHooksIntoSettings(existingSettings, hooksJson);
|
|
2789
|
+
}
|
|
2790
|
+
if (!opts.dryRun) {
|
|
2791
|
+
fs3.mkdirSync(path4.dirname(settingsPath), { recursive: true });
|
|
2792
|
+
fs3.writeFileSync(
|
|
2793
|
+
settingsPath,
|
|
2794
|
+
JSON.stringify(existingSettings, null, 2) + "\n",
|
|
2795
|
+
"utf8"
|
|
2796
|
+
);
|
|
2797
|
+
} else if (opts.verbose) {
|
|
2798
|
+
console.log(
|
|
2799
|
+
`[dry-run] would merge settings into ${path4.relative(projectDir, settingsPath)}`
|
|
2800
|
+
);
|
|
2801
|
+
}
|
|
2802
|
+
const gitignoreAction = await ensureManagedGitignoreBlock(
|
|
2803
|
+
projectDir,
|
|
2804
|
+
opts.dryRun
|
|
2805
|
+
);
|
|
2806
|
+
if (opts.verbose && gitignoreAction !== "unchanged") {
|
|
2807
|
+
console.log(
|
|
2808
|
+
`${opts.dryRun ? "[dry-run] would " : ""}${gitignoreAction} managed .gitignore block in ${path4.relative(projectDir, path4.join(projectDir, ".gitignore"))}`
|
|
2809
|
+
);
|
|
2810
|
+
}
|
|
2811
|
+
if (!opts.dryRun) {
|
|
2812
|
+
const manifest = defaultManifest();
|
|
2813
|
+
manifest.files = manifestEntries;
|
|
2814
|
+
writeManifest(projectDir, manifest);
|
|
2815
|
+
}
|
|
2816
|
+
console.log(
|
|
2817
|
+
`codebyplan claude install${opts.dryRun ? " (dry-run)" : ""}: ${manifestEntries.length} files, ${countHookEntries(templatesDir)} hook entries.`
|
|
2818
|
+
);
|
|
2819
|
+
if (opts.renderer && !opts.dryRun) {
|
|
2820
|
+
await writeStatuslineLocalConfig(projectDir, { renderer: opts.renderer });
|
|
2821
|
+
}
|
|
2822
|
+
try {
|
|
2823
|
+
const { runLspFull: runLspFull2 } = await Promise.resolve().then(() => (init_lsp(), lsp_exports));
|
|
2824
|
+
await runLspFull2(projectDir, { dryRun: opts.dryRun });
|
|
2825
|
+
} catch {
|
|
2826
|
+
}
|
|
2827
|
+
} catch (err) {
|
|
2828
|
+
console.error(
|
|
2829
|
+
`codebyplan claude install failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2830
|
+
);
|
|
2831
|
+
process.exitCode = 1;
|
|
2396
2832
|
}
|
|
2397
2833
|
}
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2834
|
+
function runInstallUser(opts, deps) {
|
|
2835
|
+
let templatesDir;
|
|
2836
|
+
try {
|
|
2837
|
+
templatesDir = deps.templatesDir ?? resolveTemplatesDir();
|
|
2838
|
+
} catch (err) {
|
|
2839
|
+
console.error(
|
|
2840
|
+
err instanceof Error ? err.message : `codebyplan claude install: ${String(err)}`
|
|
2841
|
+
);
|
|
2842
|
+
process.exitCode = 1;
|
|
2843
|
+
return;
|
|
2844
|
+
}
|
|
2845
|
+
try {
|
|
2846
|
+
const userDir = deps.userDir ?? path4.join(os2.homedir(), ".claude");
|
|
2847
|
+
const settingsPath = path4.join(userDir, "settings.json");
|
|
2848
|
+
const userBaseSettingsPath = path4.join(
|
|
2849
|
+
templatesDir,
|
|
2850
|
+
"settings.user.base.json"
|
|
2851
|
+
);
|
|
2852
|
+
if (!fs3.existsSync(userBaseSettingsPath)) {
|
|
2853
|
+
console.error(
|
|
2854
|
+
"codebyplan claude install: settings.user.base.json not found in templates."
|
|
2855
|
+
);
|
|
2403
2856
|
process.exitCode = 1;
|
|
2404
2857
|
return;
|
|
2405
2858
|
}
|
|
2406
|
-
const
|
|
2859
|
+
const userBase = JSON.parse(
|
|
2860
|
+
fs3.readFileSync(userBaseSettingsPath, "utf8")
|
|
2861
|
+
);
|
|
2862
|
+
const existingSettings = fs3.existsSync(settingsPath) ? JSON.parse(fs3.readFileSync(settingsPath, "utf8")) : {};
|
|
2863
|
+
mergeBaseSettingsIntoSettings(existingSettings, userBase);
|
|
2864
|
+
if (!opts.dryRun) {
|
|
2865
|
+
fs3.mkdirSync(userDir, { recursive: true });
|
|
2866
|
+
fs3.writeFileSync(
|
|
2867
|
+
settingsPath,
|
|
2868
|
+
JSON.stringify(existingSettings, null, 2) + "\n",
|
|
2869
|
+
"utf8"
|
|
2870
|
+
);
|
|
2871
|
+
const manifest = defaultManifest();
|
|
2872
|
+
manifest.files = [];
|
|
2873
|
+
writeManifestForScope("user", manifest, userDir);
|
|
2874
|
+
} else if (opts.verbose) {
|
|
2875
|
+
console.log(
|
|
2876
|
+
`[dry-run] would merge user base settings into ${settingsPath}`
|
|
2877
|
+
);
|
|
2878
|
+
}
|
|
2407
2879
|
console.log(
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
}
|
|
2880
|
+
`codebyplan claude install --scope user${opts.dryRun ? " (dry-run)" : ""}: settings.json updated, 0 template files copied.`
|
|
2881
|
+
);
|
|
2882
|
+
} catch (err) {
|
|
2883
|
+
console.error(
|
|
2884
|
+
`codebyplan claude install failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2413
2885
|
);
|
|
2414
|
-
return;
|
|
2415
|
-
}
|
|
2416
|
-
if (!tokens) {
|
|
2417
|
-
console.log("\n Not logged in. Run `codebyplan login` to authenticate.\n");
|
|
2418
2886
|
process.exitCode = 1;
|
|
2419
|
-
return;
|
|
2420
2887
|
}
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
const
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
}
|
|
2433
|
-
console.
|
|
2888
|
+
}
|
|
2889
|
+
function countHookEntries(templatesDir) {
|
|
2890
|
+
const p = path4.join(templatesDir, "hooks", "hooks.json");
|
|
2891
|
+
if (!fs3.existsSync(p)) return 0;
|
|
2892
|
+
try {
|
|
2893
|
+
const j = JSON.parse(fs3.readFileSync(p, "utf8"));
|
|
2894
|
+
let n = 0;
|
|
2895
|
+
for (const blocks of Object.values(j.hooks)) {
|
|
2896
|
+
for (const b of blocks) n += b.hooks.length;
|
|
2897
|
+
}
|
|
2898
|
+
return n;
|
|
2899
|
+
} catch (err) {
|
|
2900
|
+
console.error(
|
|
2901
|
+
`codebyplan: could not count hook entries in hooks.json: ${err instanceof Error ? err.message : String(err)}`
|
|
2902
|
+
);
|
|
2903
|
+
return 0;
|
|
2434
2904
|
}
|
|
2435
2905
|
}
|
|
2436
|
-
var
|
|
2437
|
-
"src/cli/
|
|
2906
|
+
var init_install = __esm({
|
|
2907
|
+
"src/cli/claude/install.ts"() {
|
|
2438
2908
|
"use strict";
|
|
2439
|
-
|
|
2440
|
-
|
|
2909
|
+
init_template_walker();
|
|
2910
|
+
init_gitignore_block();
|
|
2911
|
+
init_manifest();
|
|
2912
|
+
init_settings_merge();
|
|
2913
|
+
init_statusline_config();
|
|
2441
2914
|
}
|
|
2442
2915
|
});
|
|
2443
2916
|
|
|
2444
|
-
// src/cli/
|
|
2445
|
-
var
|
|
2446
|
-
__export(
|
|
2447
|
-
|
|
2917
|
+
// src/cli/setup.ts
|
|
2918
|
+
var setup_exports = {};
|
|
2919
|
+
__export(setup_exports, {
|
|
2920
|
+
runSetup: () => runSetup
|
|
2448
2921
|
});
|
|
2449
|
-
import { readFile as
|
|
2450
|
-
import { homedir as
|
|
2451
|
-
import { join as
|
|
2452
|
-
|
|
2453
|
-
|
|
2922
|
+
import { mkdir as mkdir5, readFile as readFile9, writeFile as writeFile7 } from "node:fs/promises";
|
|
2923
|
+
import { homedir as homedir4 } from "node:os";
|
|
2924
|
+
import { join as join12 } from "node:path";
|
|
2925
|
+
import { stdin, stdout as stdout2 } from "node:process";
|
|
2926
|
+
import { createInterface } from "node:readline/promises";
|
|
2927
|
+
function getConfigPath(scope) {
|
|
2928
|
+
return scope === "user" ? join12(homedir4(), ".claude.json") : join12(process.cwd(), ".mcp.json");
|
|
2454
2929
|
}
|
|
2455
|
-
async function
|
|
2930
|
+
async function readConfig(path10) {
|
|
2456
2931
|
try {
|
|
2457
|
-
const raw = await
|
|
2932
|
+
const raw = await readFile9(path10, "utf-8");
|
|
2458
2933
|
const parsed = JSON.parse(raw);
|
|
2459
2934
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
2460
2935
|
return parsed;
|
|
2461
2936
|
}
|
|
2462
|
-
return
|
|
2937
|
+
return {};
|
|
2463
2938
|
} catch {
|
|
2464
|
-
return
|
|
2939
|
+
return {};
|
|
2465
2940
|
}
|
|
2466
2941
|
}
|
|
2467
|
-
function
|
|
2468
|
-
|
|
2469
|
-
return "x-api-key" in entry.headers;
|
|
2942
|
+
function buildMcpEntry() {
|
|
2943
|
+
return { type: "http", url: mcpEndpoint() };
|
|
2470
2944
|
}
|
|
2471
|
-
async function
|
|
2472
|
-
const
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
return true;
|
|
2945
|
+
async function writeMcpConfig(scope) {
|
|
2946
|
+
const configPath = getConfigPath(scope);
|
|
2947
|
+
const config = await readConfig(configPath);
|
|
2948
|
+
if (typeof config.mcpServers !== "object" || config.mcpServers === null || Array.isArray(config.mcpServers)) {
|
|
2949
|
+
config.mcpServers = {};
|
|
2950
|
+
}
|
|
2951
|
+
config.mcpServers.codebyplan = buildMcpEntry();
|
|
2952
|
+
await writeFile7(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
2953
|
+
return configPath;
|
|
2481
2954
|
}
|
|
2482
|
-
async function
|
|
2483
|
-
|
|
2484
|
-
await
|
|
2485
|
-
const
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2955
|
+
async function fetchRepos(auth) {
|
|
2956
|
+
const baseUrl3 = (process.env.CODEBYPLAN_API_URL ?? "https://www.codebyplan.com").replace(/\/$/, "");
|
|
2957
|
+
const headers = auth.kind === "oauth" ? { Authorization: `Bearer ${await getAccessToken()}` } : { "x-api-key": auth.apiKey };
|
|
2958
|
+
const res = await fetch(`${baseUrl3}/api/repos`, {
|
|
2959
|
+
headers,
|
|
2960
|
+
signal: AbortSignal.timeout(1e4)
|
|
2961
|
+
});
|
|
2962
|
+
if (res.status === 401) {
|
|
2963
|
+
if (auth.kind === "oauth") {
|
|
2964
|
+
throw new Error(
|
|
2965
|
+
"Session rejected. Run `codebyplan logout && codebyplan login`."
|
|
2966
|
+
);
|
|
2494
2967
|
}
|
|
2968
|
+
throw new Error("Invalid API key. Please check and try again.");
|
|
2495
2969
|
}
|
|
2496
|
-
if (
|
|
2497
|
-
console.log(
|
|
2498
|
-
" All codebyplan MCP entries are already up to date \u2014 nothing to change."
|
|
2499
|
-
);
|
|
2500
|
-
} else {
|
|
2970
|
+
if (!res.ok) {
|
|
2501
2971
|
console.log(
|
|
2502
|
-
`
|
|
2503
|
-
Done. Restart Claude Code to use the new endpoint: ${newUrl}
|
|
2972
|
+
` Warning: API returned status ${res.status}, but continuing.
|
|
2504
2973
|
`
|
|
2505
2974
|
);
|
|
2975
|
+
return [];
|
|
2506
2976
|
}
|
|
2977
|
+
const body = await res.json();
|
|
2978
|
+
return body.data ?? [];
|
|
2507
2979
|
}
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
return err instanceof Error && err.code === "ABORT_ERR";
|
|
2526
|
-
}
|
|
2527
|
-
async function confirmProceed(message) {
|
|
2528
|
-
const rl = createInterface2({ input: stdin2, output: stdout3 });
|
|
2529
|
-
try {
|
|
2530
|
-
while (true) {
|
|
2531
|
-
const answer = await rl.question(message ?? " Proceed? [Y/n] ");
|
|
2532
|
-
const a = answer.trim().toLowerCase();
|
|
2533
|
-
if (a === "" || a === "y" || a === "yes") return true;
|
|
2534
|
-
if (a === "n" || a === "no") return false;
|
|
2535
|
-
console.log(
|
|
2536
|
-
` Unknown option "${answer.trim()}". Valid: y/yes, n/no, or Enter for yes.`
|
|
2537
|
-
);
|
|
2980
|
+
async function chooseAuthMode(rl) {
|
|
2981
|
+
console.log(" How would you like to authenticate?\n");
|
|
2982
|
+
console.log(" 1. OAuth \u2014 open browser to sign in (recommended)");
|
|
2983
|
+
console.log(
|
|
2984
|
+
" 2. Legacy API key \u2014 for the 30-day shim (deprecated 2026-06-30)\n"
|
|
2985
|
+
);
|
|
2986
|
+
const choice = (await rl.question(" Select (1/2, default: 1): ")).trim();
|
|
2987
|
+
if (choice === "" || choice === "1") {
|
|
2988
|
+
try {
|
|
2989
|
+
await runLogin();
|
|
2990
|
+
return { kind: "oauth" };
|
|
2991
|
+
} catch (err) {
|
|
2992
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2993
|
+
console.log(`
|
|
2994
|
+
OAuth login failed: ${msg}
|
|
2995
|
+
`);
|
|
2996
|
+
return null;
|
|
2538
2997
|
}
|
|
2539
|
-
} catch (err) {
|
|
2540
|
-
if (isAbortError(err)) throw new SyncCancelledError();
|
|
2541
|
-
throw err;
|
|
2542
|
-
} finally {
|
|
2543
|
-
rl.close();
|
|
2544
2998
|
}
|
|
2999
|
+
const apiKey = (await rl.question(" Enter your API key: ")).trim();
|
|
3000
|
+
if (!apiKey) {
|
|
3001
|
+
console.log("\n No API key provided. Aborting setup.\n");
|
|
3002
|
+
return null;
|
|
3003
|
+
}
|
|
3004
|
+
return { kind: "legacy", apiKey };
|
|
2545
3005
|
}
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
};
|
|
3006
|
+
async function writeCodebyplanDirectory(projectPath, selectedRepo, deviceId) {
|
|
3007
|
+
const codebyplanDir = join12(projectPath, ".codebyplan");
|
|
3008
|
+
await mkdir5(codebyplanDir, { recursive: true });
|
|
3009
|
+
const repoJson = {
|
|
3010
|
+
repo_id: selectedRepo.id
|
|
3011
|
+
};
|
|
3012
|
+
const repoAny = selectedRepo;
|
|
3013
|
+
if (typeof repoAny.organization_id === "string") {
|
|
3014
|
+
repoJson.organization_id = repoAny.organization_id;
|
|
2556
3015
|
}
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
3016
|
+
if (typeof repoAny.project_id === "string") {
|
|
3017
|
+
repoJson.project_id = repoAny.project_id;
|
|
3018
|
+
}
|
|
3019
|
+
await writeFile7(
|
|
3020
|
+
join12(codebyplanDir, "repo.json"),
|
|
3021
|
+
JSON.stringify(repoJson, null, 2) + "\n",
|
|
3022
|
+
"utf-8"
|
|
3023
|
+
);
|
|
3024
|
+
await writeFile7(
|
|
3025
|
+
join12(codebyplanDir, "server.json"),
|
|
3026
|
+
JSON.stringify(
|
|
3027
|
+
{
|
|
3028
|
+
server_port: null,
|
|
3029
|
+
server_type: null,
|
|
3030
|
+
auto_push_enabled: false,
|
|
3031
|
+
port_allocations: []
|
|
3032
|
+
},
|
|
3033
|
+
null,
|
|
3034
|
+
2
|
|
3035
|
+
) + "\n",
|
|
3036
|
+
"utf-8"
|
|
3037
|
+
);
|
|
3038
|
+
await writeFile7(
|
|
3039
|
+
join12(codebyplanDir, "git.json"),
|
|
3040
|
+
JSON.stringify({ git_branch: null, branch_config: null }, null, 2) + "\n",
|
|
3041
|
+
"utf-8"
|
|
3042
|
+
);
|
|
3043
|
+
await writeFile7(
|
|
3044
|
+
join12(codebyplanDir, "shipment.json"),
|
|
3045
|
+
JSON.stringify({ shipment: null }, null, 2) + "\n",
|
|
3046
|
+
"utf-8"
|
|
3047
|
+
);
|
|
3048
|
+
await writeFile7(
|
|
3049
|
+
join12(codebyplanDir, "vendor.json"),
|
|
3050
|
+
JSON.stringify({}, null, 2) + "\n",
|
|
3051
|
+
"utf-8"
|
|
3052
|
+
);
|
|
3053
|
+
await writeFile7(
|
|
3054
|
+
join12(codebyplanDir, "e2e.json"),
|
|
3055
|
+
JSON.stringify({}, null, 2) + "\n",
|
|
3056
|
+
"utf-8"
|
|
3057
|
+
);
|
|
3058
|
+
await writeFile7(
|
|
3059
|
+
join12(codebyplanDir, "eslint.json"),
|
|
3060
|
+
JSON.stringify({}, null, 2) + "\n",
|
|
3061
|
+
"utf-8"
|
|
3062
|
+
);
|
|
3063
|
+
const statuslinePath = join12(codebyplanDir, "statusline.json");
|
|
3064
|
+
let statuslineExists = false;
|
|
2563
3065
|
try {
|
|
2564
|
-
await
|
|
2565
|
-
|
|
3066
|
+
await readFile9(statuslinePath, "utf-8");
|
|
3067
|
+
statuslineExists = true;
|
|
2566
3068
|
} catch {
|
|
2567
|
-
return false;
|
|
2568
3069
|
}
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
try {
|
|
2574
|
-
const raw = await readFile9(
|
|
2575
|
-
join12(projectPath, "pnpm-workspace.yaml"),
|
|
3070
|
+
if (!statuslineExists) {
|
|
3071
|
+
await writeFile7(
|
|
3072
|
+
statuslinePath,
|
|
3073
|
+
JSON.stringify(STATUSLINE_DEFAULTS, null, 2) + "\n",
|
|
2576
3074
|
"utf-8"
|
|
2577
3075
|
);
|
|
2578
|
-
const matches = raw.match(/^\s*-\s*['"]?([^'"#\n]+)['"]?/gm);
|
|
2579
|
-
if (matches) {
|
|
2580
|
-
for (const m of matches) {
|
|
2581
|
-
const pattern = m.replace(/^\s*-\s*['"]?/, "").replace(/['"]?\s*$/, "").trim();
|
|
2582
|
-
if (pattern) patterns.push(pattern);
|
|
2583
|
-
}
|
|
2584
|
-
}
|
|
2585
|
-
} catch {
|
|
2586
|
-
}
|
|
2587
|
-
if (patterns.length === 0) {
|
|
2588
|
-
try {
|
|
2589
|
-
const raw = await readFile9(join12(projectPath, "package.json"), "utf-8");
|
|
2590
|
-
const pkg = JSON.parse(raw);
|
|
2591
|
-
const ws = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces?.packages;
|
|
2592
|
-
if (ws) patterns.push(...ws);
|
|
2593
|
-
} catch {
|
|
2594
|
-
}
|
|
2595
3076
|
}
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
const absPath = join12(absDir, entry.name);
|
|
2606
|
-
if (await fileExists(join12(absPath, "package.json"))) {
|
|
2607
|
-
apps.push({ name: entry.name, path: relPath, absPath });
|
|
2608
|
-
}
|
|
2609
|
-
}
|
|
2610
|
-
}
|
|
2611
|
-
} catch {
|
|
2612
|
-
}
|
|
2613
|
-
}
|
|
3077
|
+
await writeLocalConfig(projectPath, { device_id: deviceId });
|
|
3078
|
+
console.log(` Created ${codebyplanDir}/`);
|
|
3079
|
+
console.log(
|
|
3080
|
+
` repo.json, server.json, git.json, shipment.json, vendor.json, e2e.json, eslint.json, statusline.json`
|
|
3081
|
+
);
|
|
3082
|
+
console.log(` device.local.json (gitignored)`);
|
|
3083
|
+
const gitignoreAction = await ensureManagedGitignoreBlock(projectPath);
|
|
3084
|
+
if (gitignoreAction !== "unchanged") {
|
|
3085
|
+
console.log(" Updated .gitignore (codebyplan managed block)");
|
|
2614
3086
|
}
|
|
2615
|
-
return apps;
|
|
2616
3087
|
}
|
|
2617
|
-
async function
|
|
2618
|
-
|
|
3088
|
+
async function runSetup() {
|
|
3089
|
+
const rl = createInterface({ input: stdin, output: stdout2 });
|
|
3090
|
+
console.log("\n CodeByPlan Setup\n");
|
|
3091
|
+
console.log(" This will configure Claude Code to use CodeByPlan.\n");
|
|
2619
3092
|
try {
|
|
2620
|
-
const
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
3093
|
+
const auth = await chooseAuthMode(rl);
|
|
3094
|
+
if (!auth) return;
|
|
3095
|
+
console.log("\n Where should the MCP server be configured?\n");
|
|
3096
|
+
console.log(" 1. Global \u2014 available in all projects (~/.claude.json)");
|
|
3097
|
+
console.log(" 2. Project \u2014 only this project (.mcp.json)\n");
|
|
3098
|
+
const scopeInput = (await rl.question(" Select (1/2, default: 1): ")).trim();
|
|
3099
|
+
const scope = scopeInput === "2" ? "project" : "user";
|
|
3100
|
+
console.log("\n Configuring MCP server...");
|
|
3101
|
+
const configPath = await writeMcpConfig(scope);
|
|
3102
|
+
console.log(` Done! Config written to ${configPath}
|
|
3103
|
+
`);
|
|
3104
|
+
let repos = [];
|
|
3105
|
+
try {
|
|
3106
|
+
repos = await fetchRepos(auth);
|
|
3107
|
+
} catch (err) {
|
|
3108
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3109
|
+
console.log(` ${msg}
|
|
3110
|
+
`);
|
|
3111
|
+
return;
|
|
2630
3112
|
}
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
3113
|
+
if (repos.length === 0) {
|
|
3114
|
+
console.log(
|
|
3115
|
+
" No repositories found. Create one at https://codebyplan.com after setup.\n"
|
|
3116
|
+
);
|
|
3117
|
+
console.log(
|
|
3118
|
+
" Setup complete! Start a new Claude Code session to begin.\n"
|
|
3119
|
+
);
|
|
3120
|
+
return;
|
|
2634
3121
|
}
|
|
2635
|
-
console.
|
|
2636
|
-
|
|
2637
|
-
)
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
const caps = /* @__PURE__ */ new Set();
|
|
2643
|
-
for (const sub of JSX_SCAN_DIRS) {
|
|
2644
|
-
if (await hasJsxFile(join12(dirPath, sub))) {
|
|
2645
|
-
caps.add("jsx");
|
|
2646
|
-
break;
|
|
3122
|
+
console.log(" Initialize this project?\n");
|
|
3123
|
+
const initAnswer = (await rl.question(" Link to a repository? (Y/n): ")).trim().toLowerCase();
|
|
3124
|
+
if (initAnswer !== "" && initAnswer !== "y" && initAnswer !== "yes") {
|
|
3125
|
+
console.log(
|
|
3126
|
+
" Setup complete! Start a new Claude Code session to begin.\n"
|
|
3127
|
+
);
|
|
3128
|
+
return;
|
|
2647
3129
|
}
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
const allDeps = {
|
|
2651
|
-
...pkgJson.dependencies ?? {},
|
|
2652
|
-
...pkgJson.devDependencies ?? {}
|
|
2653
|
-
};
|
|
2654
|
-
for (const dep of Object.keys(allDeps)) {
|
|
2655
|
-
if (SERVER_FRAMEWORK_DEPS.has(dep)) {
|
|
2656
|
-
caps.add("node-server");
|
|
2657
|
-
break;
|
|
2658
|
-
}
|
|
3130
|
+
if (auth.kind === "legacy") {
|
|
3131
|
+
process.env.CODEBYPLAN_API_KEY = auth.apiKey;
|
|
2659
3132
|
}
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
}
|
|
3133
|
+
console.log("\n Your repositories:\n");
|
|
3134
|
+
for (let i = 0; i < repos.length; i++) {
|
|
3135
|
+
console.log(` ${i + 1}. ${repos[i].name}`);
|
|
2664
3136
|
}
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
caps.add("cli-bin");
|
|
2672
|
-
} else if (typeof pkgJson.bin === "object" && pkgJson.bin !== null && Object.keys(pkgJson.bin).length > 0) {
|
|
2673
|
-
caps.add("cli-bin");
|
|
3137
|
+
console.log();
|
|
3138
|
+
const choice = (await rl.question(" Select a repository (number): ")).trim();
|
|
3139
|
+
const index = parseInt(choice, 10) - 1;
|
|
3140
|
+
if (isNaN(index) || index < 0 || index >= repos.length) {
|
|
3141
|
+
console.log(" Invalid selection. Skipping project init.\n");
|
|
3142
|
+
return;
|
|
2674
3143
|
}
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
}
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
}
|
|
2695
|
-
continue;
|
|
2696
|
-
}
|
|
2697
|
-
for (const { prefix, rule: prefixRule } of PACKAGE_PREFIX_MAP) {
|
|
2698
|
-
if (depName.startsWith(prefix)) {
|
|
2699
|
-
const key = prefixRule.name.toLowerCase();
|
|
2700
|
-
if (!seen.has(key)) {
|
|
2701
|
-
seen.set(key, {
|
|
2702
|
-
name: prefixRule.name,
|
|
2703
|
-
category: prefixRule.category
|
|
2704
|
-
});
|
|
2705
|
-
}
|
|
2706
|
-
break;
|
|
2707
|
-
}
|
|
2708
|
-
}
|
|
3144
|
+
const selectedRepo = repos[index];
|
|
3145
|
+
console.log(`
|
|
3146
|
+
Selected: ${selectedRepo.name}
|
|
3147
|
+
`);
|
|
3148
|
+
const projectPath = process.cwd();
|
|
3149
|
+
const pathBasedId = await resolveAndCacheWorktreeId(
|
|
3150
|
+
selectedRepo.id,
|
|
3151
|
+
projectPath,
|
|
3152
|
+
{ skipWrite: true }
|
|
3153
|
+
);
|
|
3154
|
+
const deviceId = await getOrCreateDeviceId(projectPath);
|
|
3155
|
+
let branch = "main";
|
|
3156
|
+
try {
|
|
3157
|
+
const { execSync: execSync9 } = await import("node:child_process");
|
|
3158
|
+
branch = execSync9("git symbolic-ref --short HEAD", {
|
|
3159
|
+
cwd: projectPath,
|
|
3160
|
+
encoding: "utf-8"
|
|
3161
|
+
}).trim();
|
|
3162
|
+
} catch {
|
|
2709
3163
|
}
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
3164
|
+
const tupleId = await resolveWorktreeId({
|
|
3165
|
+
repoId: selectedRepo.id,
|
|
3166
|
+
repoPath: projectPath,
|
|
3167
|
+
branch,
|
|
3168
|
+
deviceId
|
|
3169
|
+
});
|
|
3170
|
+
const _worktreeId = tupleId ?? pathBasedId;
|
|
3171
|
+
void _worktreeId;
|
|
3172
|
+
await writeCodebyplanDirectory(projectPath, selectedRepo, deviceId);
|
|
3173
|
+
const priorExitCode = process.exitCode;
|
|
3174
|
+
let installFailed = false;
|
|
3175
|
+
try {
|
|
3176
|
+
const { runInstall: runInstall2 } = await Promise.resolve().then(() => (init_install(), install_exports));
|
|
3177
|
+
await runInstall2(
|
|
3178
|
+
{ yes: true, dryRun: false, verbose: false, scope: "project" },
|
|
3179
|
+
{ projectDir: projectPath }
|
|
3180
|
+
);
|
|
3181
|
+
installFailed = process.exitCode !== priorExitCode;
|
|
3182
|
+
} catch {
|
|
3183
|
+
installFailed = true;
|
|
2716
3184
|
}
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
return {
|
|
2723
|
-
...entry,
|
|
2724
|
-
capabilities: isBearer ? capsArray : []
|
|
2725
|
-
};
|
|
2726
|
-
});
|
|
2727
|
-
if (capsArray.length > 0) {
|
|
2728
|
-
const hasBearerWithCaps = entries.some(
|
|
2729
|
-
(e) => CAPABILITY_BEARER_NAMES.has(e.name.toLowerCase()) && (e.capabilities?.some((c) => c.length > 0) ?? false)
|
|
2730
|
-
);
|
|
2731
|
-
if (!hasBearerWithCaps) {
|
|
2732
|
-
entries.push({
|
|
2733
|
-
name: SYNTHETIC_CARRIER_NAME,
|
|
2734
|
-
category: "tool",
|
|
2735
|
-
capabilities: capsArray
|
|
2736
|
-
});
|
|
3185
|
+
if (installFailed) {
|
|
3186
|
+
process.exitCode = priorExitCode;
|
|
3187
|
+
console.log(
|
|
3188
|
+
" Warning: .claude/ install step failed. Run `codebyplan claude install` manually."
|
|
3189
|
+
);
|
|
2737
3190
|
}
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
3191
|
+
const existingRenderer = await readStatuslineLocalConfig(projectPath);
|
|
3192
|
+
const isInteractive = process.stdin.isTTY === true;
|
|
3193
|
+
if (!existingRenderer && isInteractive) {
|
|
3194
|
+
const availability = detectAvailableRenderers();
|
|
3195
|
+
const available = Object.entries(availability).filter(([, v]) => v).map(([k]) => k).join(", ");
|
|
3196
|
+
console.log(
|
|
3197
|
+
`
|
|
3198
|
+
Which statusline renderer would you like to use? (available: ${available})`
|
|
3199
|
+
);
|
|
3200
|
+
console.log(" bash \u2014 shell script, zero dependencies (default)");
|
|
3201
|
+
console.log(" node \u2014 Node.js renderer");
|
|
3202
|
+
console.log(" python \u2014 Python 3 renderer");
|
|
3203
|
+
const rendererInput = (await rl.question(
|
|
3204
|
+
" Select renderer (bash/node/python, default: bash): "
|
|
3205
|
+
)).trim().toLowerCase();
|
|
3206
|
+
const chosen = rendererInput === "node" ? "node" : rendererInput === "python" ? "python" : "bash";
|
|
3207
|
+
await writeStatuslineLocalConfig(projectPath, { renderer: chosen });
|
|
3208
|
+
if (!availability[chosen]) {
|
|
3209
|
+
console.log(
|
|
3210
|
+
` Warning: '${chosen}' was not detected on this machine. You can change it later with \`codebyplan statusline\`.`
|
|
3211
|
+
);
|
|
3212
|
+
} else {
|
|
3213
|
+
console.log(` Renderer set to '${chosen}'.`);
|
|
2748
3214
|
}
|
|
2749
3215
|
}
|
|
3216
|
+
console.log(
|
|
3217
|
+
"\n Run `npx codebyplan config` to sync repo config from the DB."
|
|
3218
|
+
);
|
|
3219
|
+
console.log(
|
|
3220
|
+
" Setup complete! Start a new Claude Code session to begin.\n"
|
|
3221
|
+
);
|
|
3222
|
+
} finally {
|
|
3223
|
+
rl.close();
|
|
2750
3224
|
}
|
|
2751
|
-
return Array.from(set).sort();
|
|
2752
3225
|
}
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
const flatMap = /* @__PURE__ */ new Map();
|
|
2764
|
-
for (const entry of repo) {
|
|
2765
|
-
flatMap.set(entry.name.toLowerCase(), entry);
|
|
3226
|
+
var init_setup = __esm({
|
|
3227
|
+
"src/cli/setup.ts"() {
|
|
3228
|
+
"use strict";
|
|
3229
|
+
init_gitignore_block();
|
|
3230
|
+
init_local_config();
|
|
3231
|
+
init_statusline_config();
|
|
3232
|
+
init_resolve_worktree();
|
|
3233
|
+
init_token_refresh();
|
|
3234
|
+
init_urls();
|
|
3235
|
+
init_login();
|
|
2766
3236
|
}
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
}
|
|
3237
|
+
});
|
|
3238
|
+
|
|
3239
|
+
// src/cli/statusline.ts
|
|
3240
|
+
var statusline_exports = {};
|
|
3241
|
+
__export(statusline_exports, {
|
|
3242
|
+
runStatusline: () => runStatusline
|
|
3243
|
+
});
|
|
3244
|
+
import { dirname as dirname5 } from "node:path";
|
|
3245
|
+
async function resolveProjectPath() {
|
|
3246
|
+
const found = await findCodebyplanConfig(process.cwd());
|
|
3247
|
+
if (found) {
|
|
3248
|
+
return dirname5(dirname5(found.path));
|
|
2780
3249
|
}
|
|
2781
|
-
|
|
2782
|
-
compareByCategoryThenName
|
|
2783
|
-
);
|
|
2784
|
-
const appsCleaned = apps.map((a) => ({
|
|
2785
|
-
...a,
|
|
2786
|
-
stack: stripSyntheticIfCovered(a.stack).sort(compareByCategoryThenName)
|
|
2787
|
-
}));
|
|
2788
|
-
const flat = stripSyntheticIfCovered(Array.from(flatMap.values())).sort(
|
|
2789
|
-
compareByCategoryThenName
|
|
2790
|
-
);
|
|
2791
|
-
return { repo: repoCleaned, apps: appsCleaned, flat };
|
|
3250
|
+
return process.cwd();
|
|
2792
3251
|
}
|
|
2793
|
-
function
|
|
2794
|
-
const
|
|
2795
|
-
if (
|
|
2796
|
-
|
|
2797
|
-
for (const e of entries) {
|
|
2798
|
-
if (e.name === SYNTHETIC_CARRIER_NAME) continue;
|
|
2799
|
-
for (const c of e.capabilities ?? []) realBearerCaps.add(c);
|
|
2800
|
-
}
|
|
2801
|
-
if (synth.capabilities.every((c) => realBearerCaps.has(c))) {
|
|
2802
|
-
return entries.filter((e) => e.name !== SYNTHETIC_CARRIER_NAME);
|
|
3252
|
+
function parseRendererArg(arg) {
|
|
3253
|
+
const normalised = arg.startsWith("--") ? arg.slice(2) : arg;
|
|
3254
|
+
if (VALID_RENDERERS2.has(normalised)) {
|
|
3255
|
+
return normalised;
|
|
2803
3256
|
}
|
|
2804
|
-
return
|
|
3257
|
+
return null;
|
|
2805
3258
|
}
|
|
2806
|
-
function
|
|
2807
|
-
const
|
|
2808
|
-
const
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
apps: [],
|
|
2819
|
-
flat: remote.filter(stripCarrier)
|
|
2820
|
-
} : {
|
|
2821
|
-
repo: remote.repo.filter(stripCarrier),
|
|
2822
|
-
apps: remote.apps.map((a) => ({
|
|
2823
|
-
...a,
|
|
2824
|
-
stack: a.stack.filter(stripCarrier)
|
|
2825
|
-
})),
|
|
2826
|
-
flat: remote.flat.filter(stripCarrier)
|
|
2827
|
-
};
|
|
2828
|
-
const seen = /* @__PURE__ */ new Map();
|
|
2829
|
-
for (const entry of remoteResult.flat) {
|
|
2830
|
-
seen.set(entry.name.toLowerCase(), entry);
|
|
3259
|
+
async function runStatusline(args) {
|
|
3260
|
+
const positional = args.filter((a) => !a.startsWith("-"));
|
|
3261
|
+
const flagArgs = args.filter((a) => a.startsWith("--"));
|
|
3262
|
+
const rendererTokens = [...positional, ...flagArgs].filter(
|
|
3263
|
+
(a) => parseRendererArg(a) !== null
|
|
3264
|
+
);
|
|
3265
|
+
if (rendererTokens.length > 1) {
|
|
3266
|
+
process.stderr.write(
|
|
3267
|
+
"codebyplan statusline: bash, node, and python are mutually exclusive; pass only one.\n"
|
|
3268
|
+
);
|
|
3269
|
+
process.exitCode = 1;
|
|
3270
|
+
return;
|
|
2831
3271
|
}
|
|
2832
|
-
|
|
2833
|
-
for (const
|
|
2834
|
-
const
|
|
2835
|
-
if (
|
|
2836
|
-
|
|
2837
|
-
|
|
3272
|
+
let rendererArg;
|
|
3273
|
+
for (const a of [...positional, ...flagArgs]) {
|
|
3274
|
+
const parsed = parseRendererArg(a);
|
|
3275
|
+
if (parsed !== null) {
|
|
3276
|
+
rendererArg = a;
|
|
3277
|
+
break;
|
|
2838
3278
|
}
|
|
2839
3279
|
}
|
|
2840
|
-
const
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
3280
|
+
const unknownPositional = args.filter(
|
|
3281
|
+
(a) => !a.startsWith("-") && !VALID_RENDERERS2.has(a)
|
|
3282
|
+
);
|
|
3283
|
+
if (unknownPositional.length > 0) {
|
|
3284
|
+
process.stderr.write(
|
|
3285
|
+
`codebyplan statusline: unknown argument '${unknownPositional[0]}'.
|
|
3286
|
+
|
|
3287
|
+
Usage:
|
|
3288
|
+
codebyplan statusline Show current renderer and line toggles
|
|
3289
|
+
codebyplan statusline bash|node|python Set the renderer
|
|
3290
|
+
codebyplan statusline --bash|--node|--python Set the renderer (flag form)
|
|
3291
|
+
`
|
|
3292
|
+
);
|
|
3293
|
+
process.exitCode = 1;
|
|
3294
|
+
return;
|
|
3295
|
+
}
|
|
3296
|
+
const unknownFlags = flagArgs.filter((a) => parseRendererArg(a) === null);
|
|
3297
|
+
if (unknownFlags.length > 0) {
|
|
3298
|
+
process.stderr.write(
|
|
3299
|
+
`codebyplan statusline: unknown flag '${unknownFlags[0]}'.
|
|
3300
|
+
|
|
3301
|
+
Usage:
|
|
3302
|
+
codebyplan statusline Show current renderer and line toggles
|
|
3303
|
+
codebyplan statusline bash|node|python Set the renderer
|
|
3304
|
+
codebyplan statusline --bash|--node|--python Set the renderer (flag form)
|
|
3305
|
+
`
|
|
2852
3306
|
);
|
|
3307
|
+
process.exitCode = 1;
|
|
3308
|
+
return;
|
|
2853
3309
|
}
|
|
2854
|
-
|
|
2855
|
-
|
|
3310
|
+
const projectPath = await resolveProjectPath();
|
|
3311
|
+
if (rendererArg !== void 0) {
|
|
3312
|
+
const chosen = parseRendererArg(rendererArg);
|
|
3313
|
+
await writeStatuslineLocalConfig(projectPath, { renderer: chosen });
|
|
3314
|
+
const availability2 = detectAvailableRenderers();
|
|
3315
|
+
if (!availability2[chosen]) {
|
|
3316
|
+
process.stderr.write(
|
|
3317
|
+
`Warning: '${chosen}' was not detected on this machine. The renderer is saved but may not run.
|
|
3318
|
+
`
|
|
3319
|
+
);
|
|
3320
|
+
}
|
|
3321
|
+
console.log(`Statusline renderer set to '${chosen}'.`);
|
|
3322
|
+
return;
|
|
2856
3323
|
}
|
|
2857
|
-
|
|
3324
|
+
const availability = detectAvailableRenderers();
|
|
3325
|
+
const [renderer, config] = await Promise.all([
|
|
3326
|
+
resolveRenderer(projectPath),
|
|
3327
|
+
readStatuslineConfig(projectPath)
|
|
3328
|
+
]);
|
|
3329
|
+
const availStr = Object.entries(availability).map(([k, v]) => `${k}: ${v ? "yes" : "no"}`).join(", ");
|
|
3330
|
+
console.log(`
|
|
3331
|
+
Statusline configuration`);
|
|
3332
|
+
console.log(` Renderer: ${renderer}`);
|
|
3333
|
+
console.log(` Available: ${availStr}`);
|
|
3334
|
+
console.log(` no_color: ${config.no_color}`);
|
|
3335
|
+
console.log(`
|
|
3336
|
+
Line toggles:`);
|
|
3337
|
+
for (const [key, val] of Object.entries(config.lines)) {
|
|
3338
|
+
console.log(` ${key.padEnd(14)} ${val ? "on" : "off"}`);
|
|
3339
|
+
}
|
|
3340
|
+
console.log();
|
|
2858
3341
|
}
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
3342
|
+
var VALID_RENDERERS2;
|
|
3343
|
+
var init_statusline = __esm({
|
|
3344
|
+
"src/cli/statusline.ts"() {
|
|
3345
|
+
"use strict";
|
|
3346
|
+
init_flags();
|
|
3347
|
+
init_statusline_config();
|
|
3348
|
+
VALID_RENDERERS2 = /* @__PURE__ */ new Set(["bash", "node", "python"]);
|
|
3349
|
+
}
|
|
3350
|
+
});
|
|
3351
|
+
|
|
3352
|
+
// src/cli/logout.ts
|
|
3353
|
+
var logout_exports = {};
|
|
3354
|
+
__export(logout_exports, {
|
|
3355
|
+
runLogout: () => runLogout
|
|
3356
|
+
});
|
|
3357
|
+
async function runLogout() {
|
|
3358
|
+
await clearTokens();
|
|
3359
|
+
await clearClient();
|
|
3360
|
+
console.log("\n Logged out.\n");
|
|
2868
3361
|
}
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
3362
|
+
var init_logout = __esm({
|
|
3363
|
+
"src/cli/logout.ts"() {
|
|
3364
|
+
"use strict";
|
|
3365
|
+
init_client_registration();
|
|
3366
|
+
init_keychain();
|
|
3367
|
+
}
|
|
3368
|
+
});
|
|
3369
|
+
|
|
3370
|
+
// src/cli/whoami.ts
|
|
3371
|
+
var whoami_exports = {};
|
|
3372
|
+
__export(whoami_exports, {
|
|
3373
|
+
runWhoami: () => runWhoami
|
|
3374
|
+
});
|
|
3375
|
+
async function fetchMe(accessToken) {
|
|
3376
|
+
try {
|
|
3377
|
+
const res = await fetch(meEndpoint(), {
|
|
3378
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
3379
|
+
signal: AbortSignal.timeout(1e4)
|
|
3380
|
+
});
|
|
3381
|
+
if (!res.ok) return null;
|
|
3382
|
+
const body = await res.json();
|
|
2872
3383
|
return {
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
flat: parseTechStack(obj.flat)
|
|
3384
|
+
email: body.email ?? null,
|
|
3385
|
+
username: body.username ?? null
|
|
2876
3386
|
};
|
|
3387
|
+
} catch {
|
|
3388
|
+
return null;
|
|
2877
3389
|
}
|
|
2878
|
-
const flat = parseTechStack(raw);
|
|
2879
|
-
return { repo: flat, apps: [], flat };
|
|
2880
3390
|
}
|
|
2881
|
-
function
|
|
2882
|
-
const
|
|
2883
|
-
if (
|
|
2884
|
-
|
|
2885
|
-
|
|
3391
|
+
async function runWhoami(opts = {}) {
|
|
3392
|
+
const tokens = await loadTokens();
|
|
3393
|
+
if (opts.json) {
|
|
3394
|
+
if (!tokens) {
|
|
3395
|
+
console.log("null");
|
|
3396
|
+
process.exitCode = 1;
|
|
3397
|
+
return;
|
|
3398
|
+
}
|
|
3399
|
+
const me2 = await fetchMe(tokens.access_token);
|
|
3400
|
+
console.log(
|
|
3401
|
+
JSON.stringify({
|
|
3402
|
+
user_id: tokens.user_id,
|
|
3403
|
+
email: me2?.email ?? tokens.email,
|
|
3404
|
+
username: me2?.username ?? null
|
|
3405
|
+
})
|
|
3406
|
+
);
|
|
3407
|
+
return;
|
|
3408
|
+
}
|
|
3409
|
+
if (!tokens) {
|
|
3410
|
+
console.log("\n Not logged in. Run `codebyplan login` to authenticate.\n");
|
|
3411
|
+
process.exitCode = 1;
|
|
3412
|
+
return;
|
|
3413
|
+
}
|
|
3414
|
+
const me = await fetchMe(tokens.access_token);
|
|
3415
|
+
const degraded = me === null;
|
|
3416
|
+
const email = me?.email ?? tokens.email;
|
|
3417
|
+
const username = me?.username ?? null;
|
|
3418
|
+
console.log(`
|
|
3419
|
+
user_id: ${tokens.user_id}`);
|
|
3420
|
+
console.log(` email: ${email ?? "(unknown)"}`);
|
|
3421
|
+
console.log(` username: ${username ?? "(not set)"}`);
|
|
3422
|
+
if (degraded) {
|
|
3423
|
+
console.log(` (profile fetch failed \u2014 showing stored credentials)
|
|
3424
|
+
`);
|
|
3425
|
+
} else {
|
|
3426
|
+
console.log();
|
|
2886
3427
|
}
|
|
2887
|
-
return "other";
|
|
2888
3428
|
}
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
results.push(pkgPath);
|
|
3429
|
+
var init_whoami = __esm({
|
|
3430
|
+
"src/cli/whoami.ts"() {
|
|
3431
|
+
"use strict";
|
|
3432
|
+
init_keychain();
|
|
3433
|
+
init_urls();
|
|
2895
3434
|
}
|
|
3435
|
+
});
|
|
3436
|
+
|
|
3437
|
+
// src/cli/upgrade-auth.ts
|
|
3438
|
+
var upgrade_auth_exports = {};
|
|
3439
|
+
__export(upgrade_auth_exports, {
|
|
3440
|
+
runUpgradeAuth: () => runUpgradeAuth
|
|
3441
|
+
});
|
|
3442
|
+
import { readFile as readFile10, writeFile as writeFile8 } from "node:fs/promises";
|
|
3443
|
+
import { homedir as homedir5 } from "node:os";
|
|
3444
|
+
import { join as join13 } from "node:path";
|
|
3445
|
+
function configPaths() {
|
|
3446
|
+
return [join13(homedir5(), ".claude.json"), join13(process.cwd(), ".mcp.json")];
|
|
3447
|
+
}
|
|
3448
|
+
async function readConfig2(path10) {
|
|
2896
3449
|
try {
|
|
2897
|
-
const
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
join12(dir, entry.name),
|
|
2902
|
-
projectPath,
|
|
2903
|
-
depth + 1
|
|
2904
|
-
);
|
|
2905
|
-
results.push(...subResults);
|
|
3450
|
+
const raw = await readFile10(path10, "utf-8");
|
|
3451
|
+
const parsed = JSON.parse(raw);
|
|
3452
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
3453
|
+
return parsed;
|
|
2906
3454
|
}
|
|
3455
|
+
return null;
|
|
2907
3456
|
} catch {
|
|
3457
|
+
return null;
|
|
2908
3458
|
}
|
|
2909
|
-
return results;
|
|
2910
3459
|
}
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
}
|
|
3460
|
+
function entryHasLegacyApiKey(entry) {
|
|
3461
|
+
if (!entry || !entry.headers) return false;
|
|
3462
|
+
return "x-api-key" in entry.headers;
|
|
3463
|
+
}
|
|
3464
|
+
async function rewriteConfig(path10, config, newUrl) {
|
|
3465
|
+
const servers = config.mcpServers;
|
|
3466
|
+
if (!servers) return false;
|
|
3467
|
+
const entry = servers.codebyplan;
|
|
3468
|
+
if (!entry) return false;
|
|
3469
|
+
if (!entryHasLegacyApiKey(entry) && entry.url === newUrl && entry.type === "http")
|
|
3470
|
+
return false;
|
|
3471
|
+
servers.codebyplan = { type: "http", url: newUrl };
|
|
3472
|
+
await writeFile8(path10, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
3473
|
+
return true;
|
|
3474
|
+
}
|
|
3475
|
+
async function runUpgradeAuth() {
|
|
3476
|
+
console.log("\n Upgrading codebyplan auth to OAuth...\n");
|
|
3477
|
+
await runLogin();
|
|
3478
|
+
const newUrl = mcpEndpoint();
|
|
3479
|
+
let migrated = 0;
|
|
3480
|
+
for (const path10 of configPaths()) {
|
|
3481
|
+
const config = await readConfig2(path10);
|
|
3482
|
+
if (!config) continue;
|
|
3483
|
+
const changed = await rewriteConfig(path10, config, newUrl);
|
|
3484
|
+
if (changed) {
|
|
3485
|
+
console.log(` Updated ${path10}`);
|
|
3486
|
+
migrated++;
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
if (migrated === 0) {
|
|
3490
|
+
console.log(
|
|
3491
|
+
" All codebyplan MCP entries are already up to date \u2014 nothing to change."
|
|
3492
|
+
);
|
|
3493
|
+
} else {
|
|
3494
|
+
console.log(
|
|
3495
|
+
`
|
|
3496
|
+
Done. Restart Claude Code to use the new endpoint: ${newUrl}
|
|
3497
|
+
`
|
|
3498
|
+
);
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
var init_upgrade_auth = __esm({
|
|
3502
|
+
"src/cli/upgrade-auth.ts"() {
|
|
3503
|
+
"use strict";
|
|
3504
|
+
init_urls();
|
|
3505
|
+
init_login();
|
|
3506
|
+
}
|
|
3507
|
+
});
|
|
3508
|
+
|
|
3509
|
+
// src/cli/confirm.ts
|
|
3510
|
+
var confirm_exports = {};
|
|
3511
|
+
__export(confirm_exports, {
|
|
3512
|
+
SyncCancelledError: () => SyncCancelledError,
|
|
3513
|
+
confirmProceed: () => confirmProceed
|
|
3514
|
+
});
|
|
3515
|
+
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
3516
|
+
import { stdin as stdin2, stdout as stdout3 } from "node:process";
|
|
3517
|
+
function isAbortError(err) {
|
|
3518
|
+
return err instanceof Error && err.code === "ABORT_ERR";
|
|
3519
|
+
}
|
|
3520
|
+
async function confirmProceed(message) {
|
|
3521
|
+
const rl = createInterface2({ input: stdin2, output: stdout3 });
|
|
3522
|
+
try {
|
|
3523
|
+
while (true) {
|
|
3524
|
+
const answer = await rl.question(message ?? " Proceed? [Y/n] ");
|
|
3525
|
+
const a = answer.trim().toLowerCase();
|
|
3526
|
+
if (a === "" || a === "y" || a === "yes") return true;
|
|
3527
|
+
if (a === "n" || a === "no") return false;
|
|
3528
|
+
console.log(
|
|
3529
|
+
` Unknown option "${answer.trim()}". Valid: y/yes, n/no, or Enter for yes.`
|
|
3530
|
+
);
|
|
2939
3531
|
}
|
|
3532
|
+
} catch (err) {
|
|
3533
|
+
if (isAbortError(err)) throw new SyncCancelledError();
|
|
3534
|
+
throw err;
|
|
3535
|
+
} finally {
|
|
3536
|
+
rl.close();
|
|
2940
3537
|
}
|
|
2941
|
-
return { dependencies };
|
|
2942
3538
|
}
|
|
2943
|
-
var
|
|
2944
|
-
var
|
|
2945
|
-
"src/
|
|
3539
|
+
var SyncCancelledError;
|
|
3540
|
+
var init_confirm = __esm({
|
|
3541
|
+
"src/cli/confirm.ts"() {
|
|
2946
3542
|
"use strict";
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
gatsby: { name: "Gatsby", category: "framework" },
|
|
2952
|
-
express: { name: "Express", category: "framework" },
|
|
2953
|
-
fastify: { name: "Fastify", category: "framework" },
|
|
2954
|
-
hono: { name: "Hono", category: "framework" },
|
|
2955
|
-
"@remix-run/node": { name: "Remix", category: "framework" },
|
|
2956
|
-
svelte: { name: "Svelte", category: "framework" },
|
|
2957
|
-
astro: { name: "Astro", category: "framework" },
|
|
2958
|
-
"@angular/core": { name: "Angular", category: "framework" },
|
|
2959
|
-
"@nestjs/core": { name: "NestJS", category: "framework" },
|
|
2960
|
-
// Libraries (UI)
|
|
2961
|
-
react: { name: "React", category: "framework" },
|
|
2962
|
-
vue: { name: "Vue", category: "framework" },
|
|
2963
|
-
"solid-js": { name: "Solid", category: "framework" },
|
|
2964
|
-
preact: { name: "Preact", category: "framework" },
|
|
2965
|
-
// Languages (detected via devDeps)
|
|
2966
|
-
typescript: { name: "TypeScript", category: "language" },
|
|
2967
|
-
// Styling
|
|
2968
|
-
tailwindcss: { name: "Tailwind CSS", category: "styling" },
|
|
2969
|
-
sass: { name: "SCSS", category: "styling" },
|
|
2970
|
-
"styled-components": { name: "styled-components", category: "styling" },
|
|
2971
|
-
"@emotion/react": { name: "Emotion", category: "styling" },
|
|
2972
|
-
// Database
|
|
2973
|
-
prisma: { name: "Prisma", category: "database" },
|
|
2974
|
-
"@prisma/client": { name: "Prisma", category: "database" },
|
|
2975
|
-
"drizzle-orm": { name: "Drizzle", category: "database" },
|
|
2976
|
-
"@supabase/supabase-js": { name: "Supabase", category: "database" },
|
|
2977
|
-
mongoose: { name: "MongoDB", category: "database" },
|
|
2978
|
-
typeorm: { name: "TypeORM", category: "database" },
|
|
2979
|
-
knex: { name: "Knex", category: "database" },
|
|
2980
|
-
// Testing
|
|
2981
|
-
jest: { name: "Jest", category: "testing" },
|
|
2982
|
-
vitest: { name: "Vitest", category: "testing" },
|
|
2983
|
-
mocha: { name: "Mocha", category: "testing" },
|
|
2984
|
-
playwright: { name: "Playwright", category: "testing" },
|
|
2985
|
-
"@playwright/test": { name: "Playwright", category: "testing" },
|
|
2986
|
-
cypress: { name: "Cypress", category: "testing" },
|
|
2987
|
-
supertest: { name: "Supertest", category: "testing" },
|
|
2988
|
-
webdriverio: { name: "WebdriverIO", category: "testing" },
|
|
2989
|
-
"@wdio/cli": { name: "WebdriverIO", category: "testing" },
|
|
2990
|
-
"@vscode/test-cli": { name: "VS Code Test CLI", category: "testing" },
|
|
2991
|
-
// Build tools
|
|
2992
|
-
turbo: { name: "Turborepo", category: "build" },
|
|
2993
|
-
vite: { name: "Vite", category: "build" },
|
|
2994
|
-
webpack: { name: "Webpack", category: "build" },
|
|
2995
|
-
esbuild: { name: "esbuild", category: "build" },
|
|
2996
|
-
rollup: { name: "Rollup", category: "build" },
|
|
2997
|
-
nx: { name: "Nx", category: "build" },
|
|
2998
|
-
lerna: { name: "Lerna", category: "build" },
|
|
2999
|
-
tsup: { name: "tsup", category: "build" },
|
|
3000
|
-
"@swc/core": { name: "SWC", category: "build" },
|
|
3001
|
-
parcel: { name: "Parcel", category: "build" },
|
|
3002
|
-
// Tools
|
|
3003
|
-
eslint: { name: "ESLint", category: "tool" },
|
|
3004
|
-
prettier: { name: "Prettier", category: "tool" },
|
|
3005
|
-
"@biomejs/biome": { name: "Biome", category: "tool" },
|
|
3006
|
-
storybook: { name: "Storybook", category: "tool" },
|
|
3007
|
-
// Component libs
|
|
3008
|
-
"@mui/material": { name: "MUI", category: "component-lib" },
|
|
3009
|
-
"@chakra-ui/react": { name: "Chakra UI", category: "component-lib" },
|
|
3010
|
-
"@mantine/core": { name: "Mantine", category: "component-lib" },
|
|
3011
|
-
// GraphQL
|
|
3012
|
-
graphql: { name: "GraphQL", category: "graphql" },
|
|
3013
|
-
"@apollo/client": { name: "Apollo Client", category: "graphql" },
|
|
3014
|
-
urql: { name: "urql", category: "graphql" },
|
|
3015
|
-
"graphql-request": { name: "graphql-request", category: "graphql" },
|
|
3016
|
-
// Documentation
|
|
3017
|
-
typedoc: { name: "TypeDoc", category: "documentation" },
|
|
3018
|
-
"@docusaurus/core": { name: "Docusaurus", category: "documentation" },
|
|
3019
|
-
vitepress: { name: "VitePress", category: "documentation" },
|
|
3020
|
-
// Code quality
|
|
3021
|
-
husky: { name: "Husky", category: "quality" },
|
|
3022
|
-
"lint-staged": { name: "lint-staged", category: "quality" },
|
|
3023
|
-
commitlint: { name: "commitlint", category: "quality" },
|
|
3024
|
-
"@commitlint/cli": { name: "commitlint", category: "quality" },
|
|
3025
|
-
// Mobile
|
|
3026
|
-
"react-native": { name: "React Native", category: "mobile" },
|
|
3027
|
-
expo: { name: "Expo", category: "mobile" }
|
|
3028
|
-
};
|
|
3029
|
-
PACKAGE_PREFIX_MAP = [
|
|
3030
|
-
{
|
|
3031
|
-
prefix: "@radix-ui/",
|
|
3032
|
-
rule: { name: "Radix UI", category: "component-lib" }
|
|
3033
|
-
},
|
|
3034
|
-
{ prefix: "@storybook/", rule: { name: "Storybook", category: "tool" } },
|
|
3035
|
-
{
|
|
3036
|
-
prefix: "@testing-library/",
|
|
3037
|
-
rule: { name: "Testing Library", category: "testing" }
|
|
3038
|
-
}
|
|
3039
|
-
];
|
|
3040
|
-
CONFIG_FILE_MAP = [
|
|
3041
|
-
{ file: "tsconfig.json", rule: { name: "TypeScript", category: "language" } },
|
|
3042
|
-
{ file: "next.config.js", rule: { name: "Next.js", category: "framework" } },
|
|
3043
|
-
{ file: "next.config.mjs", rule: { name: "Next.js", category: "framework" } },
|
|
3044
|
-
{ file: "next.config.ts", rule: { name: "Next.js", category: "framework" } },
|
|
3045
|
-
{
|
|
3046
|
-
file: "tailwind.config.js",
|
|
3047
|
-
rule: { name: "Tailwind CSS", category: "styling" }
|
|
3048
|
-
},
|
|
3049
|
-
{
|
|
3050
|
-
file: "tailwind.config.ts",
|
|
3051
|
-
rule: { name: "Tailwind CSS", category: "styling" }
|
|
3052
|
-
},
|
|
3053
|
-
{ file: "turbo.json", rule: { name: "Turborepo", category: "build" } },
|
|
3054
|
-
{
|
|
3055
|
-
file: "docker-compose.yml",
|
|
3056
|
-
rule: { name: "Docker", category: "deployment" }
|
|
3057
|
-
},
|
|
3058
|
-
{
|
|
3059
|
-
file: "docker-compose.yaml",
|
|
3060
|
-
rule: { name: "Docker", category: "deployment" }
|
|
3061
|
-
},
|
|
3062
|
-
{ file: "Dockerfile", rule: { name: "Docker", category: "deployment" } },
|
|
3063
|
-
{ file: "vercel.json", rule: { name: "Vercel", category: "deployment" } },
|
|
3064
|
-
{ file: ".storybook/main.js", rule: { name: "Storybook", category: "tool" } },
|
|
3065
|
-
{ file: ".storybook/main.ts", rule: { name: "Storybook", category: "tool" } },
|
|
3066
|
-
{
|
|
3067
|
-
file: ".storybook/main.mjs",
|
|
3068
|
-
rule: { name: "Storybook", category: "tool" }
|
|
3069
|
-
},
|
|
3070
|
-
{
|
|
3071
|
-
file: "components.json",
|
|
3072
|
-
rule: { name: "shadcn/ui", category: "component-lib" }
|
|
3073
|
-
},
|
|
3074
|
-
{ file: "nx.json", rule: { name: "Nx", category: "build" } },
|
|
3075
|
-
{ file: "lerna.json", rule: { name: "Lerna", category: "build" } },
|
|
3076
|
-
{
|
|
3077
|
-
file: "playwright.config.ts",
|
|
3078
|
-
rule: { name: "Playwright", category: "testing" }
|
|
3079
|
-
},
|
|
3080
|
-
{
|
|
3081
|
-
file: "playwright.config.js",
|
|
3082
|
-
rule: { name: "Playwright", category: "testing" }
|
|
3083
|
-
},
|
|
3084
|
-
{
|
|
3085
|
-
file: "playwright.config.mjs",
|
|
3086
|
-
rule: { name: "Playwright", category: "testing" }
|
|
3087
|
-
},
|
|
3088
|
-
{ file: "wdio.conf.ts", rule: { name: "WebdriverIO", category: "testing" } },
|
|
3089
|
-
{
|
|
3090
|
-
file: "wdio.conf.js",
|
|
3091
|
-
rule: { name: "WebdriverIO", category: "testing" }
|
|
3092
|
-
},
|
|
3093
|
-
{
|
|
3094
|
-
file: "maestro/config.yaml",
|
|
3095
|
-
rule: { name: "Maestro", category: "testing" }
|
|
3543
|
+
SyncCancelledError = class extends Error {
|
|
3544
|
+
constructor() {
|
|
3545
|
+
super("Sync cancelled");
|
|
3546
|
+
this.name = "SyncCancelledError";
|
|
3096
3547
|
}
|
|
3097
|
-
];
|
|
3098
|
-
SYNTHETIC_CARRIER_NAME = "__capabilities__";
|
|
3099
|
-
CAPABILITY_BEARER_NAMES = /* @__PURE__ */ new Set([
|
|
3100
|
-
"react",
|
|
3101
|
-
"next.js",
|
|
3102
|
-
"vue",
|
|
3103
|
-
"svelte",
|
|
3104
|
-
"solid",
|
|
3105
|
-
"preact",
|
|
3106
|
-
"remix",
|
|
3107
|
-
"astro",
|
|
3108
|
-
"angular",
|
|
3109
|
-
"nestjs",
|
|
3110
|
-
"nuxt",
|
|
3111
|
-
"gatsby",
|
|
3112
|
-
"express",
|
|
3113
|
-
"fastify",
|
|
3114
|
-
"hono",
|
|
3115
|
-
"react native",
|
|
3116
|
-
"expo"
|
|
3117
|
-
]);
|
|
3118
|
-
JSX_TEST_PATTERN = /\.(test|spec)\.(tsx|jsx)$/;
|
|
3119
|
-
JSX_SKIP_DIRS = /* @__PURE__ */ new Set(["__tests__", "test", "tests"]);
|
|
3120
|
-
JSX_SCAN_DIRS = ["src", "app", "pages"];
|
|
3121
|
-
SERVER_MAIN_ENTRIES = /* @__PURE__ */ new Set([
|
|
3122
|
-
"dist/main.js",
|
|
3123
|
-
"dist/server.js",
|
|
3124
|
-
"dist/index.js",
|
|
3125
|
-
"main.js",
|
|
3126
|
-
"server.js"
|
|
3127
|
-
]);
|
|
3128
|
-
SERVER_FRAMEWORK_DEPS = /* @__PURE__ */ new Set([
|
|
3129
|
-
"@nestjs/core",
|
|
3130
|
-
"express",
|
|
3131
|
-
"fastify",
|
|
3132
|
-
"hono"
|
|
3133
|
-
]);
|
|
3134
|
-
compareByCategoryThenName = (a, b) => {
|
|
3135
|
-
const catCmp = a.category.localeCompare(b.category);
|
|
3136
|
-
if (catCmp !== 0) return catCmp;
|
|
3137
|
-
return a.name.localeCompare(b.name);
|
|
3138
3548
|
};
|
|
3139
|
-
SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
3140
|
-
"node_modules",
|
|
3141
|
-
".next",
|
|
3142
|
-
"dist",
|
|
3143
|
-
".turbo",
|
|
3144
|
-
".git",
|
|
3145
|
-
"coverage",
|
|
3146
|
-
"build",
|
|
3147
|
-
"out",
|
|
3148
|
-
".vercel",
|
|
3149
|
-
".expo"
|
|
3150
|
-
]);
|
|
3151
3549
|
}
|
|
3152
3550
|
});
|
|
3153
3551
|
|
|
@@ -3537,11 +3935,11 @@ __export(eslint_exports, {
|
|
|
3537
3935
|
eslintInit: () => eslintInit,
|
|
3538
3936
|
runEslint: () => runEslint
|
|
3539
3937
|
});
|
|
3540
|
-
import { readFile as
|
|
3541
|
-
import { join as
|
|
3542
|
-
async function
|
|
3938
|
+
import { readFile as readFile11, writeFile as writeFile9, access as access3, readdir as readdir2 } from "node:fs/promises";
|
|
3939
|
+
import { join as join14, relative as relative4 } from "node:path";
|
|
3940
|
+
async function fileExists3(filePath) {
|
|
3543
3941
|
try {
|
|
3544
|
-
await
|
|
3942
|
+
await access3(filePath);
|
|
3545
3943
|
return true;
|
|
3546
3944
|
} catch {
|
|
3547
3945
|
return false;
|
|
@@ -3549,7 +3947,7 @@ async function fileExists2(filePath) {
|
|
|
3549
3947
|
}
|
|
3550
3948
|
async function autoDetectIgnorePatterns(absPath) {
|
|
3551
3949
|
const patterns = [];
|
|
3552
|
-
if (await
|
|
3950
|
+
if (await fileExists3(join14(absPath, "esbuild.js"))) {
|
|
3553
3951
|
patterns.push("esbuild.js");
|
|
3554
3952
|
}
|
|
3555
3953
|
let entries = [];
|
|
@@ -3569,19 +3967,19 @@ async function autoDetectIgnorePatterns(absPath) {
|
|
|
3569
3967
|
}
|
|
3570
3968
|
for (const ext of ["ts", "mts", "js", "mjs"]) {
|
|
3571
3969
|
const candidate = `vitest.config.${ext}`;
|
|
3572
|
-
if (await
|
|
3970
|
+
if (await fileExists3(join14(absPath, candidate))) {
|
|
3573
3971
|
patterns.push(candidate);
|
|
3574
3972
|
break;
|
|
3575
3973
|
}
|
|
3576
3974
|
}
|
|
3577
3975
|
for (const ext of ["ts", "mts", "js", "mjs"]) {
|
|
3578
3976
|
const candidate = `vite.config.${ext}`;
|
|
3579
|
-
if (await
|
|
3977
|
+
if (await fileExists3(join14(absPath, candidate))) {
|
|
3580
3978
|
patterns.push(candidate);
|
|
3581
3979
|
break;
|
|
3582
3980
|
}
|
|
3583
3981
|
}
|
|
3584
|
-
if (await
|
|
3982
|
+
if (await fileExists3(join14(absPath, "tauri.conf.json"))) {
|
|
3585
3983
|
patterns.push("src-tauri/**");
|
|
3586
3984
|
patterns.push("**/*.d.ts");
|
|
3587
3985
|
}
|
|
@@ -3589,14 +3987,14 @@ async function autoDetectIgnorePatterns(absPath) {
|
|
|
3589
3987
|
}
|
|
3590
3988
|
function detectPackageManager(projectPath) {
|
|
3591
3989
|
return (async () => {
|
|
3592
|
-
if (await
|
|
3593
|
-
if (await
|
|
3990
|
+
if (await fileExists3(join14(projectPath, "pnpm-lock.yaml"))) return "pnpm";
|
|
3991
|
+
if (await fileExists3(join14(projectPath, "yarn.lock"))) return "yarn";
|
|
3594
3992
|
return "npm";
|
|
3595
3993
|
})();
|
|
3596
3994
|
}
|
|
3597
3995
|
async function getInstalledDeps(pkgJsonPath) {
|
|
3598
3996
|
try {
|
|
3599
|
-
const raw = await
|
|
3997
|
+
const raw = await readFile11(pkgJsonPath, "utf-8");
|
|
3600
3998
|
const pkg = JSON.parse(raw);
|
|
3601
3999
|
const all = /* @__PURE__ */ new Set();
|
|
3602
4000
|
for (const name of Object.keys(pkg.dependencies ?? {})) all.add(name);
|
|
@@ -3709,7 +4107,7 @@ async function eslintInit(repoId, projectPath) {
|
|
|
3709
4107
|
ignorePatterns: detectedIgnores
|
|
3710
4108
|
});
|
|
3711
4109
|
const hash = hashConfig(content);
|
|
3712
|
-
const configPath =
|
|
4110
|
+
const configPath = join14(target.absPath, "eslint.config.mjs");
|
|
3713
4111
|
configsToWrite.push({
|
|
3714
4112
|
target,
|
|
3715
4113
|
presets,
|
|
@@ -3731,11 +4129,11 @@ async function eslintInit(repoId, projectPath) {
|
|
|
3731
4129
|
return;
|
|
3732
4130
|
}
|
|
3733
4131
|
const pm = await detectPackageManager(projectPath);
|
|
3734
|
-
const rootPkgJsonPath =
|
|
4132
|
+
const rootPkgJsonPath = join14(projectPath, "package.json");
|
|
3735
4133
|
const installed = await getInstalledDeps(rootPkgJsonPath);
|
|
3736
4134
|
if (isMonorepo) {
|
|
3737
4135
|
for (const { target } of configsToWrite) {
|
|
3738
|
-
const appPkgJson =
|
|
4136
|
+
const appPkgJson = join14(target.absPath, "package.json");
|
|
3739
4137
|
const appDeps = await getInstalledDeps(appPkgJson);
|
|
3740
4138
|
for (const dep of appDeps) {
|
|
3741
4139
|
installed.add(dep);
|
|
@@ -3785,9 +4183,9 @@ async function eslintInit(repoId, projectPath) {
|
|
|
3785
4183
|
configPath,
|
|
3786
4184
|
userOverrides
|
|
3787
4185
|
} of configsToWrite) {
|
|
3788
|
-
if (await
|
|
4186
|
+
if (await fileExists3(configPath)) {
|
|
3789
4187
|
try {
|
|
3790
|
-
const existing = await
|
|
4188
|
+
const existing = await readFile11(configPath, "utf-8");
|
|
3791
4189
|
const existingHash = hashConfig(existing);
|
|
3792
4190
|
if (existingHash === hash) {
|
|
3793
4191
|
console.log(
|
|
@@ -3807,7 +4205,7 @@ async function eslintInit(repoId, projectPath) {
|
|
|
3807
4205
|
}
|
|
3808
4206
|
}
|
|
3809
4207
|
try {
|
|
3810
|
-
await
|
|
4208
|
+
await writeFile9(configPath, content, "utf-8");
|
|
3811
4209
|
} catch (err) {
|
|
3812
4210
|
console.error(
|
|
3813
4211
|
` ${target.name}: Failed to write config: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -4107,16 +4505,16 @@ var init_sync_approvals = __esm({
|
|
|
4107
4505
|
});
|
|
4108
4506
|
|
|
4109
4507
|
// src/lib/worktree-cache.ts
|
|
4110
|
-
import { mkdir as
|
|
4111
|
-
import { dirname as dirname6, join as
|
|
4508
|
+
import { mkdir as mkdir6, readFile as readFile12, writeFile as writeFile10 } from "node:fs/promises";
|
|
4509
|
+
import { dirname as dirname6, join as join15 } from "node:path";
|
|
4112
4510
|
function worktreeCachePath(repoRoot) {
|
|
4113
|
-
return
|
|
4511
|
+
return join15(repoRoot, ".codebyplan", "worktree.local.json");
|
|
4114
4512
|
}
|
|
4115
4513
|
async function readCachedWorktreeId(repoRoot, currentBranch) {
|
|
4116
4514
|
const cachePath = worktreeCachePath(repoRoot);
|
|
4117
4515
|
let raw;
|
|
4118
4516
|
try {
|
|
4119
|
-
raw = await
|
|
4517
|
+
raw = await readFile12(cachePath, "utf-8");
|
|
4120
4518
|
} catch (err) {
|
|
4121
4519
|
const code = err.code;
|
|
4122
4520
|
if (code === "ENOENT") {
|
|
@@ -4161,8 +4559,8 @@ async function writeWorktreeCache(repoRoot, data) {
|
|
|
4161
4559
|
resolved_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4162
4560
|
};
|
|
4163
4561
|
try {
|
|
4164
|
-
await
|
|
4165
|
-
await
|
|
4562
|
+
await mkdir6(dirPath, { recursive: true });
|
|
4563
|
+
await writeFile10(
|
|
4166
4564
|
cachePath,
|
|
4167
4565
|
JSON.stringify(payload, null, 2) + "\n",
|
|
4168
4566
|
"utf-8"
|
|
@@ -4191,8 +4589,8 @@ __export(round_exports, {
|
|
|
4191
4589
|
runRoundSyncApprovals: () => runRoundSyncApprovals,
|
|
4192
4590
|
setRetryDelayMs: () => setRetryDelayMs
|
|
4193
4591
|
});
|
|
4194
|
-
import { access as
|
|
4195
|
-
import { join as
|
|
4592
|
+
import { access as access4 } from "node:fs/promises";
|
|
4593
|
+
import { join as join16 } from "node:path";
|
|
4196
4594
|
import { execSync as execSync2 } from "node:child_process";
|
|
4197
4595
|
function setRetryDelayMs(ms) {
|
|
4198
4596
|
RETRY_DELAY_MS = ms;
|
|
@@ -4373,7 +4771,7 @@ async function runRoundSyncApprovals(args) {
|
|
|
4373
4771
|
"sync-approvals: git status failed; proceeding with empty diff\n"
|
|
4374
4772
|
);
|
|
4375
4773
|
}
|
|
4376
|
-
const hookPath =
|
|
4774
|
+
const hookPath = join16(
|
|
4377
4775
|
repoRoot,
|
|
4378
4776
|
".claude",
|
|
4379
4777
|
"hooks",
|
|
@@ -4381,7 +4779,7 @@ async function runRoundSyncApprovals(args) {
|
|
|
4381
4779
|
);
|
|
4382
4780
|
let lintFormatHookExists = false;
|
|
4383
4781
|
try {
|
|
4384
|
-
await
|
|
4782
|
+
await access4(hookPath);
|
|
4385
4783
|
lintFormatHookExists = true;
|
|
4386
4784
|
} catch {
|
|
4387
4785
|
}
|
|
@@ -4464,8 +4862,8 @@ var init_round = __esm({
|
|
|
4464
4862
|
});
|
|
4465
4863
|
|
|
4466
4864
|
// src/lib/migrate-branch-model.ts
|
|
4467
|
-
import { readFile as
|
|
4468
|
-
import { join as
|
|
4865
|
+
import { readFile as readFile13, writeFile as writeFile11 } from "node:fs/promises";
|
|
4866
|
+
import { join as join17 } from "node:path";
|
|
4469
4867
|
import { execSync as execSync3 } from "node:child_process";
|
|
4470
4868
|
function assertValidBranchName(branch) {
|
|
4471
4869
|
if (!/^[a-zA-Z0-9/_.-]+$/.test(branch)) {
|
|
@@ -4474,8 +4872,8 @@ function assertValidBranchName(branch) {
|
|
|
4474
4872
|
);
|
|
4475
4873
|
}
|
|
4476
4874
|
}
|
|
4477
|
-
async function
|
|
4478
|
-
const raw = await
|
|
4875
|
+
async function readJsonFile2(filePath) {
|
|
4876
|
+
const raw = await readFile13(filePath, "utf-8");
|
|
4479
4877
|
const parsed = JSON.parse(raw);
|
|
4480
4878
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
4481
4879
|
throw new Error(`${filePath} does not contain a JSON object`);
|
|
@@ -4544,17 +4942,17 @@ async function runBranchMigration(opts) {
|
|
|
4544
4942
|
if (found) {
|
|
4545
4943
|
if (found.path.endsWith("/repo.json")) {
|
|
4546
4944
|
const dir = found.path.slice(0, found.path.lastIndexOf("/"));
|
|
4547
|
-
configPath =
|
|
4945
|
+
configPath = join17(dir, "git.json");
|
|
4548
4946
|
} else {
|
|
4549
4947
|
configPath = found.path;
|
|
4550
4948
|
}
|
|
4551
4949
|
} else {
|
|
4552
|
-
configPath =
|
|
4950
|
+
configPath = join17(cwd, ".codebyplan", "git.json");
|
|
4553
4951
|
}
|
|
4554
4952
|
let fileRaw;
|
|
4555
4953
|
let fileParsed;
|
|
4556
4954
|
try {
|
|
4557
|
-
const result = await
|
|
4955
|
+
const result = await readJsonFile2(configPath);
|
|
4558
4956
|
fileRaw = result.raw;
|
|
4559
4957
|
fileParsed = result.parsed;
|
|
4560
4958
|
} catch (err) {
|
|
@@ -4623,7 +5021,7 @@ async function runBranchMigration(opts) {
|
|
|
4623
5021
|
const updatedParsed = { ...fileParsed, branch_config: after };
|
|
4624
5022
|
const newJson = JSON.stringify(updatedParsed, null, 2) + "\n";
|
|
4625
5023
|
if (newJson !== fileRaw) {
|
|
4626
|
-
await
|
|
5024
|
+
await writeFile11(configPath, newJson, "utf-8");
|
|
4627
5025
|
}
|
|
4628
5026
|
}
|
|
4629
5027
|
return {
|
|
@@ -4729,24 +5127,24 @@ var init_branch = __esm({
|
|
|
4729
5127
|
});
|
|
4730
5128
|
|
|
4731
5129
|
// src/lib/git-utils.ts
|
|
4732
|
-
import { readFile as
|
|
4733
|
-
import { join as
|
|
4734
|
-
import { spawnSync as
|
|
5130
|
+
import { readFile as readFile14 } from "node:fs/promises";
|
|
5131
|
+
import { join as join18 } from "node:path";
|
|
5132
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
4735
5133
|
async function readBaseBranch(cwd) {
|
|
4736
5134
|
const found = await findCodebyplanConfig(cwd);
|
|
4737
5135
|
let gitJsonPath;
|
|
4738
5136
|
if (found) {
|
|
4739
5137
|
if (found.path.endsWith("/repo.json")) {
|
|
4740
5138
|
const dir = found.path.slice(0, found.path.lastIndexOf("/"));
|
|
4741
|
-
gitJsonPath =
|
|
5139
|
+
gitJsonPath = join18(dir, "git.json");
|
|
4742
5140
|
} else {
|
|
4743
5141
|
gitJsonPath = found.path;
|
|
4744
5142
|
}
|
|
4745
5143
|
} else {
|
|
4746
|
-
gitJsonPath =
|
|
5144
|
+
gitJsonPath = join18(cwd, ".codebyplan", "git.json");
|
|
4747
5145
|
}
|
|
4748
5146
|
try {
|
|
4749
|
-
const raw = await
|
|
5147
|
+
const raw = await readFile14(gitJsonPath, "utf-8");
|
|
4750
5148
|
const parsed = JSON.parse(raw);
|
|
4751
5149
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
4752
5150
|
return "main";
|
|
@@ -4766,7 +5164,7 @@ async function readBaseBranch(cwd) {
|
|
|
4766
5164
|
function resolveBaseRef(cwd, baseBranch) {
|
|
4767
5165
|
if (baseBranch.startsWith("origin/")) return baseBranch;
|
|
4768
5166
|
const remoteRef = `origin/${baseBranch}`;
|
|
4769
|
-
const check =
|
|
5167
|
+
const check = spawnSync4(
|
|
4770
5168
|
"git",
|
|
4771
5169
|
["rev-parse", "--verify", "--quiet", `${remoteRef}^{commit}`],
|
|
4772
5170
|
{ cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -4784,9 +5182,9 @@ var init_git_utils = __esm({
|
|
|
4784
5182
|
});
|
|
4785
5183
|
|
|
4786
5184
|
// src/lib/bump.ts
|
|
4787
|
-
import { readFile as
|
|
4788
|
-
import { join as
|
|
4789
|
-
import { spawnSync as
|
|
5185
|
+
import { readFile as readFile15, writeFile as writeFile12, access as access5, readdir as readdir3 } from "node:fs/promises";
|
|
5186
|
+
import { join as join19, relative as relative5, resolve as resolve3 } from "node:path";
|
|
5187
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
4790
5188
|
function parsePnpmWorkspaceGlobs(raw) {
|
|
4791
5189
|
const lines = raw.split("\n");
|
|
4792
5190
|
const globs = [];
|
|
@@ -4815,18 +5213,18 @@ async function expandGlob(cwd, glob) {
|
|
|
4815
5213
|
if (parts.length !== 2 || parts[1] !== "*") {
|
|
4816
5214
|
return [];
|
|
4817
5215
|
}
|
|
4818
|
-
const parentDir =
|
|
5216
|
+
const parentDir = join19(cwd, parts[0]);
|
|
4819
5217
|
let dirs;
|
|
4820
5218
|
try {
|
|
4821
5219
|
const entries = await readdir3(parentDir, { withFileTypes: true });
|
|
4822
|
-
dirs = entries.filter((e) => e.isDirectory()).map((e) =>
|
|
5220
|
+
dirs = entries.filter((e) => e.isDirectory()).map((e) => join19(parentDir, e.name));
|
|
4823
5221
|
} catch {
|
|
4824
5222
|
return [];
|
|
4825
5223
|
}
|
|
4826
5224
|
const results = [];
|
|
4827
5225
|
for (const dir of dirs) {
|
|
4828
5226
|
try {
|
|
4829
|
-
await
|
|
5227
|
+
await access5(join19(dir, "package.json"));
|
|
4830
5228
|
results.push(dir);
|
|
4831
5229
|
} catch {
|
|
4832
5230
|
}
|
|
@@ -4837,7 +5235,7 @@ async function buildPackageMap(cwd) {
|
|
|
4837
5235
|
const map = /* @__PURE__ */ new Map();
|
|
4838
5236
|
let globs = [];
|
|
4839
5237
|
try {
|
|
4840
|
-
const raw = await
|
|
5238
|
+
const raw = await readFile15(join19(cwd, "pnpm-workspace.yaml"), "utf-8");
|
|
4841
5239
|
globs = parsePnpmWorkspaceGlobs(raw);
|
|
4842
5240
|
} catch {
|
|
4843
5241
|
}
|
|
@@ -4845,7 +5243,7 @@ async function buildPackageMap(cwd) {
|
|
|
4845
5243
|
const dirs = await expandGlob(cwd, glob);
|
|
4846
5244
|
for (const dir of dirs) {
|
|
4847
5245
|
try {
|
|
4848
|
-
const pkgRaw = await
|
|
5246
|
+
const pkgRaw = await readFile15(join19(dir, "package.json"), "utf-8");
|
|
4849
5247
|
const pkg = JSON.parse(pkgRaw);
|
|
4850
5248
|
const name = pkg.name ?? relative5(cwd, dir);
|
|
4851
5249
|
map.set(dir, { name, dir });
|
|
@@ -4894,7 +5292,7 @@ function compareSemver(a, b) {
|
|
|
4894
5292
|
return 0;
|
|
4895
5293
|
}
|
|
4896
5294
|
function gitShowFile(cwd, ref, relPath) {
|
|
4897
|
-
const result =
|
|
5295
|
+
const result = spawnSync5("git", ["show", `${ref}:${relPath}`], {
|
|
4898
5296
|
cwd,
|
|
4899
5297
|
encoding: "utf-8",
|
|
4900
5298
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4935,7 +5333,7 @@ function injectVersion(raw, nextVersion, filePath) {
|
|
|
4935
5333
|
async function prependChangelog(changelogPath, packageName, nextVersion, now, dryRun) {
|
|
4936
5334
|
let existing;
|
|
4937
5335
|
try {
|
|
4938
|
-
existing = await
|
|
5336
|
+
existing = await readFile15(changelogPath, "utf-8");
|
|
4939
5337
|
} catch {
|
|
4940
5338
|
return false;
|
|
4941
5339
|
}
|
|
@@ -4948,7 +5346,7 @@ async function prependChangelog(changelogPath, packageName, nextVersion, now, dr
|
|
|
4948
5346
|
`;
|
|
4949
5347
|
const updated = entry + existing;
|
|
4950
5348
|
if (!dryRun) {
|
|
4951
|
-
await
|
|
5349
|
+
await writeFile12(changelogPath, updated, "utf-8");
|
|
4952
5350
|
}
|
|
4953
5351
|
return true;
|
|
4954
5352
|
}
|
|
@@ -4958,7 +5356,7 @@ async function runBump(opts) {
|
|
|
4958
5356
|
const now = opts?.now ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4959
5357
|
const baseBranch = await readBaseBranch(cwd);
|
|
4960
5358
|
const baseRef = resolveBaseRef(cwd, baseBranch);
|
|
4961
|
-
const diffResult =
|
|
5359
|
+
const diffResult = spawnSync5(
|
|
4962
5360
|
"git",
|
|
4963
5361
|
["diff", "--name-only", `${baseRef}...HEAD`],
|
|
4964
5362
|
{
|
|
@@ -4974,7 +5372,7 @@ async function runBump(opts) {
|
|
|
4974
5372
|
const changedFiles = (diffResult.stdout ?? "").trim().split("\n").filter(Boolean).map((f) => resolve3(cwd, f));
|
|
4975
5373
|
const packageMap = await buildPackageMap(cwd);
|
|
4976
5374
|
const packageDirs = Array.from(packageMap.keys());
|
|
4977
|
-
const rootPkgPath =
|
|
5375
|
+
const rootPkgPath = join19(cwd, "package.json");
|
|
4978
5376
|
const changedPackageDirs = /* @__PURE__ */ new Set();
|
|
4979
5377
|
for (const absFile of changedFiles) {
|
|
4980
5378
|
const owner = findOwningPackage(absFile, packageDirs);
|
|
@@ -4985,28 +5383,28 @@ async function runBump(opts) {
|
|
|
4985
5383
|
const entries = [];
|
|
4986
5384
|
for (const pkgDir of changedPackageDirs) {
|
|
4987
5385
|
const pkgInfo = packageMap.get(pkgDir);
|
|
4988
|
-
const pkgJsonPath =
|
|
5386
|
+
const pkgJsonPath = join19(pkgDir, "package.json");
|
|
4989
5387
|
if (pkgJsonPath === rootPkgPath) continue;
|
|
4990
5388
|
const versionFileCandidates = [
|
|
4991
5389
|
{ abs: pkgJsonPath, rel: relative5(cwd, pkgJsonPath).replace(/\\/g, "/") }
|
|
4992
5390
|
];
|
|
4993
|
-
const tauriConfPath =
|
|
5391
|
+
const tauriConfPath = join19(pkgDir, "src-tauri", "tauri.conf.json");
|
|
4994
5392
|
const tauriRelPath = relative5(cwd, tauriConfPath).replace(/\\/g, "/");
|
|
4995
5393
|
try {
|
|
4996
|
-
await
|
|
5394
|
+
await access5(tauriConfPath);
|
|
4997
5395
|
versionFileCandidates.push({ abs: tauriConfPath, rel: tauriRelPath });
|
|
4998
5396
|
} catch {
|
|
4999
5397
|
}
|
|
5000
|
-
const appJsonPath =
|
|
5398
|
+
const appJsonPath = join19(pkgDir, "app.json");
|
|
5001
5399
|
const appJsonRelPath = relative5(cwd, appJsonPath).replace(/\\/g, "/");
|
|
5002
5400
|
try {
|
|
5003
|
-
await
|
|
5401
|
+
await access5(appJsonPath);
|
|
5004
5402
|
versionFileCandidates.push({ abs: appJsonPath, rel: appJsonRelPath });
|
|
5005
5403
|
} catch {
|
|
5006
5404
|
}
|
|
5007
5405
|
let currentPkgJsonRaw;
|
|
5008
5406
|
try {
|
|
5009
|
-
currentPkgJsonRaw = await
|
|
5407
|
+
currentPkgJsonRaw = await readFile15(pkgJsonPath, "utf-8");
|
|
5010
5408
|
} catch (err) {
|
|
5011
5409
|
console.warn(
|
|
5012
5410
|
`runBump: could not read ${pkgJsonPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -5051,7 +5449,7 @@ async function runBump(opts) {
|
|
|
5051
5449
|
for (const { abs, rel } of versionFileCandidates) {
|
|
5052
5450
|
let raw;
|
|
5053
5451
|
try {
|
|
5054
|
-
raw = await
|
|
5452
|
+
raw = await readFile15(abs, "utf-8");
|
|
5055
5453
|
} catch {
|
|
5056
5454
|
continue;
|
|
5057
5455
|
}
|
|
@@ -5065,11 +5463,11 @@ async function runBump(opts) {
|
|
|
5065
5463
|
}
|
|
5066
5464
|
const updated = injectVersion(raw, nextVersion, abs);
|
|
5067
5465
|
if (!dryRun) {
|
|
5068
|
-
await
|
|
5466
|
+
await writeFile12(abs, updated, "utf-8");
|
|
5069
5467
|
}
|
|
5070
5468
|
updatedVersionFiles.push(rel);
|
|
5071
5469
|
}
|
|
5072
|
-
const changelogPath =
|
|
5470
|
+
const changelogPath = join19(pkgDir, "CHANGELOG.md");
|
|
5073
5471
|
const changelogUpdated = await prependChangelog(
|
|
5074
5472
|
changelogPath,
|
|
5075
5473
|
pkgInfo.name,
|
|
@@ -5190,7 +5588,7 @@ var init_bump2 = __esm({
|
|
|
5190
5588
|
});
|
|
5191
5589
|
|
|
5192
5590
|
// src/lib/ship.ts
|
|
5193
|
-
import { execSync as execSync4, spawnSync as
|
|
5591
|
+
import { execSync as execSync4, spawnSync as spawnSync6 } from "node:child_process";
|
|
5194
5592
|
import { relative as relative6 } from "node:path";
|
|
5195
5593
|
function assertValidBranchName2(branch, label) {
|
|
5196
5594
|
if (!/^[a-zA-Z0-9/_.-]+$/.test(branch)) {
|
|
@@ -5215,7 +5613,7 @@ async function pollChecks(feat, timeoutSeconds, cwd, _sleepMs = (ms) => new Prom
|
|
|
5215
5613
|
let totalGhErrors = 0;
|
|
5216
5614
|
let iteration = 0;
|
|
5217
5615
|
while (Date.now() < deadline) {
|
|
5218
|
-
const ghResult =
|
|
5616
|
+
const ghResult = spawnSync6(
|
|
5219
5617
|
"gh",
|
|
5220
5618
|
["pr", "checks", feat, "--json", "name,state,conclusion"],
|
|
5221
5619
|
{
|
|
@@ -5373,7 +5771,7 @@ ${statusOut.trim()}`
|
|
|
5373
5771
|
...e.changelogUpdated ? [relative6(cwd, e.packageDir).replace(/\\/g, "/") + "/CHANGELOG.md"] : []
|
|
5374
5772
|
]);
|
|
5375
5773
|
if (toStage.length > 0) {
|
|
5376
|
-
const addResult =
|
|
5774
|
+
const addResult = spawnSync6("git", ["add", "--", ...toStage], {
|
|
5377
5775
|
cwd,
|
|
5378
5776
|
encoding: "utf-8",
|
|
5379
5777
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -5383,13 +5781,13 @@ ${statusOut.trim()}`
|
|
|
5383
5781
|
`Failed to stage bump files: ${addResult.stderr?.trim() ?? addResult.error?.message}`
|
|
5384
5782
|
);
|
|
5385
5783
|
}
|
|
5386
|
-
const commitResult =
|
|
5784
|
+
const commitResult = spawnSync6(
|
|
5387
5785
|
"git",
|
|
5388
5786
|
["commit", "-m", "chore(release): bump versions"],
|
|
5389
5787
|
{ cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
5390
5788
|
);
|
|
5391
5789
|
if (commitResult.status !== 0 || commitResult.error) {
|
|
5392
|
-
|
|
5790
|
+
spawnSync6(
|
|
5393
5791
|
"git",
|
|
5394
5792
|
["restore", "--staged", "--worktree", "--", ...toStage],
|
|
5395
5793
|
{
|
|
@@ -5434,7 +5832,7 @@ ${statusOut.trim()}`
|
|
|
5434
5832
|
} else {
|
|
5435
5833
|
createArgs.push("--body", defaultPrBody(feat, base));
|
|
5436
5834
|
}
|
|
5437
|
-
const createResult =
|
|
5835
|
+
const createResult = spawnSync6("gh", createArgs, {
|
|
5438
5836
|
cwd,
|
|
5439
5837
|
encoding: "utf-8",
|
|
5440
5838
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -5465,7 +5863,7 @@ ${statusOut.trim()}`
|
|
|
5465
5863
|
});
|
|
5466
5864
|
let mergeCommit = null;
|
|
5467
5865
|
try {
|
|
5468
|
-
const prViewResult =
|
|
5866
|
+
const prViewResult = spawnSync6(
|
|
5469
5867
|
"gh",
|
|
5470
5868
|
["pr", "view", feat, "--json", "state,mergeCommit"],
|
|
5471
5869
|
{
|
|
@@ -6255,7 +6653,7 @@ __export(version_status_exports, {
|
|
|
6255
6653
|
});
|
|
6256
6654
|
import { execFileSync, execSync as execSync6 } from "node:child_process";
|
|
6257
6655
|
import { existsSync as existsSync4, readFileSync as readFileSync5 } from "node:fs";
|
|
6258
|
-
import { dirname as dirname8, join as
|
|
6656
|
+
import { dirname as dirname8, join as join21 } from "node:path";
|
|
6259
6657
|
function fetchLatestVersion() {
|
|
6260
6658
|
try {
|
|
6261
6659
|
return execFileSync("npm", ["view", "codebyplan", "version"], {
|
|
@@ -6294,9 +6692,9 @@ function detectPackageManager2(gitRoot) {
|
|
|
6294
6692
|
let dir = process.cwd();
|
|
6295
6693
|
const stopAt = gitRoot ?? null;
|
|
6296
6694
|
while (true) {
|
|
6297
|
-
if (existsSync4(
|
|
6298
|
-
if (existsSync4(
|
|
6299
|
-
if (existsSync4(
|
|
6695
|
+
if (existsSync4(join21(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
6696
|
+
if (existsSync4(join21(dir, "yarn.lock"))) return "yarn";
|
|
6697
|
+
if (existsSync4(join21(dir, "package-lock.json"))) return "npm";
|
|
6300
6698
|
if (stopAt !== null && dir === stopAt) break;
|
|
6301
6699
|
const parent = dirname8(dir);
|
|
6302
6700
|
if (parent === dir) break;
|
|
@@ -6316,7 +6714,7 @@ function buildInstallCommand2(pm) {
|
|
|
6316
6714
|
}
|
|
6317
6715
|
async function resolveGuard(gitRoot, currentBranch) {
|
|
6318
6716
|
if (gitRoot !== null) {
|
|
6319
|
-
const canonicalPkgPath =
|
|
6717
|
+
const canonicalPkgPath = join21(
|
|
6320
6718
|
gitRoot,
|
|
6321
6719
|
"packages",
|
|
6322
6720
|
"codebyplan-package",
|
|
@@ -6340,10 +6738,10 @@ async function resolveGuard(gitRoot, currentBranch) {
|
|
|
6340
6738
|
let gitJsonPath;
|
|
6341
6739
|
if (found.path.endsWith("/repo.json")) {
|
|
6342
6740
|
const dir = found.path.slice(0, found.path.lastIndexOf("/"));
|
|
6343
|
-
gitJsonPath =
|
|
6741
|
+
gitJsonPath = join21(dir, "git.json");
|
|
6344
6742
|
} else {
|
|
6345
6743
|
const legacyDir = found.path.slice(0, found.path.lastIndexOf("/"));
|
|
6346
|
-
gitJsonPath =
|
|
6744
|
+
gitJsonPath = join21(legacyDir, ".codebyplan", "git.json");
|
|
6347
6745
|
}
|
|
6348
6746
|
const raw = readFileSync5(gitJsonPath, "utf-8");
|
|
6349
6747
|
const parsed = JSON.parse(raw);
|
|
@@ -6422,8 +6820,8 @@ var upload_e2e_images_exports = {};
|
|
|
6422
6820
|
__export(upload_e2e_images_exports, {
|
|
6423
6821
|
runUploadE2eImagesCommand: () => runUploadE2eImagesCommand
|
|
6424
6822
|
});
|
|
6425
|
-
import { readFile as
|
|
6426
|
-
import { join as
|
|
6823
|
+
import { readFile as readFile16 } from "node:fs/promises";
|
|
6824
|
+
import { join as join22, basename, resolve as resolve5 } from "node:path";
|
|
6427
6825
|
import { execSync as execSync7 } from "node:child_process";
|
|
6428
6826
|
function baseUrl2() {
|
|
6429
6827
|
return (process.env.CODEBYPLAN_API_URL ?? "https://www.codebyplan.com").replace(/\/$/, "");
|
|
@@ -6457,8 +6855,8 @@ function parseArgs(args) {
|
|
|
6457
6855
|
}
|
|
6458
6856
|
async function readE2eConfig(projectPath) {
|
|
6459
6857
|
try {
|
|
6460
|
-
const raw = await
|
|
6461
|
-
|
|
6858
|
+
const raw = await readFile16(
|
|
6859
|
+
join22(projectPath, ".codebyplan", "e2e.json"),
|
|
6462
6860
|
"utf-8"
|
|
6463
6861
|
);
|
|
6464
6862
|
return JSON.parse(raw);
|
|
@@ -6499,7 +6897,7 @@ function collectPngsFromGitDiff(projectPath, frameworkName, frameworkConfig, bas
|
|
|
6499
6897
|
continue;
|
|
6500
6898
|
const isNew = status === "A";
|
|
6501
6899
|
results.push({
|
|
6502
|
-
absolutePath:
|
|
6900
|
+
absolutePath: join22(projectPath, filePath),
|
|
6503
6901
|
filename: basename(filePath),
|
|
6504
6902
|
framework: frameworkName,
|
|
6505
6903
|
is_new: isNew
|
|
@@ -6596,7 +6994,7 @@ async function runUploadE2eImagesCommand(args) {
|
|
|
6596
6994
|
for (const png of allPngs) {
|
|
6597
6995
|
let bytes;
|
|
6598
6996
|
try {
|
|
6599
|
-
bytes = await
|
|
6997
|
+
bytes = await readFile16(png.absolutePath);
|
|
6600
6998
|
} catch {
|
|
6601
6999
|
process.stderr.write(
|
|
6602
7000
|
`upload-e2e-images: could not read file: ${png.absolutePath}
|
|
@@ -6755,19 +7153,19 @@ var init_worktree_port_resolver = __esm({
|
|
|
6755
7153
|
});
|
|
6756
7154
|
|
|
6757
7155
|
// src/lib/migrate-local-config.ts
|
|
6758
|
-
import { mkdir as
|
|
6759
|
-
import { join as
|
|
7156
|
+
import { mkdir as mkdir7, readFile as readFile17, unlink as unlink3, writeFile as writeFile13 } from "node:fs/promises";
|
|
7157
|
+
import { join as join23 } from "node:path";
|
|
6760
7158
|
function legacySharedPath(projectPath) {
|
|
6761
|
-
return
|
|
7159
|
+
return join23(projectPath, ".codebyplan.json");
|
|
6762
7160
|
}
|
|
6763
7161
|
function legacyLocalPath(projectPath) {
|
|
6764
|
-
return
|
|
7162
|
+
return join23(projectPath, ".codebyplan.local.json");
|
|
6765
7163
|
}
|
|
6766
7164
|
function newDirPath(projectPath) {
|
|
6767
|
-
return
|
|
7165
|
+
return join23(projectPath, ".codebyplan");
|
|
6768
7166
|
}
|
|
6769
7167
|
function sentinelPath(projectPath) {
|
|
6770
|
-
return
|
|
7168
|
+
return join23(projectPath, ".codebyplan", "repo.json");
|
|
6771
7169
|
}
|
|
6772
7170
|
async function statSafe(p) {
|
|
6773
7171
|
const { stat: stat2 } = await import("node:fs/promises");
|
|
@@ -6806,7 +7204,7 @@ async function runLocalMigration(projectPath) {
|
|
|
6806
7204
|
}
|
|
6807
7205
|
let legacyRaw;
|
|
6808
7206
|
try {
|
|
6809
|
-
legacyRaw = await
|
|
7207
|
+
legacyRaw = await readFile17(legacySharedPath(projectPath), "utf-8");
|
|
6810
7208
|
} catch {
|
|
6811
7209
|
return {
|
|
6812
7210
|
migrated: true,
|
|
@@ -6833,7 +7231,7 @@ async function runLocalMigration(projectPath) {
|
|
|
6833
7231
|
let deviceId;
|
|
6834
7232
|
let deviceWrittenByHelper = false;
|
|
6835
7233
|
try {
|
|
6836
|
-
const localRaw = await
|
|
7234
|
+
const localRaw = await readFile17(legacyLocalPath(projectPath), "utf-8");
|
|
6837
7235
|
const localParsed = JSON.parse(localRaw);
|
|
6838
7236
|
if (typeof localParsed.device_id === "string") {
|
|
6839
7237
|
deviceId = localParsed.device_id;
|
|
@@ -6841,7 +7239,7 @@ async function runLocalMigration(projectPath) {
|
|
|
6841
7239
|
} catch {
|
|
6842
7240
|
}
|
|
6843
7241
|
try {
|
|
6844
|
-
await
|
|
7242
|
+
await mkdir7(newDirPath(projectPath), { recursive: true });
|
|
6845
7243
|
} catch (err) {
|
|
6846
7244
|
const code = err.code;
|
|
6847
7245
|
if (code === "ENOTDIR" || code === "EEXIST") {
|
|
@@ -6860,8 +7258,8 @@ async function runLocalMigration(projectPath) {
|
|
|
6860
7258
|
if ("repo_id" in cfg) repoJson.repo_id = cfg.repo_id;
|
|
6861
7259
|
if ("organization_id" in cfg) repoJson.organization_id = cfg.organization_id;
|
|
6862
7260
|
if ("project_id" in cfg) repoJson.project_id = cfg.project_id;
|
|
6863
|
-
await
|
|
6864
|
-
|
|
7261
|
+
await writeFile13(
|
|
7262
|
+
join23(projectPath, ".codebyplan", "repo.json"),
|
|
6865
7263
|
JSON.stringify(repoJson, null, 2) + "\n",
|
|
6866
7264
|
"utf-8"
|
|
6867
7265
|
);
|
|
@@ -6873,8 +7271,8 @@ async function runLocalMigration(projectPath) {
|
|
|
6873
7271
|
serverJson.auto_push_enabled = cfg.auto_push_enabled;
|
|
6874
7272
|
if ("port_allocations" in cfg)
|
|
6875
7273
|
serverJson.port_allocations = cfg.port_allocations;
|
|
6876
|
-
await
|
|
6877
|
-
|
|
7274
|
+
await writeFile13(
|
|
7275
|
+
join23(projectPath, ".codebyplan", "server.json"),
|
|
6878
7276
|
JSON.stringify(serverJson, null, 2) + "\n",
|
|
6879
7277
|
"utf-8"
|
|
6880
7278
|
);
|
|
@@ -6882,44 +7280,44 @@ async function runLocalMigration(projectPath) {
|
|
|
6882
7280
|
const gitJson = {};
|
|
6883
7281
|
if ("git_branch" in cfg) gitJson.git_branch = cfg.git_branch;
|
|
6884
7282
|
if ("branch_config" in cfg) gitJson.branch_config = cfg.branch_config;
|
|
6885
|
-
await
|
|
6886
|
-
|
|
7283
|
+
await writeFile13(
|
|
7284
|
+
join23(projectPath, ".codebyplan", "git.json"),
|
|
6887
7285
|
JSON.stringify(gitJson, null, 2) + "\n",
|
|
6888
7286
|
"utf-8"
|
|
6889
7287
|
);
|
|
6890
7288
|
filesChanged.push(".codebyplan/git.json");
|
|
6891
7289
|
const shipmentJson = {};
|
|
6892
7290
|
if ("shipment" in cfg) shipmentJson.shipment = cfg.shipment;
|
|
6893
|
-
await
|
|
6894
|
-
|
|
7291
|
+
await writeFile13(
|
|
7292
|
+
join23(projectPath, ".codebyplan", "shipment.json"),
|
|
6895
7293
|
JSON.stringify(shipmentJson, null, 2) + "\n",
|
|
6896
7294
|
"utf-8"
|
|
6897
7295
|
);
|
|
6898
7296
|
filesChanged.push(".codebyplan/shipment.json");
|
|
6899
7297
|
const vendorJson = {};
|
|
6900
|
-
await
|
|
6901
|
-
|
|
7298
|
+
await writeFile13(
|
|
7299
|
+
join23(projectPath, ".codebyplan", "vendor.json"),
|
|
6902
7300
|
JSON.stringify(vendorJson, null, 2) + "\n",
|
|
6903
7301
|
"utf-8"
|
|
6904
7302
|
);
|
|
6905
7303
|
filesChanged.push(".codebyplan/vendor.json");
|
|
6906
7304
|
const e2eJson = {};
|
|
6907
|
-
await
|
|
6908
|
-
|
|
7305
|
+
await writeFile13(
|
|
7306
|
+
join23(projectPath, ".codebyplan", "e2e.json"),
|
|
6909
7307
|
JSON.stringify(e2eJson, null, 2) + "\n",
|
|
6910
7308
|
"utf-8"
|
|
6911
7309
|
);
|
|
6912
7310
|
filesChanged.push(".codebyplan/e2e.json");
|
|
6913
7311
|
const eslintJson = {};
|
|
6914
|
-
await
|
|
6915
|
-
|
|
7312
|
+
await writeFile13(
|
|
7313
|
+
join23(projectPath, ".codebyplan", "eslint.json"),
|
|
6916
7314
|
JSON.stringify(eslintJson, null, 2) + "\n",
|
|
6917
7315
|
"utf-8"
|
|
6918
7316
|
);
|
|
6919
7317
|
filesChanged.push(".codebyplan/eslint.json");
|
|
6920
7318
|
if (!deviceWrittenByHelper) {
|
|
6921
|
-
await
|
|
6922
|
-
|
|
7319
|
+
await writeFile13(
|
|
7320
|
+
join23(projectPath, ".codebyplan", "device.local.json"),
|
|
6923
7321
|
JSON.stringify({ device_id: deviceId }, null, 2) + "\n",
|
|
6924
7322
|
"utf-8"
|
|
6925
7323
|
);
|
|
@@ -6931,9 +7329,9 @@ async function runLocalMigration(projectPath) {
|
|
|
6931
7329
|
"Migration write incomplete: .codebyplan/repo.json was not persisted. Re-run migration to retry from a clean state."
|
|
6932
7330
|
);
|
|
6933
7331
|
}
|
|
6934
|
-
const gitignorePath =
|
|
7332
|
+
const gitignorePath = join23(projectPath, ".gitignore");
|
|
6935
7333
|
try {
|
|
6936
|
-
const gitignoreContent = await
|
|
7334
|
+
const gitignoreContent = await readFile17(gitignorePath, "utf-8");
|
|
6937
7335
|
const legacyLine = ".codebyplan.local.json";
|
|
6938
7336
|
const newLine = ".codebyplan/device.local.json";
|
|
6939
7337
|
const hasLegacy = gitignoreContent.split("\n").some((l) => l.trimEnd() === legacyLine);
|
|
@@ -6952,18 +7350,18 @@ async function runLocalMigration(projectPath) {
|
|
|
6952
7350
|
updated = gitignoreContent;
|
|
6953
7351
|
}
|
|
6954
7352
|
if (updated !== gitignoreContent) {
|
|
6955
|
-
await
|
|
7353
|
+
await writeFile13(gitignorePath, updated, "utf-8");
|
|
6956
7354
|
filesChanged.push(".gitignore");
|
|
6957
7355
|
}
|
|
6958
7356
|
} catch {
|
|
6959
7357
|
}
|
|
6960
7358
|
try {
|
|
6961
|
-
await
|
|
7359
|
+
await unlink3(legacySharedPath(projectPath));
|
|
6962
7360
|
filesChanged.push(".codebyplan.json (deleted)");
|
|
6963
7361
|
} catch {
|
|
6964
7362
|
}
|
|
6965
7363
|
try {
|
|
6966
|
-
await
|
|
7364
|
+
await unlink3(legacyLocalPath(projectPath));
|
|
6967
7365
|
filesChanged.push(".codebyplan.local.json (deleted)");
|
|
6968
7366
|
} catch {
|
|
6969
7367
|
}
|
|
@@ -6993,8 +7391,8 @@ __export(config_exports, {
|
|
|
6993
7391
|
readVendorConfig: () => readVendorConfig,
|
|
6994
7392
|
runConfig: () => runConfig
|
|
6995
7393
|
});
|
|
6996
|
-
import { mkdir as
|
|
6997
|
-
import { join as
|
|
7394
|
+
import { mkdir as mkdir8, readFile as readFile18, writeFile as writeFile14 } from "node:fs/promises";
|
|
7395
|
+
import { join as join24 } from "node:path";
|
|
6998
7396
|
async function runConfig() {
|
|
6999
7397
|
const flags = parseFlags(3);
|
|
7000
7398
|
const dryRun = hasFlag("dry-run", 3);
|
|
@@ -7027,7 +7425,7 @@ async function runConfig() {
|
|
|
7027
7425
|
console.log("\n Config complete.\n");
|
|
7028
7426
|
}
|
|
7029
7427
|
async function syncConfigToFile(repoId, projectPath, dryRun) {
|
|
7030
|
-
const codebyplanDir =
|
|
7428
|
+
const codebyplanDir = join24(projectPath, ".codebyplan");
|
|
7031
7429
|
const {
|
|
7032
7430
|
resolvedWorktreeId,
|
|
7033
7431
|
portAllocations,
|
|
@@ -7099,7 +7497,7 @@ async function syncConfigToFile(repoId, projectPath, dryRun) {
|
|
|
7099
7497
|
console.log(" Config would be updated (dry-run).");
|
|
7100
7498
|
return;
|
|
7101
7499
|
}
|
|
7102
|
-
await
|
|
7500
|
+
await mkdir8(codebyplanDir, { recursive: true });
|
|
7103
7501
|
const files = [
|
|
7104
7502
|
{ name: "repo.json", payload: repoPayload },
|
|
7105
7503
|
{ name: "server.json", payload: serverPayload },
|
|
@@ -7111,16 +7509,16 @@ async function syncConfigToFile(repoId, projectPath, dryRun) {
|
|
|
7111
7509
|
];
|
|
7112
7510
|
let anyUpdated = false;
|
|
7113
7511
|
for (const { name, payload, createOnly } of files) {
|
|
7114
|
-
const filePath =
|
|
7512
|
+
const filePath = join24(codebyplanDir, name);
|
|
7115
7513
|
const newJson = JSON.stringify(payload, null, 2) + "\n";
|
|
7116
7514
|
let currentJson = "";
|
|
7117
7515
|
try {
|
|
7118
|
-
currentJson = await
|
|
7516
|
+
currentJson = await readFile18(filePath, "utf-8");
|
|
7119
7517
|
} catch {
|
|
7120
7518
|
}
|
|
7121
7519
|
if (createOnly && currentJson !== "") continue;
|
|
7122
7520
|
if (currentJson === newJson) continue;
|
|
7123
|
-
await
|
|
7521
|
+
await writeFile14(filePath, newJson, "utf-8");
|
|
7124
7522
|
console.log(` Updated .codebyplan/${name}`);
|
|
7125
7523
|
anyUpdated = true;
|
|
7126
7524
|
}
|
|
@@ -7130,8 +7528,8 @@ async function syncConfigToFile(repoId, projectPath, dryRun) {
|
|
|
7130
7528
|
}
|
|
7131
7529
|
async function readRepoConfig(projectPath) {
|
|
7132
7530
|
try {
|
|
7133
|
-
const raw = await
|
|
7134
|
-
|
|
7531
|
+
const raw = await readFile18(
|
|
7532
|
+
join24(projectPath, ".codebyplan", "repo.json"),
|
|
7135
7533
|
"utf-8"
|
|
7136
7534
|
);
|
|
7137
7535
|
return JSON.parse(raw);
|
|
@@ -7141,8 +7539,8 @@ async function readRepoConfig(projectPath) {
|
|
|
7141
7539
|
}
|
|
7142
7540
|
async function readServerConfig(projectPath) {
|
|
7143
7541
|
try {
|
|
7144
|
-
const raw = await
|
|
7145
|
-
|
|
7542
|
+
const raw = await readFile18(
|
|
7543
|
+
join24(projectPath, ".codebyplan", "server.json"),
|
|
7146
7544
|
"utf-8"
|
|
7147
7545
|
);
|
|
7148
7546
|
return JSON.parse(raw);
|
|
@@ -7152,8 +7550,8 @@ async function readServerConfig(projectPath) {
|
|
|
7152
7550
|
}
|
|
7153
7551
|
async function readGitConfig(projectPath) {
|
|
7154
7552
|
try {
|
|
7155
|
-
const raw = await
|
|
7156
|
-
|
|
7553
|
+
const raw = await readFile18(
|
|
7554
|
+
join24(projectPath, ".codebyplan", "git.json"),
|
|
7157
7555
|
"utf-8"
|
|
7158
7556
|
);
|
|
7159
7557
|
return JSON.parse(raw);
|
|
@@ -7163,8 +7561,8 @@ async function readGitConfig(projectPath) {
|
|
|
7163
7561
|
}
|
|
7164
7562
|
async function readShipmentConfig(projectPath) {
|
|
7165
7563
|
try {
|
|
7166
|
-
const raw = await
|
|
7167
|
-
|
|
7564
|
+
const raw = await readFile18(
|
|
7565
|
+
join24(projectPath, ".codebyplan", "shipment.json"),
|
|
7168
7566
|
"utf-8"
|
|
7169
7567
|
);
|
|
7170
7568
|
return JSON.parse(raw);
|
|
@@ -7174,8 +7572,8 @@ async function readShipmentConfig(projectPath) {
|
|
|
7174
7572
|
}
|
|
7175
7573
|
async function readVendorConfig(projectPath) {
|
|
7176
7574
|
try {
|
|
7177
|
-
const raw = await
|
|
7178
|
-
|
|
7575
|
+
const raw = await readFile18(
|
|
7576
|
+
join24(projectPath, ".codebyplan", "vendor.json"),
|
|
7179
7577
|
"utf-8"
|
|
7180
7578
|
);
|
|
7181
7579
|
return JSON.parse(raw);
|
|
@@ -7185,8 +7583,8 @@ async function readVendorConfig(projectPath) {
|
|
|
7185
7583
|
}
|
|
7186
7584
|
async function readE2eConfig2(projectPath) {
|
|
7187
7585
|
try {
|
|
7188
|
-
const raw = await
|
|
7189
|
-
|
|
7586
|
+
const raw = await readFile18(
|
|
7587
|
+
join24(projectPath, ".codebyplan", "e2e.json"),
|
|
7190
7588
|
"utf-8"
|
|
7191
7589
|
);
|
|
7192
7590
|
return JSON.parse(raw);
|
|
@@ -7196,8 +7594,8 @@ async function readE2eConfig2(projectPath) {
|
|
|
7196
7594
|
}
|
|
7197
7595
|
async function readServerLocalConfig(projectPath) {
|
|
7198
7596
|
try {
|
|
7199
|
-
const raw = await
|
|
7200
|
-
|
|
7597
|
+
const raw = await readFile18(
|
|
7598
|
+
join24(projectPath, ".codebyplan", "server.local.json"),
|
|
7201
7599
|
"utf-8"
|
|
7202
7600
|
);
|
|
7203
7601
|
return JSON.parse(raw);
|
|
@@ -7252,14 +7650,14 @@ var init_server_detect = __esm({
|
|
|
7252
7650
|
});
|
|
7253
7651
|
|
|
7254
7652
|
// src/lib/port-verify.ts
|
|
7255
|
-
import { readFile as
|
|
7653
|
+
import { readFile as readFile19 } from "node:fs/promises";
|
|
7256
7654
|
async function verifyPorts(projectPath, portAllocations) {
|
|
7257
7655
|
const mismatches = [];
|
|
7258
7656
|
const allocatedPorts = new Set(portAllocations.map((a) => a.port));
|
|
7259
7657
|
const packageJsonPaths = await findPackageJsonFiles(projectPath, projectPath);
|
|
7260
7658
|
for (const pkgPath of packageJsonPaths) {
|
|
7261
7659
|
try {
|
|
7262
|
-
const raw = await
|
|
7660
|
+
const raw = await readFile19(pkgPath, "utf-8");
|
|
7263
7661
|
const pkg = JSON.parse(raw);
|
|
7264
7662
|
const scriptPort = detectPortFromScripts(pkg);
|
|
7265
7663
|
if (scriptPort !== null && !allocatedPorts.has(scriptPort)) {
|
|
@@ -7322,7 +7720,7 @@ async function findUnallocatedApps(projectPath, portAllocations) {
|
|
|
7322
7720
|
}
|
|
7323
7721
|
let pkg;
|
|
7324
7722
|
try {
|
|
7325
|
-
const raw = await
|
|
7723
|
+
const raw = await readFile19(`${app.absPath}/package.json`, "utf-8");
|
|
7326
7724
|
pkg = JSON.parse(raw);
|
|
7327
7725
|
} catch {
|
|
7328
7726
|
continue;
|
|
@@ -7372,8 +7770,8 @@ __export(ports_exports, {
|
|
|
7372
7770
|
parseEnvFile: () => parseEnvFile,
|
|
7373
7771
|
runPorts: () => runPorts
|
|
7374
7772
|
});
|
|
7375
|
-
import { mkdir as
|
|
7376
|
-
import { join as
|
|
7773
|
+
import { mkdir as mkdir9, readFile as readFile20, writeFile as writeFile15 } from "node:fs/promises";
|
|
7774
|
+
import { join as join25 } from "node:path";
|
|
7377
7775
|
async function runPorts() {
|
|
7378
7776
|
const flags = parseFlags(3);
|
|
7379
7777
|
const dryRun = hasFlag("dry-run", 3);
|
|
@@ -7494,12 +7892,12 @@ async function writeServerLocalConfig(repoId, projectPath, dryRun) {
|
|
|
7494
7892
|
// and ServerLocalConfig.port_allocations is typed the same — honest end-to-end.
|
|
7495
7893
|
port_allocations: portAllocations
|
|
7496
7894
|
};
|
|
7497
|
-
const codebyplanDir =
|
|
7498
|
-
const filePath =
|
|
7895
|
+
const codebyplanDir = join25(projectPath, ".codebyplan");
|
|
7896
|
+
const filePath = join25(codebyplanDir, "server.local.json");
|
|
7499
7897
|
const newJson = JSON.stringify(payload, null, 2) + "\n";
|
|
7500
7898
|
let currentJson = "";
|
|
7501
7899
|
try {
|
|
7502
|
-
currentJson = await
|
|
7900
|
+
currentJson = await readFile20(filePath, "utf-8");
|
|
7503
7901
|
} catch {
|
|
7504
7902
|
}
|
|
7505
7903
|
if (currentJson === newJson) {
|
|
@@ -7510,18 +7908,18 @@ async function writeServerLocalConfig(repoId, projectPath, dryRun) {
|
|
|
7510
7908
|
console.log(" Would update .codebyplan/server.local.json (dry-run).");
|
|
7511
7909
|
return;
|
|
7512
7910
|
}
|
|
7513
|
-
await
|
|
7514
|
-
await
|
|
7911
|
+
await mkdir9(codebyplanDir, { recursive: true });
|
|
7912
|
+
await writeFile15(filePath, newJson, "utf-8");
|
|
7515
7913
|
console.log(
|
|
7516
7914
|
` Updated .codebyplan/server.local.json (worktree ${resolvedWorktreeId ?? "\u2014"}, ${portAllocations.length} allocation${portAllocations.length === 1 ? "" : "s"}).`
|
|
7517
7915
|
);
|
|
7518
7916
|
}
|
|
7519
7917
|
async function provisionE2eEnv(projectPath, dryRun) {
|
|
7520
|
-
const relSource =
|
|
7521
|
-
const sourcePath =
|
|
7918
|
+
const relSource = join25("apps", "web", ".env.local");
|
|
7919
|
+
const sourcePath = join25(projectPath, relSource);
|
|
7522
7920
|
let sourceRaw;
|
|
7523
7921
|
try {
|
|
7524
|
-
sourceRaw = await
|
|
7922
|
+
sourceRaw = await readFile20(sourcePath, "utf-8");
|
|
7525
7923
|
} catch {
|
|
7526
7924
|
console.warn(
|
|
7527
7925
|
` Skipped .codebyplan/e2e.env \u2014 source ${relSource} not found.`
|
|
@@ -7550,12 +7948,12 @@ async function provisionE2eEnv(projectPath, dryRun) {
|
|
|
7550
7948
|
);
|
|
7551
7949
|
return;
|
|
7552
7950
|
}
|
|
7553
|
-
const codebyplanDir =
|
|
7554
|
-
const filePath =
|
|
7951
|
+
const codebyplanDir = join25(projectPath, ".codebyplan");
|
|
7952
|
+
const filePath = join25(codebyplanDir, "e2e.env");
|
|
7555
7953
|
const newContent = lines.join("\n") + "\n";
|
|
7556
7954
|
let currentContent = "";
|
|
7557
7955
|
try {
|
|
7558
|
-
currentContent = await
|
|
7956
|
+
currentContent = await readFile20(filePath, "utf-8");
|
|
7559
7957
|
} catch {
|
|
7560
7958
|
}
|
|
7561
7959
|
if (currentContent === newContent) {
|
|
@@ -7566,8 +7964,8 @@ async function provisionE2eEnv(projectPath, dryRun) {
|
|
|
7566
7964
|
console.log(" Would provision .codebyplan/e2e.env (dry-run).");
|
|
7567
7965
|
return;
|
|
7568
7966
|
}
|
|
7569
|
-
await
|
|
7570
|
-
await
|
|
7967
|
+
await mkdir9(codebyplanDir, { recursive: true });
|
|
7968
|
+
await writeFile15(filePath, newContent, "utf-8");
|
|
7571
7969
|
console.log(
|
|
7572
7970
|
` Provisioned .codebyplan/e2e.env (${lines.length} var${lines.length === 1 ? "" : "s"}).`
|
|
7573
7971
|
);
|
|
@@ -8414,6 +8812,11 @@ async function runUpdate(opts, deps = {}) {
|
|
|
8414
8812
|
if (opts.renderer && !opts.dryRun) {
|
|
8415
8813
|
await writeStatuslineLocalConfig(projectDir, { renderer: opts.renderer });
|
|
8416
8814
|
}
|
|
8815
|
+
try {
|
|
8816
|
+
const { runLspFull: runLspFull2 } = await Promise.resolve().then(() => (init_lsp(), lsp_exports));
|
|
8817
|
+
await runLspFull2(projectDir, { dryRun: opts.dryRun });
|
|
8818
|
+
} catch {
|
|
8819
|
+
}
|
|
8417
8820
|
} catch (err) {
|
|
8418
8821
|
console.error(
|
|
8419
8822
|
`codebyplan claude update failed: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -8886,6 +9289,11 @@ void (async () => {
|
|
|
8886
9289
|
await runTechStack2();
|
|
8887
9290
|
process.exit(0);
|
|
8888
9291
|
}
|
|
9292
|
+
if (arg === "lsp") {
|
|
9293
|
+
const { runLsp: runLsp2 } = await Promise.resolve().then(() => (init_lsp(), lsp_exports));
|
|
9294
|
+
await runLsp2();
|
|
9295
|
+
process.exit(0);
|
|
9296
|
+
}
|
|
8889
9297
|
if (arg === "claude") {
|
|
8890
9298
|
const subcommand = process.argv[3];
|
|
8891
9299
|
const flagArgs = process.argv.slice(3);
|
|
@@ -8972,6 +9380,7 @@ void (async () => {
|
|
|
8972
9380
|
codebyplan tech-stack Detect and sync tech stack dependencies
|
|
8973
9381
|
(--full-tech-stack: sync every local worktree on this device)
|
|
8974
9382
|
codebyplan eslint ESLint config management (init)
|
|
9383
|
+
codebyplan lsp Detect tech stack and enable LSP plugins in Claude Code
|
|
8975
9384
|
codebyplan round sync-approvals Sync git diff and approvals with round/task state
|
|
8976
9385
|
codebyplan bump Detect changed packages and patch-bump versions
|
|
8977
9386
|
codebyplan ship Ship current feat branch to production via PR
|