@sightmap/sightmap 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +826 -136
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -1498,10 +1498,10 @@ async function processFile(absPath, relPath, opts, fileResults, diagnostics) {
|
|
|
1498
1498
|
}
|
|
1499
1499
|
|
|
1500
1500
|
// src/cli/init/index.ts
|
|
1501
|
-
import { resolve as
|
|
1502
|
-
import { mkdir as mkdir4, readFile as
|
|
1501
|
+
import { resolve as resolve22 } from "path";
|
|
1502
|
+
import { mkdir as mkdir4, readFile as readFile14, rm, writeFile as writeFile7 } from "fs/promises";
|
|
1503
1503
|
import { tmpdir } from "os";
|
|
1504
|
-
import * as
|
|
1504
|
+
import * as clack3 from "@clack/prompts";
|
|
1505
1505
|
|
|
1506
1506
|
// src/cli/init/detect/framework.ts
|
|
1507
1507
|
import { readFile as readFile4, access } from "fs/promises";
|
|
@@ -1625,9 +1625,220 @@ async function detectSightmapDir(cwd) {
|
|
|
1625
1625
|
return { present: true, viewCount: yamls.length };
|
|
1626
1626
|
}
|
|
1627
1627
|
|
|
1628
|
+
// src/cli/init/detect/workspaces.ts
|
|
1629
|
+
import { readFile as readFile6, readdir as readdir5, stat as stat3 } from "fs/promises";
|
|
1630
|
+
import { resolve as resolve15, relative as relative6 } from "path";
|
|
1631
|
+
import { parse as parseYaml } from "yaml";
|
|
1632
|
+
async function exists3(p) {
|
|
1633
|
+
try {
|
|
1634
|
+
await stat3(p);
|
|
1635
|
+
return true;
|
|
1636
|
+
} catch {
|
|
1637
|
+
return false;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
async function readWorkspacePatterns(root) {
|
|
1641
|
+
const patterns = [];
|
|
1642
|
+
const pnpmWs = resolve15(root, "pnpm-workspace.yaml");
|
|
1643
|
+
if (await exists3(pnpmWs)) {
|
|
1644
|
+
try {
|
|
1645
|
+
const parsed = parseYaml(await readFile6(pnpmWs, "utf8"));
|
|
1646
|
+
const list = parsed && Array.isArray(parsed.packages) ? parsed.packages : [];
|
|
1647
|
+
for (const p of list) {
|
|
1648
|
+
if (typeof p === "string") patterns.push(p);
|
|
1649
|
+
}
|
|
1650
|
+
} catch {
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
const pkgJson = resolve15(root, "package.json");
|
|
1654
|
+
if (await exists3(pkgJson)) {
|
|
1655
|
+
try {
|
|
1656
|
+
const pkg = JSON.parse(await readFile6(pkgJson, "utf8"));
|
|
1657
|
+
const ws = pkg.workspaces;
|
|
1658
|
+
if (Array.isArray(ws)) patterns.push(...ws);
|
|
1659
|
+
else if (ws && Array.isArray(ws.packages)) patterns.push(...ws.packages);
|
|
1660
|
+
} catch {
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
return patterns;
|
|
1664
|
+
}
|
|
1665
|
+
async function expandPattern(root, pattern) {
|
|
1666
|
+
const trimmed = pattern.replace(/^\.\//, "").replace(/\/$/, "");
|
|
1667
|
+
if (trimmed.startsWith("!")) return [];
|
|
1668
|
+
if (trimmed.includes("**")) return expandDoubleStar(root, trimmed);
|
|
1669
|
+
const segments = trimmed.split("/");
|
|
1670
|
+
return expandSegments(root, segments, 0, root);
|
|
1671
|
+
}
|
|
1672
|
+
async function expandSegments(root, segs, idx, current) {
|
|
1673
|
+
if (idx >= segs.length) {
|
|
1674
|
+
if (await isPackageDir(current)) return [current];
|
|
1675
|
+
return [];
|
|
1676
|
+
}
|
|
1677
|
+
const seg = segs[idx];
|
|
1678
|
+
if (seg === void 0) return [];
|
|
1679
|
+
if (seg === "*") {
|
|
1680
|
+
let entries = [];
|
|
1681
|
+
try {
|
|
1682
|
+
entries = await readdir5(current);
|
|
1683
|
+
} catch {
|
|
1684
|
+
return [];
|
|
1685
|
+
}
|
|
1686
|
+
const out = [];
|
|
1687
|
+
for (const name of entries) {
|
|
1688
|
+
if (name.startsWith(".")) continue;
|
|
1689
|
+
const child = resolve15(current, name);
|
|
1690
|
+
let s;
|
|
1691
|
+
try {
|
|
1692
|
+
s = await stat3(child);
|
|
1693
|
+
} catch {
|
|
1694
|
+
continue;
|
|
1695
|
+
}
|
|
1696
|
+
if (!s.isDirectory()) continue;
|
|
1697
|
+
out.push(...await expandSegments(root, segs, idx + 1, child));
|
|
1698
|
+
}
|
|
1699
|
+
return out;
|
|
1700
|
+
}
|
|
1701
|
+
return expandSegments(root, segs, idx + 1, resolve15(current, seg));
|
|
1702
|
+
}
|
|
1703
|
+
async function expandDoubleStar(root, pattern) {
|
|
1704
|
+
const prefix = (pattern.split("**")[0] ?? "").replace(/\/$/, "");
|
|
1705
|
+
const base = prefix ? resolve15(root, prefix) : root;
|
|
1706
|
+
const out = [];
|
|
1707
|
+
await walk2(base, 0, 5, out);
|
|
1708
|
+
return out;
|
|
1709
|
+
}
|
|
1710
|
+
async function walk2(dir, depth, maxDepth, out) {
|
|
1711
|
+
if (depth > maxDepth) return;
|
|
1712
|
+
if (await isPackageDir(dir)) out.push(dir);
|
|
1713
|
+
let entries = [];
|
|
1714
|
+
try {
|
|
1715
|
+
entries = await readdir5(dir);
|
|
1716
|
+
} catch {
|
|
1717
|
+
return;
|
|
1718
|
+
}
|
|
1719
|
+
for (const name of entries) {
|
|
1720
|
+
if (name.startsWith(".") || name === "node_modules") continue;
|
|
1721
|
+
const child = resolve15(dir, name);
|
|
1722
|
+
let s;
|
|
1723
|
+
try {
|
|
1724
|
+
s = await stat3(child);
|
|
1725
|
+
} catch {
|
|
1726
|
+
continue;
|
|
1727
|
+
}
|
|
1728
|
+
if (s.isDirectory()) await walk2(child, depth + 1, maxDepth, out);
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
async function isPackageDir(dir) {
|
|
1732
|
+
return exists3(resolve15(dir, "package.json"));
|
|
1733
|
+
}
|
|
1734
|
+
async function enumerateWorkspaces(opts) {
|
|
1735
|
+
const { root } = opts;
|
|
1736
|
+
const patterns = await readWorkspacePatterns(root);
|
|
1737
|
+
if (patterns.length === 0) return [];
|
|
1738
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
1739
|
+
for (const pat of patterns) {
|
|
1740
|
+
for (const d of await expandPattern(root, pat)) dirs.add(d);
|
|
1741
|
+
}
|
|
1742
|
+
const result = [];
|
|
1743
|
+
for (const dir of dirs) {
|
|
1744
|
+
if (dir === root) continue;
|
|
1745
|
+
const framework = await detectFramework(dir);
|
|
1746
|
+
result.push({ dir, relPath: relative6(root, dir) || ".", framework });
|
|
1747
|
+
}
|
|
1748
|
+
result.sort((a, b) => {
|
|
1749
|
+
const ak = a.framework === "unknown" ? 1 : 0;
|
|
1750
|
+
const bk = b.framework === "unknown" ? 1 : 0;
|
|
1751
|
+
if (ak !== bk) return ak - bk;
|
|
1752
|
+
return a.relPath.localeCompare(b.relPath);
|
|
1753
|
+
});
|
|
1754
|
+
return result;
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1628
1757
|
// src/cli/init/tui.ts
|
|
1629
1758
|
import * as clack from "@clack/prompts";
|
|
1630
1759
|
import pc from "picocolors";
|
|
1760
|
+
|
|
1761
|
+
// src/cli/init/framework-setup/provider-snippet.ts
|
|
1762
|
+
import { relative as relative7 } from "path";
|
|
1763
|
+
var IMPORT_LINE = `import { SightmapProvider } from '@sightmap/react';`;
|
|
1764
|
+
function renderProviderSnippet(opts) {
|
|
1765
|
+
const relPath = relative7(opts.cwd, opts.entryAbsPath) || opts.entryAbsPath;
|
|
1766
|
+
switch (opts.framework) {
|
|
1767
|
+
case "react-vite":
|
|
1768
|
+
case "react-cra":
|
|
1769
|
+
return {
|
|
1770
|
+
relPath,
|
|
1771
|
+
importLine: IMPORT_LINE,
|
|
1772
|
+
instruction: "Wrap your render root with <SightmapProvider>:",
|
|
1773
|
+
example: `ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
1774
|
+
<SightmapProvider>
|
|
1775
|
+
<App />
|
|
1776
|
+
</SightmapProvider>,
|
|
1777
|
+
);`
|
|
1778
|
+
};
|
|
1779
|
+
case "next-app":
|
|
1780
|
+
return {
|
|
1781
|
+
relPath,
|
|
1782
|
+
importLine: IMPORT_LINE,
|
|
1783
|
+
instruction: "Wrap the layout's {children} with <SightmapProvider>:",
|
|
1784
|
+
example: `export default function RootLayout({ children }) {
|
|
1785
|
+
return (
|
|
1786
|
+
<html>
|
|
1787
|
+
<body>
|
|
1788
|
+
<SightmapProvider>{children}</SightmapProvider>
|
|
1789
|
+
</body>
|
|
1790
|
+
</html>
|
|
1791
|
+
);
|
|
1792
|
+
}`
|
|
1793
|
+
};
|
|
1794
|
+
case "next-pages":
|
|
1795
|
+
return {
|
|
1796
|
+
relPath,
|
|
1797
|
+
importLine: IMPORT_LINE,
|
|
1798
|
+
instruction: "Wrap <Component {...pageProps} /> with <SightmapProvider>:",
|
|
1799
|
+
example: `export default function App({ Component, pageProps }) {
|
|
1800
|
+
return (
|
|
1801
|
+
<SightmapProvider>
|
|
1802
|
+
<Component {...pageProps} />
|
|
1803
|
+
</SightmapProvider>
|
|
1804
|
+
);
|
|
1805
|
+
}`
|
|
1806
|
+
};
|
|
1807
|
+
case "react-router-7":
|
|
1808
|
+
return {
|
|
1809
|
+
relPath,
|
|
1810
|
+
importLine: IMPORT_LINE,
|
|
1811
|
+
instruction: "Wrap the default export's returned JSX with <SightmapProvider>:",
|
|
1812
|
+
example: `export default function Root() {
|
|
1813
|
+
return (
|
|
1814
|
+
<SightmapProvider>
|
|
1815
|
+
<Outlet />
|
|
1816
|
+
</SightmapProvider>
|
|
1817
|
+
);
|
|
1818
|
+
}`
|
|
1819
|
+
};
|
|
1820
|
+
case "unknown":
|
|
1821
|
+
return {
|
|
1822
|
+
relPath,
|
|
1823
|
+
importLine: IMPORT_LINE,
|
|
1824
|
+
instruction: "Wrap your application's root component with <SightmapProvider>.",
|
|
1825
|
+
example: `<SightmapProvider>{/* your app */}</SightmapProvider>`
|
|
1826
|
+
};
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
function formatSnippet(snippet) {
|
|
1830
|
+
return [
|
|
1831
|
+
`In ${snippet.relPath}:`,
|
|
1832
|
+
"",
|
|
1833
|
+
` ${snippet.importLine}`,
|
|
1834
|
+
"",
|
|
1835
|
+
snippet.instruction,
|
|
1836
|
+
"",
|
|
1837
|
+
...snippet.example.split("\n").map((l) => ` ${l}`)
|
|
1838
|
+
].join("\n");
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
// src/cli/init/tui.ts
|
|
1631
1842
|
var FRAMEWORK_LABELS = {
|
|
1632
1843
|
"react-vite": "React (Vite)",
|
|
1633
1844
|
"react-cra": "React (CRA)",
|
|
@@ -1642,11 +1853,23 @@ var HOST_LABELS = {
|
|
|
1642
1853
|
cursor: "Cursor",
|
|
1643
1854
|
opencode: "OpenCode"
|
|
1644
1855
|
};
|
|
1856
|
+
function row(label, value) {
|
|
1857
|
+
return `${pc.cyan(label)} ${pc.white(value)}`;
|
|
1858
|
+
}
|
|
1645
1859
|
function formatDetectionSummary(d) {
|
|
1646
|
-
const fw =
|
|
1647
|
-
const hosts =
|
|
1648
|
-
|
|
1649
|
-
|
|
1860
|
+
const fw = row("Framework:", FRAMEWORK_LABELS[d.framework]);
|
|
1861
|
+
const hosts = row(
|
|
1862
|
+
"Coding agents:",
|
|
1863
|
+
d.hosts.length > 0 ? d.hosts.map((h) => HOST_LABELS[h]).join(", ") : "none detected"
|
|
1864
|
+
);
|
|
1865
|
+
const vendor = row(
|
|
1866
|
+
"Browser MCP:",
|
|
1867
|
+
d.sightmapAwareVendor ? "Sightmap-aware vendor detected" : "none detected"
|
|
1868
|
+
);
|
|
1869
|
+
const dir = row(
|
|
1870
|
+
".sightmap/:",
|
|
1871
|
+
d.sightmapDir.present ? `present (${d.sightmapDir.viewCount ?? 0} views)` : "not present"
|
|
1872
|
+
);
|
|
1650
1873
|
return [fw, hosts, vendor, dir];
|
|
1651
1874
|
}
|
|
1652
1875
|
function intro2() {
|
|
@@ -1656,7 +1879,49 @@ function outro2(message) {
|
|
|
1656
1879
|
clack.outro(pc.green(message));
|
|
1657
1880
|
}
|
|
1658
1881
|
function showDetection(d) {
|
|
1659
|
-
clack.
|
|
1882
|
+
clack.log.message(
|
|
1883
|
+
[pc.bold("Detected"), "", ...formatDetectionSummary(d)].join("\n"),
|
|
1884
|
+
{ symbol: pc.cyan("\u25C7") }
|
|
1885
|
+
);
|
|
1886
|
+
}
|
|
1887
|
+
function showPluginInstructions(hosts) {
|
|
1888
|
+
const lines = [pc.bold("Next: run these to finish setup"), ""];
|
|
1889
|
+
for (let i = 0; i < hosts.length; i++) {
|
|
1890
|
+
const h = hosts[i];
|
|
1891
|
+
lines.push(...pluginSection(h));
|
|
1892
|
+
if (i < hosts.length - 1) lines.push("");
|
|
1893
|
+
}
|
|
1894
|
+
lines.push("", pc.dim("Then restart your agent."));
|
|
1895
|
+
clack.log.message(lines.join("\n"), { symbol: pc.cyan("\u25C7") });
|
|
1896
|
+
}
|
|
1897
|
+
function pluginSection(host) {
|
|
1898
|
+
switch (host) {
|
|
1899
|
+
case "claude-code":
|
|
1900
|
+
return [
|
|
1901
|
+
pc.cyan("Claude Code"),
|
|
1902
|
+
pc.white(" /plugin marketplace add sightmap/plugin"),
|
|
1903
|
+
pc.white(" /plugin install sightmap@sightmap-marketplace")
|
|
1904
|
+
];
|
|
1905
|
+
case "codex":
|
|
1906
|
+
return [
|
|
1907
|
+
pc.cyan("Codex"),
|
|
1908
|
+
pc.white(" codex plugin marketplace add sightmap/plugin"),
|
|
1909
|
+
pc.white(" codex plugin install sightmap@sightmap-marketplace")
|
|
1910
|
+
];
|
|
1911
|
+
case "cursor":
|
|
1912
|
+
return [
|
|
1913
|
+
pc.cyan("Cursor"),
|
|
1914
|
+
pc.white(" /plugin add sightmap/plugin"),
|
|
1915
|
+
pc.dim(" (in the Cursor command palette)")
|
|
1916
|
+
];
|
|
1917
|
+
case "opencode":
|
|
1918
|
+
return [
|
|
1919
|
+
pc.cyan("OpenCode"),
|
|
1920
|
+
pc.white(" Add to opencode.json plugin array:"),
|
|
1921
|
+
pc.white(' "plugin": ["sightmap@git+https://github.com/sightmap/plugin"]'),
|
|
1922
|
+
pc.dim(" Then restart OpenCode.")
|
|
1923
|
+
];
|
|
1924
|
+
}
|
|
1660
1925
|
}
|
|
1661
1926
|
async function selectInstallPath(suggestion = "plugin") {
|
|
1662
1927
|
return clack.select({
|
|
@@ -1676,6 +1941,40 @@ async function selectInstallPath(suggestion = "plugin") {
|
|
|
1676
1941
|
]
|
|
1677
1942
|
});
|
|
1678
1943
|
}
|
|
1944
|
+
async function selectWorkspace(workspaces) {
|
|
1945
|
+
const options = workspaces.map((w) => ({
|
|
1946
|
+
value: w,
|
|
1947
|
+
label: w.relPath,
|
|
1948
|
+
hint: w.framework === "unknown" ? pc.dim("no framework") : FRAMEWORK_LABELS[w.framework]
|
|
1949
|
+
}));
|
|
1950
|
+
return clack.select({
|
|
1951
|
+
message: "Multiple workspaces found. Which one should we set up?",
|
|
1952
|
+
options,
|
|
1953
|
+
initialValue: workspaces[0]
|
|
1954
|
+
});
|
|
1955
|
+
}
|
|
1956
|
+
async function selectProviderAction(entryRelPath) {
|
|
1957
|
+
const options = [
|
|
1958
|
+
{ value: "auto", label: "Wrap automatically", hint: "Edit the file in place via codemod." },
|
|
1959
|
+
{ value: "snippet", label: "Show me the snippet to paste", hint: "Print the exact lines; I'll add them myself." },
|
|
1960
|
+
{ value: "skip", label: "Skip", hint: "Already done, or I'll handle it later." }
|
|
1961
|
+
];
|
|
1962
|
+
return clack.select({
|
|
1963
|
+
message: `Add <SightmapProvider> to ${entryRelPath}?`,
|
|
1964
|
+
initialValue: "auto",
|
|
1965
|
+
options
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
function showProviderSnippet(snippet, note) {
|
|
1969
|
+
const header = pc.bold("Add <SightmapProvider> manually");
|
|
1970
|
+
const body = formatSnippet(snippet);
|
|
1971
|
+
const trailer = note ? `
|
|
1972
|
+
|
|
1973
|
+
${pc.dim(note)}` : "";
|
|
1974
|
+
clack.log.message(`${header}
|
|
1975
|
+
|
|
1976
|
+
${body}${trailer}`, { symbol: pc.cyan("\u25C7") });
|
|
1977
|
+
}
|
|
1679
1978
|
async function selectHosts(detected, defaults) {
|
|
1680
1979
|
const options = ["claude-code", "codex", "cursor", "opencode"].map((h) => ({
|
|
1681
1980
|
value: h,
|
|
@@ -1690,18 +1989,18 @@ async function selectHosts(detected, defaults) {
|
|
|
1690
1989
|
}
|
|
1691
1990
|
|
|
1692
1991
|
// src/cli/init/mcp-config/claude-code.ts
|
|
1693
|
-
import { readFile as
|
|
1694
|
-
import { resolve as
|
|
1992
|
+
import { readFile as readFile7, writeFile as writeFile2, access as access5 } from "fs/promises";
|
|
1993
|
+
import { resolve as resolve16 } from "path";
|
|
1695
1994
|
async function readJsonIfExists2(path) {
|
|
1696
1995
|
try {
|
|
1697
1996
|
await access5(path);
|
|
1698
|
-
return JSON.parse(await
|
|
1997
|
+
return JSON.parse(await readFile7(path, "utf8"));
|
|
1699
1998
|
} catch {
|
|
1700
1999
|
return null;
|
|
1701
2000
|
}
|
|
1702
2001
|
}
|
|
1703
2002
|
async function writeClaudeCodeMcp(opts) {
|
|
1704
|
-
const path =
|
|
2003
|
+
const path = resolve16(opts.cwd, ".mcp.json");
|
|
1705
2004
|
const existing = await readJsonIfExists2(path) ?? {};
|
|
1706
2005
|
const servers = existing.mcpServers ?? {};
|
|
1707
2006
|
const next = {
|
|
@@ -1715,14 +2014,14 @@ async function writeClaudeCodeMcp(opts) {
|
|
|
1715
2014
|
}
|
|
1716
2015
|
|
|
1717
2016
|
// src/cli/init/mcp-config/cursor.ts
|
|
1718
|
-
import { readFile as
|
|
1719
|
-
import { resolve as
|
|
2017
|
+
import { readFile as readFile8, writeFile as writeFile3, access as access6, mkdir } from "fs/promises";
|
|
2018
|
+
import { resolve as resolve17, dirname as dirname2 } from "path";
|
|
1720
2019
|
async function writeCursorMcp(opts) {
|
|
1721
|
-
const path =
|
|
2020
|
+
const path = resolve17(opts.cwd, ".cursor/mcp.json");
|
|
1722
2021
|
let existing = {};
|
|
1723
2022
|
try {
|
|
1724
2023
|
await access6(path);
|
|
1725
|
-
existing = JSON.parse(await
|
|
2024
|
+
existing = JSON.parse(await readFile8(path, "utf8"));
|
|
1726
2025
|
} catch {
|
|
1727
2026
|
await mkdir(dirname2(path), { recursive: true });
|
|
1728
2027
|
}
|
|
@@ -1735,14 +2034,14 @@ async function writeCursorMcp(opts) {
|
|
|
1735
2034
|
}
|
|
1736
2035
|
|
|
1737
2036
|
// src/cli/init/mcp-config/opencode.ts
|
|
1738
|
-
import { readFile as
|
|
1739
|
-
import { resolve as
|
|
2037
|
+
import { readFile as readFile9, writeFile as writeFile4, access as access7 } from "fs/promises";
|
|
2038
|
+
import { resolve as resolve18 } from "path";
|
|
1740
2039
|
async function writeOpenCodeMcp(opts) {
|
|
1741
|
-
const path =
|
|
2040
|
+
const path = resolve18(opts.cwd, "opencode.json");
|
|
1742
2041
|
let existing = {};
|
|
1743
2042
|
try {
|
|
1744
2043
|
await access7(path);
|
|
1745
|
-
existing = JSON.parse(await
|
|
2044
|
+
existing = JSON.parse(await readFile9(path, "utf8"));
|
|
1746
2045
|
} catch {
|
|
1747
2046
|
}
|
|
1748
2047
|
const mcp = existing.mcp ?? {};
|
|
@@ -1754,14 +2053,14 @@ async function writeOpenCodeMcp(opts) {
|
|
|
1754
2053
|
}
|
|
1755
2054
|
|
|
1756
2055
|
// src/cli/init/mcp-config/codex.ts
|
|
1757
|
-
import { readFile as
|
|
2056
|
+
import { readFile as readFile10, writeFile as writeFile5, access as access8, mkdir as mkdir2 } from "fs/promises";
|
|
1758
2057
|
import { dirname as dirname3 } from "path";
|
|
1759
2058
|
import { parse as parse2, stringify } from "smol-toml";
|
|
1760
2059
|
async function writeCodexMcp(opts) {
|
|
1761
2060
|
let existing = {};
|
|
1762
2061
|
try {
|
|
1763
2062
|
await access8(opts.configPath);
|
|
1764
|
-
existing = parse2(await
|
|
2063
|
+
existing = parse2(await readFile10(opts.configPath, "utf8"));
|
|
1765
2064
|
} catch {
|
|
1766
2065
|
await mkdir2(dirname3(opts.configPath), { recursive: true });
|
|
1767
2066
|
}
|
|
@@ -1778,13 +2077,13 @@ async function writeCodexMcp(opts) {
|
|
|
1778
2077
|
|
|
1779
2078
|
// src/cli/init/tarball.ts
|
|
1780
2079
|
import pacote from "pacote";
|
|
1781
|
-
import { readFile as
|
|
1782
|
-
import { resolve as
|
|
2080
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
2081
|
+
import { resolve as resolve19 } from "path";
|
|
1783
2082
|
async function fetchPluginTarball(opts) {
|
|
1784
2083
|
const spec = `@sightmap/plugin@${opts.version}`;
|
|
1785
2084
|
await pacote.extract(spec, opts.destDir);
|
|
1786
|
-
const manifestPath =
|
|
1787
|
-
const manifest = JSON.parse(await
|
|
2085
|
+
const manifestPath = resolve19(opts.destDir, ".claude-plugin/plugin.json");
|
|
2086
|
+
const manifest = JSON.parse(await readFile11(manifestPath, "utf8"));
|
|
1788
2087
|
if (!manifest.compatibleMcpVersion) {
|
|
1789
2088
|
throw new Error("Plugin tarball missing 'compatibleMcpVersion' in plugin.json");
|
|
1790
2089
|
}
|
|
@@ -1796,56 +2095,148 @@ async function fetchPluginTarball(opts) {
|
|
|
1796
2095
|
}
|
|
1797
2096
|
|
|
1798
2097
|
// src/cli/init/skills-copy.ts
|
|
1799
|
-
import { cp, mkdir as mkdir3, readFile as
|
|
1800
|
-
import { resolve as
|
|
2098
|
+
import { chmod, cp, mkdir as mkdir3, readFile as readFile12, readdir as readdir6, stat as stat4, writeFile as writeFile6, access as access9 } from "fs/promises";
|
|
2099
|
+
import { resolve as resolve20 } from "path";
|
|
2100
|
+
|
|
2101
|
+
// src/cli/init/settings-merge.ts
|
|
2102
|
+
var PLUGIN_ROOT_TOKEN = /\$\{CLAUDE_PLUGIN_ROOT\}/g;
|
|
2103
|
+
function rewriteHookCommands(hooks, replacement) {
|
|
2104
|
+
const out = {};
|
|
2105
|
+
for (const [event, groups] of Object.entries(hooks)) {
|
|
2106
|
+
out[event] = groups.map((g) => ({
|
|
2107
|
+
...g.matcher !== void 0 ? { matcher: g.matcher } : {},
|
|
2108
|
+
hooks: g.hooks.map((h) => ({
|
|
2109
|
+
...h,
|
|
2110
|
+
command: h.command.replace(PLUGIN_ROOT_TOKEN, replacement)
|
|
2111
|
+
}))
|
|
2112
|
+
}));
|
|
2113
|
+
}
|
|
2114
|
+
return out;
|
|
2115
|
+
}
|
|
2116
|
+
function mergeHooksIntoSettings(existing, incoming) {
|
|
2117
|
+
const merged = { ...existing };
|
|
2118
|
+
const mergedHooks = { ...existing.hooks ?? {} };
|
|
2119
|
+
for (const [event, incomingGroups] of Object.entries(incoming)) {
|
|
2120
|
+
const existingGroups = mergedHooks[event] ?? [];
|
|
2121
|
+
const combined = [...existingGroups];
|
|
2122
|
+
for (const g of incomingGroups) {
|
|
2123
|
+
if (!hasGroup(combined, g)) combined.push(g);
|
|
2124
|
+
}
|
|
2125
|
+
mergedHooks[event] = combined;
|
|
2126
|
+
}
|
|
2127
|
+
merged.hooks = mergedHooks;
|
|
2128
|
+
return merged;
|
|
2129
|
+
}
|
|
2130
|
+
function hasGroup(groups, candidate) {
|
|
2131
|
+
return groups.some(
|
|
2132
|
+
(g) => (g.matcher ?? "") === (candidate.matcher ?? "") && g.hooks.length === candidate.hooks.length && g.hooks.every((h, i) => h.command === candidate.hooks[i]?.command)
|
|
2133
|
+
);
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
// src/cli/init/skills-copy.ts
|
|
1801
2137
|
async function copyPluginAssets(opts) {
|
|
1802
2138
|
const targets = resolveTargets(opts.host, opts.projectDir);
|
|
1803
2139
|
await mkdir3(targets.skills, { recursive: true });
|
|
1804
|
-
await cp(
|
|
2140
|
+
await cp(resolve20(opts.tarballDir, "skills"), targets.skills, { recursive: true });
|
|
1805
2141
|
if (targets.agents) {
|
|
1806
2142
|
await mkdir3(targets.agents, { recursive: true });
|
|
1807
|
-
await cp(
|
|
2143
|
+
await cp(resolve20(opts.tarballDir, "agents"), targets.agents, { recursive: true });
|
|
2144
|
+
}
|
|
2145
|
+
if (targets.bin) {
|
|
2146
|
+
await mkdir3(targets.bin, { recursive: true });
|
|
2147
|
+
await cp(resolve20(opts.tarballDir, "bin"), targets.bin, { recursive: true });
|
|
2148
|
+
await chmodShellScripts(targets.bin);
|
|
2149
|
+
}
|
|
2150
|
+
if (targets.settingsJson && targets.bin && targets.binEnvPath) {
|
|
2151
|
+
await installHooksIntoSettings({
|
|
2152
|
+
tarballDir: opts.tarballDir,
|
|
2153
|
+
settingsPath: targets.settingsJson,
|
|
2154
|
+
binEnvPath: targets.binEnvPath
|
|
2155
|
+
});
|
|
2156
|
+
} else if (targets.hooksJson) {
|
|
2157
|
+
await installLegacyHooksJson({
|
|
2158
|
+
tarballDir: opts.tarballDir,
|
|
2159
|
+
dstPath: targets.hooksJson,
|
|
2160
|
+
binEnvPath: targets.binEnvPath ?? `\${CLAUDE_PROJECT_DIR}/.claude/sightmap`
|
|
2161
|
+
});
|
|
1808
2162
|
}
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
2163
|
+
}
|
|
2164
|
+
async function installHooksIntoSettings(opts) {
|
|
2165
|
+
const incomingRaw = JSON.parse(
|
|
2166
|
+
await readFile12(resolve20(opts.tarballDir, "hooks/hooks.json"), "utf8")
|
|
2167
|
+
);
|
|
2168
|
+
const incoming = incomingRaw.hooks ?? {};
|
|
2169
|
+
const rewritten = rewriteHookCommands(incoming, opts.binEnvPath);
|
|
2170
|
+
let existing = {};
|
|
2171
|
+
try {
|
|
2172
|
+
await access9(opts.settingsPath);
|
|
2173
|
+
existing = JSON.parse(await readFile12(opts.settingsPath, "utf8"));
|
|
2174
|
+
} catch {
|
|
2175
|
+
}
|
|
2176
|
+
const merged = mergeHooksIntoSettings(existing, rewritten);
|
|
2177
|
+
await mkdir3(resolve20(opts.settingsPath, ".."), { recursive: true });
|
|
2178
|
+
await writeFile6(opts.settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
2179
|
+
}
|
|
2180
|
+
async function installLegacyHooksJson(opts) {
|
|
2181
|
+
const srcRaw = JSON.parse(
|
|
2182
|
+
await readFile12(resolve20(opts.tarballDir, "hooks/hooks.json"), "utf8")
|
|
2183
|
+
);
|
|
2184
|
+
const rewritten = rewriteHookCommands(srcRaw.hooks ?? {}, opts.binEnvPath);
|
|
2185
|
+
let merged = { hooks: rewritten };
|
|
2186
|
+
try {
|
|
2187
|
+
await access9(opts.dstPath);
|
|
2188
|
+
const existing = JSON.parse(await readFile12(opts.dstPath, "utf8"));
|
|
2189
|
+
merged = { hooks: { ...existing.hooks ?? {}, ...rewritten } };
|
|
2190
|
+
} catch {
|
|
2191
|
+
}
|
|
2192
|
+
await mkdir3(resolve20(opts.dstPath, ".."), { recursive: true });
|
|
2193
|
+
await writeFile6(opts.dstPath, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
2194
|
+
}
|
|
2195
|
+
async function chmodShellScripts(dir) {
|
|
2196
|
+
const entries = await readdir6(dir);
|
|
2197
|
+
for (const name of entries) {
|
|
2198
|
+
const p = resolve20(dir, name);
|
|
2199
|
+
const s = await stat4(p);
|
|
2200
|
+
if (s.isDirectory()) {
|
|
2201
|
+
await chmodShellScripts(p);
|
|
2202
|
+
} else if (name.endsWith(".sh")) {
|
|
2203
|
+
await chmod(p, 493);
|
|
1820
2204
|
}
|
|
1821
|
-
await writeFile6(dstPath, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
1822
2205
|
}
|
|
1823
2206
|
}
|
|
1824
2207
|
function resolveTargets(host, projectDir) {
|
|
1825
2208
|
switch (host) {
|
|
1826
2209
|
case "claude-code":
|
|
1827
2210
|
return {
|
|
1828
|
-
skills:
|
|
1829
|
-
agents:
|
|
1830
|
-
|
|
2211
|
+
skills: resolve20(projectDir, ".claude/skills/sightmap"),
|
|
2212
|
+
agents: resolve20(projectDir, ".claude/agents"),
|
|
2213
|
+
bin: resolve20(projectDir, ".claude/sightmap/bin"),
|
|
2214
|
+
// Stand-in for CLAUDE_PLUGIN_ROOT — tarball commands are written as
|
|
2215
|
+
// ${CLAUDE_PLUGIN_ROOT}/bin/X.sh, so we replace the plugin root, not the bin dir.
|
|
2216
|
+
binEnvPath: "${CLAUDE_PROJECT_DIR}/.claude/sightmap",
|
|
2217
|
+
settingsJson: resolve20(projectDir, ".claude/settings.json")
|
|
1831
2218
|
};
|
|
1832
2219
|
case "codex":
|
|
1833
2220
|
return {
|
|
1834
|
-
skills:
|
|
1835
|
-
agents:
|
|
1836
|
-
|
|
2221
|
+
skills: resolve20(projectDir, ".codex/skills/sightmap"),
|
|
2222
|
+
agents: resolve20(projectDir, ".codex/agents"),
|
|
2223
|
+
bin: resolve20(projectDir, ".codex/sightmap/bin"),
|
|
2224
|
+
binEnvPath: "${CLAUDE_PROJECT_DIR}/.codex/sightmap",
|
|
2225
|
+
hooksJson: resolve20(projectDir, ".codex/hooks.json")
|
|
1837
2226
|
};
|
|
1838
2227
|
case "cursor":
|
|
1839
|
-
return { skills:
|
|
2228
|
+
return { skills: resolve20(projectDir, ".cursor/rules/sightmap") };
|
|
1840
2229
|
case "opencode":
|
|
1841
|
-
return { skills:
|
|
2230
|
+
return { skills: resolve20(projectDir, ".opencode/plugins/sightmap") };
|
|
1842
2231
|
}
|
|
1843
2232
|
}
|
|
1844
2233
|
|
|
1845
2234
|
// src/cli/init/version-pin.ts
|
|
2235
|
+
var DEFAULT_LAUNCHER = { pm: "npm", command: "npx", runArgs: ["-y"] };
|
|
1846
2236
|
function buildSightmapMcpServerDef(opts) {
|
|
2237
|
+
const launcher = opts.launcher ?? DEFAULT_LAUNCHER;
|
|
1847
2238
|
const args = [
|
|
1848
|
-
|
|
2239
|
+
...launcher.runArgs,
|
|
1849
2240
|
`@sightmap/mcp@${opts.compatibleMcpVersion}`,
|
|
1850
2241
|
"--sightmap-dir",
|
|
1851
2242
|
opts.sightmapDir,
|
|
@@ -1853,35 +2244,152 @@ function buildSightmapMcpServerDef(opts) {
|
|
|
1853
2244
|
opts.curateRoot
|
|
1854
2245
|
];
|
|
1855
2246
|
if (opts.withBrowser) {
|
|
1856
|
-
args.push("--",
|
|
2247
|
+
args.push("--", launcher.command, ...launcher.runArgs, "@playwright/mcp@latest");
|
|
1857
2248
|
} else {
|
|
1858
2249
|
args.push("--curate-only");
|
|
1859
2250
|
}
|
|
1860
2251
|
return {
|
|
1861
|
-
command:
|
|
2252
|
+
command: launcher.command,
|
|
1862
2253
|
args,
|
|
1863
2254
|
env: { SIGHTMAP_PLUGIN_VERSION: opts.pluginVersion }
|
|
1864
2255
|
};
|
|
1865
2256
|
}
|
|
1866
2257
|
|
|
1867
|
-
// src/cli/init/
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
2258
|
+
// src/cli/init/smoke-mcp.ts
|
|
2259
|
+
import { spawn } from "child_process";
|
|
2260
|
+
import * as clack2 from "@clack/prompts";
|
|
2261
|
+
import pc2 from "picocolors";
|
|
2262
|
+
var DEFAULT_TIMEOUT_MS = 15e3;
|
|
2263
|
+
var INITIALIZE_PAYLOAD = {
|
|
2264
|
+
jsonrpc: "2.0",
|
|
2265
|
+
id: 1,
|
|
2266
|
+
method: "initialize",
|
|
2267
|
+
params: {
|
|
2268
|
+
protocolVersion: "2024-11-05",
|
|
2269
|
+
capabilities: {},
|
|
2270
|
+
clientInfo: { name: "sightmap-init-smoke", version: "1.0.0" }
|
|
2271
|
+
}
|
|
1873
2272
|
};
|
|
1874
|
-
function
|
|
1875
|
-
const
|
|
1876
|
-
const
|
|
1877
|
-
|
|
1878
|
-
|
|
2273
|
+
async function runSmokeTest(opts) {
|
|
2274
|
+
const startedAt = Date.now();
|
|
2275
|
+
const timeout = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
2276
|
+
return new Promise((resolveResult) => {
|
|
2277
|
+
let settled = false;
|
|
2278
|
+
const settle = (r) => {
|
|
2279
|
+
if (settled) return;
|
|
2280
|
+
settled = true;
|
|
2281
|
+
try {
|
|
2282
|
+
child.kill("SIGTERM");
|
|
2283
|
+
} catch {
|
|
2284
|
+
}
|
|
2285
|
+
resolveResult(r);
|
|
2286
|
+
};
|
|
2287
|
+
const child = spawn(opts.serverDef.command, opts.serverDef.args, {
|
|
2288
|
+
cwd: opts.cwd,
|
|
2289
|
+
env: { ...process.env, ...opts.serverDef.env },
|
|
2290
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2291
|
+
});
|
|
2292
|
+
let stdoutBuf = "";
|
|
2293
|
+
let stderrBuf = "";
|
|
2294
|
+
child.stdout?.on("data", (chunk) => {
|
|
2295
|
+
stdoutBuf += chunk.toString("utf8");
|
|
2296
|
+
let nl;
|
|
2297
|
+
while ((nl = stdoutBuf.indexOf("\n")) !== -1) {
|
|
2298
|
+
const line = stdoutBuf.slice(0, nl).trim();
|
|
2299
|
+
stdoutBuf = stdoutBuf.slice(nl + 1);
|
|
2300
|
+
if (!line) continue;
|
|
2301
|
+
try {
|
|
2302
|
+
const msg = JSON.parse(line);
|
|
2303
|
+
if (msg.id === 1 && msg.result) {
|
|
2304
|
+
settle({ ok: true, latencyMs: Date.now() - startedAt });
|
|
2305
|
+
return;
|
|
2306
|
+
}
|
|
2307
|
+
if (msg.id === 1 && msg.error) {
|
|
2308
|
+
settle({
|
|
2309
|
+
ok: false,
|
|
2310
|
+
reason: `Server returned JSON-RPC error: ${JSON.stringify(msg.error)}`,
|
|
2311
|
+
latencyMs: Date.now() - startedAt
|
|
2312
|
+
});
|
|
2313
|
+
return;
|
|
2314
|
+
}
|
|
2315
|
+
} catch {
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
});
|
|
2319
|
+
child.stderr?.on("data", (chunk) => {
|
|
2320
|
+
stderrBuf += chunk.toString("utf8");
|
|
2321
|
+
});
|
|
2322
|
+
child.on("error", (err) => {
|
|
2323
|
+
settle({
|
|
2324
|
+
ok: false,
|
|
2325
|
+
reason: `Failed to spawn: ${err.message}`,
|
|
2326
|
+
latencyMs: Date.now() - startedAt,
|
|
2327
|
+
...stderrBuf ? { stderr: stderrBuf } : {}
|
|
2328
|
+
});
|
|
2329
|
+
});
|
|
2330
|
+
child.on("exit", (code, signal) => {
|
|
2331
|
+
if (settled) return;
|
|
2332
|
+
settle({
|
|
2333
|
+
ok: false,
|
|
2334
|
+
reason: stdoutBuf.length === 0 && stderrBuf.length === 0 ? `Spawn produced no output and exited (code=${code ?? "?"}, signal=${signal ?? "?"}). This is the silent-stdin bug \u2014 try a different package manager launcher.` : `Process exited before initialize response (code=${code ?? "?"}, signal=${signal ?? "?"})`,
|
|
2335
|
+
latencyMs: Date.now() - startedAt,
|
|
2336
|
+
...stderrBuf ? { stderr: stderrBuf } : {}
|
|
2337
|
+
});
|
|
2338
|
+
});
|
|
2339
|
+
setTimeout(() => {
|
|
2340
|
+
settle({
|
|
2341
|
+
ok: false,
|
|
2342
|
+
reason: `Timed out after ${timeout}ms waiting for initialize response`,
|
|
2343
|
+
latencyMs: Date.now() - startedAt,
|
|
2344
|
+
...stderrBuf ? { stderr: stderrBuf } : {}
|
|
2345
|
+
});
|
|
2346
|
+
}, timeout);
|
|
2347
|
+
try {
|
|
2348
|
+
child.stdin?.write(JSON.stringify(INITIALIZE_PAYLOAD) + "\n");
|
|
2349
|
+
child.stdin?.end();
|
|
2350
|
+
} catch (e) {
|
|
2351
|
+
settle({
|
|
2352
|
+
ok: false,
|
|
2353
|
+
reason: `Failed to write initialize payload: ${e.message}`,
|
|
2354
|
+
latencyMs: Date.now() - startedAt
|
|
2355
|
+
});
|
|
2356
|
+
}
|
|
2357
|
+
});
|
|
2358
|
+
}
|
|
2359
|
+
async function smokeTestMcp(opts) {
|
|
2360
|
+
const spinner3 = clack2.spinner();
|
|
2361
|
+
spinner3.start("Verifying MCP can launch");
|
|
2362
|
+
const result = await runSmokeTest(opts);
|
|
2363
|
+
if (result.ok) {
|
|
2364
|
+
spinner3.stop(`MCP ready (initialized in ${result.latencyMs}ms)`);
|
|
2365
|
+
return;
|
|
2366
|
+
}
|
|
2367
|
+
spinner3.stop("MCP smoke test failed");
|
|
2368
|
+
const cmd = `${opts.serverDef.command} ${opts.serverDef.args.join(" ")}`;
|
|
2369
|
+
const lines = [
|
|
2370
|
+
pc2.bold("Could not start the MCP server."),
|
|
2371
|
+
"",
|
|
2372
|
+
pc2.cyan("Reason:") + " " + result.reason,
|
|
2373
|
+
pc2.cyan("Command:") + " " + pc2.white(cmd)
|
|
2374
|
+
];
|
|
2375
|
+
if (result.stderr) {
|
|
2376
|
+
lines.push("", pc2.cyan("stderr:"));
|
|
2377
|
+
for (const ln of result.stderr.trim().split("\n").slice(0, 6)) {
|
|
2378
|
+
lines.push(" " + pc2.dim(ln));
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
lines.push(
|
|
2382
|
+
"",
|
|
2383
|
+
pc2.dim("The config was written. You can retry by restarting your agent."),
|
|
2384
|
+
pc2.dim("Re-run `npx @sightmap/sightmap init --no-smoke` to skip this check next time.")
|
|
2385
|
+
);
|
|
2386
|
+
clack2.log.warn(lines.join("\n"));
|
|
1879
2387
|
}
|
|
1880
2388
|
|
|
1881
2389
|
// src/cli/init/framework-setup/package-manager.ts
|
|
1882
|
-
import { access as access10 } from "fs/promises";
|
|
1883
|
-
import { resolve as
|
|
1884
|
-
async function
|
|
2390
|
+
import { access as access10, readFile as readFile13 } from "fs/promises";
|
|
2391
|
+
import { dirname as dirname4, resolve as resolve21 } from "path";
|
|
2392
|
+
async function exists4(p) {
|
|
1885
2393
|
try {
|
|
1886
2394
|
await access10(p);
|
|
1887
2395
|
return true;
|
|
@@ -1889,22 +2397,75 @@ async function exists3(p) {
|
|
|
1889
2397
|
return false;
|
|
1890
2398
|
}
|
|
1891
2399
|
}
|
|
2400
|
+
var SIGNALS = [
|
|
2401
|
+
// Bun first — bun.lock can coexist with package-lock.json during migration.
|
|
2402
|
+
{ file: "bun.lock", pm: "bun" },
|
|
2403
|
+
{ file: "bun.lockb", pm: "bun" },
|
|
2404
|
+
{ file: "pnpm-lock.yaml", pm: "pnpm" },
|
|
2405
|
+
{ file: "pnpm-workspace.yaml", pm: "pnpm" },
|
|
2406
|
+
{ file: "yarn.lock", pm: "yarn" }
|
|
2407
|
+
];
|
|
1892
2408
|
async function detectPackageManager(cwd) {
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
2409
|
+
let dir = resolve21(cwd);
|
|
2410
|
+
while (true) {
|
|
2411
|
+
for (const { file, pm } of SIGNALS) {
|
|
2412
|
+
if (await exists4(resolve21(dir, file))) return pm;
|
|
2413
|
+
}
|
|
2414
|
+
const parent = dirname4(dir);
|
|
2415
|
+
if (parent === dir) return "npm";
|
|
2416
|
+
dir = parent;
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
async function detectLauncher(cwd) {
|
|
2420
|
+
const pm = await detectPackageManager(cwd);
|
|
2421
|
+
if (pm === "bun") return { pm, command: "bunx", runArgs: [] };
|
|
2422
|
+
if (pm === "pnpm") return { pm, command: "pnpm", runArgs: ["dlx"] };
|
|
2423
|
+
if (pm === "yarn") {
|
|
2424
|
+
if (await isYarnBerry(cwd)) {
|
|
2425
|
+
return { pm, command: "yarn", runArgs: ["dlx"] };
|
|
2426
|
+
}
|
|
2427
|
+
return { pm: "npm", command: "npx", runArgs: ["-y"] };
|
|
2428
|
+
}
|
|
2429
|
+
return { pm: "npm", command: "npx", runArgs: ["-y"] };
|
|
2430
|
+
}
|
|
2431
|
+
async function isYarnBerry(cwd) {
|
|
2432
|
+
let dir = resolve21(cwd);
|
|
2433
|
+
while (true) {
|
|
2434
|
+
if (await exists4(resolve21(dir, ".yarnrc.yml"))) return true;
|
|
2435
|
+
const parent = dirname4(dir);
|
|
2436
|
+
if (parent === dir) break;
|
|
2437
|
+
dir = parent;
|
|
2438
|
+
}
|
|
2439
|
+
dir = resolve21(cwd);
|
|
2440
|
+
while (true) {
|
|
2441
|
+
const pkg = resolve21(dir, "package.json");
|
|
2442
|
+
if (await exists4(pkg)) {
|
|
2443
|
+
try {
|
|
2444
|
+
const parsed = JSON.parse(await readFile13(pkg, "utf8"));
|
|
2445
|
+
const pmField = parsed.packageManager ?? "";
|
|
2446
|
+
const match2 = /^yarn@(\d+)\./.exec(pmField);
|
|
2447
|
+
if (match2 && Number(match2[1]) >= 2) return true;
|
|
2448
|
+
} catch {
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
const parent = dirname4(dir);
|
|
2452
|
+
if (parent === dir) break;
|
|
2453
|
+
dir = parent;
|
|
2454
|
+
}
|
|
2455
|
+
return false;
|
|
1896
2456
|
}
|
|
1897
2457
|
|
|
1898
2458
|
// src/cli/init/framework-setup/install-adapter.ts
|
|
1899
|
-
import { spawn } from "child_process";
|
|
2459
|
+
import { spawn as spawn2 } from "child_process";
|
|
1900
2460
|
var ADD_VERB = {
|
|
1901
2461
|
pnpm: "add",
|
|
1902
2462
|
yarn: "add",
|
|
1903
|
-
npm: "install"
|
|
2463
|
+
npm: "install",
|
|
2464
|
+
bun: "add"
|
|
1904
2465
|
};
|
|
1905
2466
|
async function installAdapter(opts) {
|
|
1906
2467
|
await new Promise((resolveP, reject) => {
|
|
1907
|
-
const child =
|
|
2468
|
+
const child = spawn2(opts.packageManager, [ADD_VERB[opts.packageManager], opts.packageName], {
|
|
1908
2469
|
cwd: opts.cwd,
|
|
1909
2470
|
stdio: "inherit"
|
|
1910
2471
|
});
|
|
@@ -1914,23 +2475,24 @@ async function installAdapter(opts) {
|
|
|
1914
2475
|
}
|
|
1915
2476
|
|
|
1916
2477
|
// src/cli/init/framework-setup/codegen.ts
|
|
1917
|
-
import { spawn as
|
|
2478
|
+
import { spawn as spawn3 } from "child_process";
|
|
1918
2479
|
var REACT_FRAMEWORKS = ["react-vite", "react-cra", "next-app", "next-pages", "react-router-7"];
|
|
1919
2480
|
var EXEC_VERB = {
|
|
1920
2481
|
pnpm: ["exec"],
|
|
1921
2482
|
yarn: ["exec"],
|
|
1922
|
-
npm: ["exec", "--"]
|
|
2483
|
+
npm: ["exec", "--"],
|
|
2484
|
+
bun: ["x"]
|
|
1923
2485
|
};
|
|
1924
2486
|
function buildCodegenCommand(opts) {
|
|
1925
2487
|
if (!REACT_FRAMEWORKS.includes(opts.framework)) return null;
|
|
1926
2488
|
return {
|
|
1927
2489
|
command: opts.packageManager,
|
|
1928
|
-
args: [...EXEC_VERB[opts.packageManager], "sightmap-react", "gen"]
|
|
2490
|
+
args: [...EXEC_VERB[opts.packageManager], "sightmap-react", "gen", "--router=auto"]
|
|
1929
2491
|
};
|
|
1930
2492
|
}
|
|
1931
2493
|
async function runCodegen(cwd, cmd) {
|
|
1932
2494
|
await new Promise((resolveP, reject) => {
|
|
1933
|
-
const child =
|
|
2495
|
+
const child = spawn3(cmd.command, cmd.args, { cwd, stdio: "inherit" });
|
|
1934
2496
|
child.on("error", reject);
|
|
1935
2497
|
child.on("exit", (code) => code === 0 ? resolveP() : reject(new Error(`codegen exited ${code}`)));
|
|
1936
2498
|
});
|
|
@@ -1948,43 +2510,109 @@ var babelParser = {
|
|
|
1948
2510
|
});
|
|
1949
2511
|
}
|
|
1950
2512
|
};
|
|
1951
|
-
function wrapWithSightmapProvider(source) {
|
|
2513
|
+
function wrapWithSightmapProvider(source, opts = {}) {
|
|
1952
2514
|
if (source.includes("SightmapProvider")) {
|
|
1953
2515
|
return { code: source, changed: false, reason: "SightmapProvider already present" };
|
|
1954
2516
|
}
|
|
1955
2517
|
const ast = recast.parse(source, { parser: babelParser });
|
|
1956
2518
|
const b = recast.types.builders;
|
|
1957
|
-
|
|
1958
|
-
|
|
2519
|
+
const wrap = (inner) => b.jsxElement(
|
|
2520
|
+
b.jsxOpeningElement(b.jsxIdentifier("SightmapProvider"), [], false),
|
|
2521
|
+
b.jsxClosingElement(b.jsxIdentifier("SightmapProvider")),
|
|
2522
|
+
[stripPrintMetadata(inner)]
|
|
2523
|
+
);
|
|
2524
|
+
let wrapped = false;
|
|
2525
|
+
let reason;
|
|
1959
2526
|
recast.types.visit(ast, {
|
|
1960
2527
|
visitCallExpression(path) {
|
|
2528
|
+
if (wrapped) return false;
|
|
1961
2529
|
const callee = path.node.callee;
|
|
1962
2530
|
if (callee.type === "MemberExpression" && callee.property.type === "Identifier" && callee.property.name === "render") {
|
|
1963
|
-
|
|
1964
|
-
|
|
2531
|
+
const arg = path.node.arguments[0];
|
|
2532
|
+
if (!arg) {
|
|
2533
|
+
reason = "render() called with no args";
|
|
2534
|
+
return false;
|
|
2535
|
+
}
|
|
2536
|
+
path.node.arguments[0] = wrap(arg);
|
|
2537
|
+
wrapped = true;
|
|
1965
2538
|
return false;
|
|
1966
2539
|
}
|
|
1967
2540
|
this.traverse(path);
|
|
1968
2541
|
}
|
|
1969
2542
|
});
|
|
1970
|
-
if (!
|
|
1971
|
-
|
|
2543
|
+
if (!wrapped && opts.framework === "react-router-7") {
|
|
2544
|
+
wrapped = tryWrapDefaultExport(ast, wrap);
|
|
2545
|
+
if (!wrapped) reason = "no default export returning JSX found in app/root.tsx";
|
|
2546
|
+
}
|
|
2547
|
+
if (!wrapped) {
|
|
2548
|
+
return { code: source, changed: false, reason: reason ?? "no .render() call found" };
|
|
1972
2549
|
}
|
|
1973
|
-
const arg = renderCall.arguments[renderArgIndex];
|
|
1974
|
-
if (!arg) return { code: source, changed: false, reason: "render() called with no args" };
|
|
1975
|
-
const wrapped = b.jsxElement(
|
|
1976
|
-
b.jsxOpeningElement(b.jsxIdentifier("SightmapProvider"), [], false),
|
|
1977
|
-
b.jsxClosingElement(b.jsxIdentifier("SightmapProvider")),
|
|
1978
|
-
[arg]
|
|
1979
|
-
);
|
|
1980
|
-
renderCall.arguments[renderArgIndex] = wrapped;
|
|
1981
2550
|
const importDecl = b.importDeclaration(
|
|
1982
2551
|
[b.importSpecifier(b.identifier("SightmapProvider"), b.identifier("SightmapProvider"))],
|
|
1983
2552
|
b.stringLiteral("@sightmap/react")
|
|
1984
2553
|
);
|
|
1985
|
-
ast
|
|
1986
|
-
|
|
1987
|
-
|
|
2554
|
+
insertImportAfterHeaderComments(ast, importDecl);
|
|
2555
|
+
return { code: recast.print(ast, { quote: "single" }).code, changed: true };
|
|
2556
|
+
}
|
|
2557
|
+
function insertImportAfterHeaderComments(ast, importDecl) {
|
|
2558
|
+
const body = ast.program.body;
|
|
2559
|
+
const firstStmt = body[0];
|
|
2560
|
+
if (firstStmt) {
|
|
2561
|
+
const newImp = importDecl;
|
|
2562
|
+
if (Array.isArray(firstStmt.leadingComments) && firstStmt.leadingComments.length > 0) {
|
|
2563
|
+
newImp.leadingComments = firstStmt.leadingComments;
|
|
2564
|
+
firstStmt.leadingComments = [];
|
|
2565
|
+
}
|
|
2566
|
+
if (Array.isArray(firstStmt.comments) && firstStmt.comments.length > 0) {
|
|
2567
|
+
newImp.comments = firstStmt.comments;
|
|
2568
|
+
firstStmt.comments = [];
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
body.unshift(importDecl);
|
|
2572
|
+
}
|
|
2573
|
+
function stripPrintMetadata(node) {
|
|
2574
|
+
const n = node;
|
|
2575
|
+
if (n && typeof n === "object") {
|
|
2576
|
+
try {
|
|
2577
|
+
n.original = void 0;
|
|
2578
|
+
} catch {
|
|
2579
|
+
}
|
|
2580
|
+
try {
|
|
2581
|
+
n.loc = void 0;
|
|
2582
|
+
} catch {
|
|
2583
|
+
}
|
|
2584
|
+
if (n.extra) n.extra.parenthesized = false;
|
|
2585
|
+
}
|
|
2586
|
+
return node;
|
|
2587
|
+
}
|
|
2588
|
+
function tryWrapDefaultExport(ast, wrap) {
|
|
2589
|
+
let done = false;
|
|
2590
|
+
recast.types.visit(ast, {
|
|
2591
|
+
visitExportDefaultDeclaration(path) {
|
|
2592
|
+
if (done) return false;
|
|
2593
|
+
const decl = path.node.declaration;
|
|
2594
|
+
const body = decl.body;
|
|
2595
|
+
if ((decl.type === "FunctionDeclaration" || decl.type === "ArrowFunctionExpression" || decl.type === "FunctionExpression") && body && body.type === "BlockStatement" && Array.isArray(body.body)) {
|
|
2596
|
+
for (const stmt of body.body) {
|
|
2597
|
+
if (stmt.type === "ReturnStatement" && stmt.argument) {
|
|
2598
|
+
const arg = stmt.argument;
|
|
2599
|
+
if (arg.type === "JSXElement" || arg.type === "JSXFragment") {
|
|
2600
|
+
stmt.argument = wrap(stmt.argument);
|
|
2601
|
+
done = true;
|
|
2602
|
+
return false;
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
if (decl.type === "ArrowFunctionExpression" && body && (body.type === "JSXElement" || body.type === "JSXFragment")) {
|
|
2608
|
+
decl.body = wrap(body);
|
|
2609
|
+
done = true;
|
|
2610
|
+
return false;
|
|
2611
|
+
}
|
|
2612
|
+
return false;
|
|
2613
|
+
}
|
|
2614
|
+
});
|
|
2615
|
+
return done;
|
|
1988
2616
|
}
|
|
1989
2617
|
|
|
1990
2618
|
// src/cli/init/existing-corpus.ts
|
|
@@ -2008,7 +2636,7 @@ function formatExistingCorpusReport(input) {
|
|
|
2008
2636
|
var REACT_FRAMEWORKS2 = ["react-vite", "react-cra", "next-app", "next-pages", "react-router-7"];
|
|
2009
2637
|
async function runInit(opts) {
|
|
2010
2638
|
intro2();
|
|
2011
|
-
|
|
2639
|
+
let detect = {
|
|
2012
2640
|
framework: await detectFramework(opts.cwd),
|
|
2013
2641
|
hosts: await detectHarnesses(opts.cwd),
|
|
2014
2642
|
sightmapAwareVendor: false,
|
|
@@ -2016,6 +2644,20 @@ async function runInit(opts) {
|
|
|
2016
2644
|
};
|
|
2017
2645
|
detect.sightmapAwareVendor = await detectSightmapAwareVendor(opts.cwd, detect.hosts);
|
|
2018
2646
|
showDetection(detect);
|
|
2647
|
+
if (detect.framework === "unknown" && !detect.sightmapDir.present) {
|
|
2648
|
+
const chosen = await maybePickWorkspace(opts);
|
|
2649
|
+
if (typeof chosen === "symbol") return 1;
|
|
2650
|
+
if (chosen) {
|
|
2651
|
+
opts = { ...opts, cwd: chosen.dir };
|
|
2652
|
+
clack3.log.info(`Targeting workspace: ${chosen.relPath}`);
|
|
2653
|
+
detect = {
|
|
2654
|
+
framework: await detectFramework(opts.cwd),
|
|
2655
|
+
hosts: detect.hosts,
|
|
2656
|
+
sightmapAwareVendor: detect.sightmapAwareVendor,
|
|
2657
|
+
sightmapDir: await detectSightmapDir(opts.cwd)
|
|
2658
|
+
};
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2019
2661
|
if (detect.sightmapDir.present) {
|
|
2020
2662
|
await runExistingCorpusFlow(opts.cwd, detect.sightmapDir.viewCount ?? 0);
|
|
2021
2663
|
} else {
|
|
@@ -2037,25 +2679,30 @@ async function runInit(opts) {
|
|
|
2037
2679
|
}
|
|
2038
2680
|
const withBrowser = resolveBrowserBundle(opts, detect);
|
|
2039
2681
|
if (installPath === "plugin") {
|
|
2040
|
-
|
|
2682
|
+
showPluginInstructions(hosts);
|
|
2041
2683
|
outro2("Run those, then restart your agent.");
|
|
2042
2684
|
return 0;
|
|
2043
2685
|
}
|
|
2044
|
-
const tarballDir =
|
|
2686
|
+
const tarballDir = resolve22(tmpdir(), `sightmap-plugin-${Date.now()}`);
|
|
2045
2687
|
await mkdir4(tarballDir, { recursive: true });
|
|
2046
2688
|
try {
|
|
2047
2689
|
const tar = await fetchPluginTarball({ destDir: tarballDir, version: "latest" });
|
|
2690
|
+
const launcher = await detectLauncher(opts.cwd);
|
|
2048
2691
|
const serverDef = buildSightmapMcpServerDef({
|
|
2049
2692
|
compatibleMcpVersion: tar.compatibleMcpVersion,
|
|
2050
2693
|
pluginVersion: tar.pluginVersion,
|
|
2051
2694
|
withBrowser,
|
|
2052
2695
|
sightmapDir: ".sightmap",
|
|
2053
|
-
curateRoot: ".sightmap"
|
|
2696
|
+
curateRoot: ".sightmap",
|
|
2697
|
+
launcher
|
|
2054
2698
|
});
|
|
2055
2699
|
for (const h of hosts) {
|
|
2056
2700
|
await writeMcpForHost(h, opts.cwd, serverDef);
|
|
2057
2701
|
await copyPluginAssets({ tarballDir, projectDir: opts.cwd, host: h });
|
|
2058
2702
|
}
|
|
2703
|
+
if (!opts.noSmoke) {
|
|
2704
|
+
await smokeTestMcp({ serverDef, cwd: opts.cwd });
|
|
2705
|
+
}
|
|
2059
2706
|
} finally {
|
|
2060
2707
|
await rm(tarballDir, { recursive: true, force: true });
|
|
2061
2708
|
}
|
|
@@ -2064,9 +2711,9 @@ async function runInit(opts) {
|
|
|
2064
2711
|
}
|
|
2065
2712
|
async function runFreshFrameworkSetup(opts, detect) {
|
|
2066
2713
|
if (!REACT_FRAMEWORKS2.includes(detect.framework)) {
|
|
2067
|
-
await mkdir4(
|
|
2714
|
+
await mkdir4(resolve22(opts.cwd, ".sightmap"), { recursive: true });
|
|
2068
2715
|
await writeFile7(
|
|
2069
|
-
|
|
2716
|
+
resolve22(opts.cwd, ".sightmap/app.yaml"),
|
|
2070
2717
|
`version: 1
|
|
2071
2718
|
views: []
|
|
2072
2719
|
`,
|
|
@@ -2075,35 +2722,66 @@ views: []
|
|
|
2075
2722
|
return;
|
|
2076
2723
|
}
|
|
2077
2724
|
const pm = await detectPackageManager(opts.cwd);
|
|
2078
|
-
const
|
|
2079
|
-
|
|
2725
|
+
const spinner3 = clack3.spinner();
|
|
2726
|
+
spinner3.start(`Installing @sightmap/react via ${pm}`);
|
|
2080
2727
|
await installAdapter({ cwd: opts.cwd, packageManager: pm, packageName: "@sightmap/react" });
|
|
2081
|
-
|
|
2728
|
+
spinner3.stop("Installed @sightmap/react");
|
|
2082
2729
|
if (!opts.noProvider) {
|
|
2083
|
-
|
|
2084
|
-
if (entry) {
|
|
2085
|
-
const source = await readFile12(entry, "utf8");
|
|
2086
|
-
const result = wrapWithSightmapProvider(source);
|
|
2087
|
-
if (result.changed) {
|
|
2088
|
-
if (opts.yes || await confirmCodemod(entry)) {
|
|
2089
|
-
await writeFile7(entry, result.code, "utf8");
|
|
2090
|
-
}
|
|
2091
|
-
} else if (result.reason) {
|
|
2092
|
-
clack2.log.warn(`SightmapProvider codemod skipped: ${result.reason}. Add manually to ${entry}.`);
|
|
2093
|
-
}
|
|
2094
|
-
} else {
|
|
2095
|
-
clack2.log.warn("Could not locate an app entry file. Add <SightmapProvider> manually.");
|
|
2096
|
-
}
|
|
2730
|
+
await runProviderStep(opts, detect);
|
|
2097
2731
|
}
|
|
2098
2732
|
if (!opts.noCodegen) {
|
|
2099
2733
|
const cmd = buildCodegenCommand({ framework: detect.framework, packageManager: pm });
|
|
2100
2734
|
if (cmd) {
|
|
2101
|
-
|
|
2735
|
+
spinner3.start("Running sightmap-react gen");
|
|
2102
2736
|
await runCodegen(opts.cwd, cmd);
|
|
2103
|
-
|
|
2737
|
+
spinner3.stop("Scaffolded .sightmap/");
|
|
2104
2738
|
}
|
|
2105
2739
|
}
|
|
2106
2740
|
}
|
|
2741
|
+
async function runProviderStep(opts, detect) {
|
|
2742
|
+
const entry = await findAppEntry(opts.cwd, detect.framework);
|
|
2743
|
+
if (!entry) {
|
|
2744
|
+
const snippet = renderProviderSnippet({
|
|
2745
|
+
framework: detect.framework,
|
|
2746
|
+
cwd: opts.cwd,
|
|
2747
|
+
entryAbsPath: "your app's root component file"
|
|
2748
|
+
});
|
|
2749
|
+
showProviderSnippet(snippet, "We couldn't locate the entry file \u2014 drop these lines into your app's root.");
|
|
2750
|
+
return;
|
|
2751
|
+
}
|
|
2752
|
+
const source = await readFile14(entry, "utf8");
|
|
2753
|
+
const result = wrapWithSightmapProvider(source, { framework: detect.framework });
|
|
2754
|
+
if (!result.changed) {
|
|
2755
|
+
const snippet = renderProviderSnippet({
|
|
2756
|
+
framework: detect.framework,
|
|
2757
|
+
cwd: opts.cwd,
|
|
2758
|
+
entryAbsPath: entry
|
|
2759
|
+
});
|
|
2760
|
+
const note = result.reason ? `Codemod skipped: ${result.reason}.` : void 0;
|
|
2761
|
+
showProviderSnippet(snippet, note);
|
|
2762
|
+
return;
|
|
2763
|
+
}
|
|
2764
|
+
if (opts.yes) {
|
|
2765
|
+
await writeFile7(entry, result.code, "utf8");
|
|
2766
|
+
return;
|
|
2767
|
+
}
|
|
2768
|
+
const entryRel = relativePath(opts.cwd, entry);
|
|
2769
|
+
const choice = await selectProviderAction(entryRel);
|
|
2770
|
+
if (choice === "auto") {
|
|
2771
|
+
await writeFile7(entry, result.code, "utf8");
|
|
2772
|
+
} else if (choice === "snippet") {
|
|
2773
|
+
const snippet = renderProviderSnippet({
|
|
2774
|
+
framework: detect.framework,
|
|
2775
|
+
cwd: opts.cwd,
|
|
2776
|
+
entryAbsPath: entry
|
|
2777
|
+
});
|
|
2778
|
+
showProviderSnippet(snippet);
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
function relativePath(cwd, abs) {
|
|
2782
|
+
if (abs.startsWith(cwd + "/")) return abs.slice(cwd.length + 1);
|
|
2783
|
+
return abs;
|
|
2784
|
+
}
|
|
2107
2785
|
async function findAppEntry(cwd, framework) {
|
|
2108
2786
|
const candidates = {
|
|
2109
2787
|
"react-vite": ["src/main.tsx", "src/main.jsx", "src/index.tsx", "src/index.jsx"],
|
|
@@ -2114,28 +2792,25 @@ async function findAppEntry(cwd, framework) {
|
|
|
2114
2792
|
unknown: []
|
|
2115
2793
|
};
|
|
2116
2794
|
for (const rel of candidates[framework]) {
|
|
2117
|
-
const abs =
|
|
2795
|
+
const abs = resolve22(cwd, rel);
|
|
2118
2796
|
try {
|
|
2119
|
-
await
|
|
2797
|
+
await readFile14(abs, "utf8");
|
|
2120
2798
|
return abs;
|
|
2121
2799
|
} catch {
|
|
2122
2800
|
}
|
|
2123
2801
|
}
|
|
2124
2802
|
return null;
|
|
2125
2803
|
}
|
|
2126
|
-
async function confirmCodemod(entry) {
|
|
2127
|
-
const choice = await clack2.confirm({
|
|
2128
|
-
message: `Wrap ${entry} with <SightmapProvider>?`,
|
|
2129
|
-
initialValue: true
|
|
2130
|
-
});
|
|
2131
|
-
return typeof choice === "boolean" ? choice : false;
|
|
2132
|
-
}
|
|
2133
2804
|
async function runExistingCorpusFlow(cwd, viewCount) {
|
|
2134
|
-
const
|
|
2135
|
-
|
|
2805
|
+
const spinner3 = clack3.spinner();
|
|
2806
|
+
spinner3.start("Auditing existing corpus...");
|
|
2136
2807
|
await runLint({ path: ".sightmap", cwd, json: false, strict: false });
|
|
2137
|
-
|
|
2138
|
-
|
|
2808
|
+
spinner3.stop("Audit complete");
|
|
2809
|
+
const header = `${viewCount} view${viewCount === 1 ? "" : "s"}`;
|
|
2810
|
+
const body = formatExistingCorpusReport({ viewCount, diagnostics: [] });
|
|
2811
|
+
clack3.log.message(`${header}
|
|
2812
|
+
|
|
2813
|
+
${body}`);
|
|
2139
2814
|
}
|
|
2140
2815
|
async function resolveHosts(opts, detect) {
|
|
2141
2816
|
if (opts.hosts) {
|
|
@@ -2159,6 +2834,20 @@ function resolveBrowserBundle(opts, detect) {
|
|
|
2159
2834
|
if (opts.curateOnly) return false;
|
|
2160
2835
|
return !detect.sightmapAwareVendor;
|
|
2161
2836
|
}
|
|
2837
|
+
async function maybePickWorkspace(opts) {
|
|
2838
|
+
const all = await enumerateWorkspaces({ root: opts.cwd });
|
|
2839
|
+
if (all.length === 0) return null;
|
|
2840
|
+
const withFramework = all.filter((w) => w.framework !== "unknown");
|
|
2841
|
+
if (withFramework.length === 0) return null;
|
|
2842
|
+
if (opts.yes) {
|
|
2843
|
+
if (withFramework.length === 1) return withFramework[0];
|
|
2844
|
+
clack3.log.warn(
|
|
2845
|
+
`Found ${withFramework.length} workspaces with a framework. Re-run from one of them, or without -y to pick interactively.`
|
|
2846
|
+
);
|
|
2847
|
+
return null;
|
|
2848
|
+
}
|
|
2849
|
+
return selectWorkspace(withFramework);
|
|
2850
|
+
}
|
|
2162
2851
|
async function writeMcpForHost(host, cwd, serverDef) {
|
|
2163
2852
|
const common = { cwd, serverName: "sightmap", serverDef };
|
|
2164
2853
|
if (host === "claude-code") return writeClaudeCodeMcp(common);
|
|
@@ -2166,7 +2855,7 @@ async function writeMcpForHost(host, cwd, serverDef) {
|
|
|
2166
2855
|
if (host === "opencode") return writeOpenCodeMcp(common);
|
|
2167
2856
|
if (host === "codex") {
|
|
2168
2857
|
return writeCodexMcp({
|
|
2169
|
-
configPath:
|
|
2858
|
+
configPath: resolve22(process.env.HOME ?? cwd, ".codex/config.toml"),
|
|
2170
2859
|
serverName: "sightmap",
|
|
2171
2860
|
serverDef
|
|
2172
2861
|
});
|
|
@@ -2266,7 +2955,7 @@ program.command("fmt [path]").description("Canonicalize .sightmap/*.yaml files (
|
|
|
2266
2955
|
process.exit(code);
|
|
2267
2956
|
}
|
|
2268
2957
|
);
|
|
2269
|
-
program.command("init").description("Initialize Sightmap in this project (detects framework + coding agent, writes config).").option("--yes", "accept all defaults (non-interactive)").option("--plugin", "force plugin install path").option("--manual", "force manual install path").option("--host <names>", "comma-separated hosts: claude-code,codex,cursor,opencode").option("--with-browser", "force bundled Playwright + browser tools").option("--curate-only", "force curation-only (skip browser tools)").option("--no-codegen", "skip sightmap-react gen").option("--no-provider", "skip <SightmapProvider> codemod").action(async (opts) => {
|
|
2958
|
+
program.command("init").description("Initialize Sightmap in this project (detects framework + coding agent, writes config).").option("--yes", "accept all defaults (non-interactive)").option("--plugin", "force plugin install path").option("--manual", "force manual install path").option("--host <names>", "comma-separated hosts: claude-code,codex,cursor,opencode").option("--with-browser", "force bundled Playwright + browser tools").option("--curate-only", "force curation-only (skip browser tools)").option("--no-codegen", "skip sightmap-react gen").option("--no-provider", "skip <SightmapProvider> codemod").option("--no-smoke", "skip the MCP launch smoke test (CI / headless use)").action(async (opts) => {
|
|
2270
2959
|
const code = await runInit({
|
|
2271
2960
|
cwd: program.opts().cwd,
|
|
2272
2961
|
yes: opts.yes === true,
|
|
@@ -2276,7 +2965,8 @@ program.command("init").description("Initialize Sightmap in this project (detect
|
|
|
2276
2965
|
...opts.withBrowser ? { withBrowser: true } : {},
|
|
2277
2966
|
...opts.curateOnly ? { curateOnly: true } : {},
|
|
2278
2967
|
...opts.codegen === false ? { noCodegen: true } : {},
|
|
2279
|
-
...opts.provider === false ? { noProvider: true } : {}
|
|
2968
|
+
...opts.provider === false ? { noProvider: true } : {},
|
|
2969
|
+
...opts.smoke === false ? { noSmoke: true } : {}
|
|
2280
2970
|
});
|
|
2281
2971
|
process.exit(code);
|
|
2282
2972
|
});
|