@sightmap/sightmap 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +625 -117
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -1498,8 +1498,8 @@ 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 readFile13, rm, writeFile as writeFile7 } from "fs/promises";
|
|
1503
1503
|
import { tmpdir } from "os";
|
|
1504
1504
|
import * as clack2 from "@clack/prompts";
|
|
1505
1505
|
|
|
@@ -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,49 +2095,139 @@ 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
|
|
|
@@ -1864,24 +2253,10 @@ function buildSightmapMcpServerDef(opts) {
|
|
|
1864
2253
|
};
|
|
1865
2254
|
}
|
|
1866
2255
|
|
|
1867
|
-
// src/cli/init/plugin-path.ts
|
|
1868
|
-
var SECTIONS = {
|
|
1869
|
-
"claude-code": "Claude Code:\n /plugin marketplace add sightmap/plugin\n /plugin install sightmap@sightmap-marketplace",
|
|
1870
|
-
codex: "Codex:\n codex plugin marketplace add sightmap/plugin\n codex plugin install sightmap@sightmap-marketplace",
|
|
1871
|
-
cursor: "Cursor:\n /plugin add sightmap/plugin\n (in the Cursor command palette)",
|
|
1872
|
-
opencode: 'OpenCode:\n Add to opencode.json plugin array:\n "plugin": ["sightmap@git+https://github.com/sightmap/plugin"]\n Then restart OpenCode.'
|
|
1873
|
-
};
|
|
1874
|
-
function renderPluginInstructions(hosts) {
|
|
1875
|
-
const head = "Run these to finish setup:\n";
|
|
1876
|
-
const body = hosts.map((h) => SECTIONS[h]).join("\n\n");
|
|
1877
|
-
const foot = "\n\nThen restart your agent.";
|
|
1878
|
-
return head + "\n" + body + foot;
|
|
1879
|
-
}
|
|
1880
|
-
|
|
1881
2256
|
// src/cli/init/framework-setup/package-manager.ts
|
|
1882
2257
|
import { access as access10 } from "fs/promises";
|
|
1883
|
-
import { resolve as
|
|
1884
|
-
async function
|
|
2258
|
+
import { dirname as dirname4, resolve as resolve21 } from "path";
|
|
2259
|
+
async function exists4(p) {
|
|
1885
2260
|
try {
|
|
1886
2261
|
await access10(p);
|
|
1887
2262
|
return true;
|
|
@@ -1889,10 +2264,21 @@ async function exists3(p) {
|
|
|
1889
2264
|
return false;
|
|
1890
2265
|
}
|
|
1891
2266
|
}
|
|
2267
|
+
var SIGNALS = [
|
|
2268
|
+
{ file: "pnpm-lock.yaml", pm: "pnpm" },
|
|
2269
|
+
{ file: "pnpm-workspace.yaml", pm: "pnpm" },
|
|
2270
|
+
{ file: "yarn.lock", pm: "yarn" }
|
|
2271
|
+
];
|
|
1892
2272
|
async function detectPackageManager(cwd) {
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
2273
|
+
let dir = resolve21(cwd);
|
|
2274
|
+
while (true) {
|
|
2275
|
+
for (const { file, pm } of SIGNALS) {
|
|
2276
|
+
if (await exists4(resolve21(dir, file))) return pm;
|
|
2277
|
+
}
|
|
2278
|
+
const parent = dirname4(dir);
|
|
2279
|
+
if (parent === dir) return "npm";
|
|
2280
|
+
dir = parent;
|
|
2281
|
+
}
|
|
1896
2282
|
}
|
|
1897
2283
|
|
|
1898
2284
|
// src/cli/init/framework-setup/install-adapter.ts
|
|
@@ -1925,7 +2311,7 @@ function buildCodegenCommand(opts) {
|
|
|
1925
2311
|
if (!REACT_FRAMEWORKS.includes(opts.framework)) return null;
|
|
1926
2312
|
return {
|
|
1927
2313
|
command: opts.packageManager,
|
|
1928
|
-
args: [...EXEC_VERB[opts.packageManager], "sightmap-react", "gen"]
|
|
2314
|
+
args: [...EXEC_VERB[opts.packageManager], "sightmap-react", "gen", "--router=auto"]
|
|
1929
2315
|
};
|
|
1930
2316
|
}
|
|
1931
2317
|
async function runCodegen(cwd, cmd) {
|
|
@@ -1948,43 +2334,109 @@ var babelParser = {
|
|
|
1948
2334
|
});
|
|
1949
2335
|
}
|
|
1950
2336
|
};
|
|
1951
|
-
function wrapWithSightmapProvider(source) {
|
|
2337
|
+
function wrapWithSightmapProvider(source, opts = {}) {
|
|
1952
2338
|
if (source.includes("SightmapProvider")) {
|
|
1953
2339
|
return { code: source, changed: false, reason: "SightmapProvider already present" };
|
|
1954
2340
|
}
|
|
1955
2341
|
const ast = recast.parse(source, { parser: babelParser });
|
|
1956
2342
|
const b = recast.types.builders;
|
|
1957
|
-
|
|
1958
|
-
|
|
2343
|
+
const wrap = (inner) => b.jsxElement(
|
|
2344
|
+
b.jsxOpeningElement(b.jsxIdentifier("SightmapProvider"), [], false),
|
|
2345
|
+
b.jsxClosingElement(b.jsxIdentifier("SightmapProvider")),
|
|
2346
|
+
[stripPrintMetadata(inner)]
|
|
2347
|
+
);
|
|
2348
|
+
let wrapped = false;
|
|
2349
|
+
let reason;
|
|
1959
2350
|
recast.types.visit(ast, {
|
|
1960
2351
|
visitCallExpression(path) {
|
|
2352
|
+
if (wrapped) return false;
|
|
1961
2353
|
const callee = path.node.callee;
|
|
1962
2354
|
if (callee.type === "MemberExpression" && callee.property.type === "Identifier" && callee.property.name === "render") {
|
|
1963
|
-
|
|
1964
|
-
|
|
2355
|
+
const arg = path.node.arguments[0];
|
|
2356
|
+
if (!arg) {
|
|
2357
|
+
reason = "render() called with no args";
|
|
2358
|
+
return false;
|
|
2359
|
+
}
|
|
2360
|
+
path.node.arguments[0] = wrap(arg);
|
|
2361
|
+
wrapped = true;
|
|
1965
2362
|
return false;
|
|
1966
2363
|
}
|
|
1967
2364
|
this.traverse(path);
|
|
1968
2365
|
}
|
|
1969
2366
|
});
|
|
1970
|
-
if (!
|
|
1971
|
-
|
|
2367
|
+
if (!wrapped && opts.framework === "react-router-7") {
|
|
2368
|
+
wrapped = tryWrapDefaultExport(ast, wrap);
|
|
2369
|
+
if (!wrapped) reason = "no default export returning JSX found in app/root.tsx";
|
|
2370
|
+
}
|
|
2371
|
+
if (!wrapped) {
|
|
2372
|
+
return { code: source, changed: false, reason: reason ?? "no .render() call found" };
|
|
1972
2373
|
}
|
|
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
2374
|
const importDecl = b.importDeclaration(
|
|
1982
2375
|
[b.importSpecifier(b.identifier("SightmapProvider"), b.identifier("SightmapProvider"))],
|
|
1983
2376
|
b.stringLiteral("@sightmap/react")
|
|
1984
2377
|
);
|
|
1985
|
-
ast
|
|
1986
|
-
|
|
1987
|
-
|
|
2378
|
+
insertImportAfterHeaderComments(ast, importDecl);
|
|
2379
|
+
return { code: recast.print(ast, { quote: "single" }).code, changed: true };
|
|
2380
|
+
}
|
|
2381
|
+
function insertImportAfterHeaderComments(ast, importDecl) {
|
|
2382
|
+
const body = ast.program.body;
|
|
2383
|
+
const firstStmt = body[0];
|
|
2384
|
+
if (firstStmt) {
|
|
2385
|
+
const newImp = importDecl;
|
|
2386
|
+
if (Array.isArray(firstStmt.leadingComments) && firstStmt.leadingComments.length > 0) {
|
|
2387
|
+
newImp.leadingComments = firstStmt.leadingComments;
|
|
2388
|
+
firstStmt.leadingComments = [];
|
|
2389
|
+
}
|
|
2390
|
+
if (Array.isArray(firstStmt.comments) && firstStmt.comments.length > 0) {
|
|
2391
|
+
newImp.comments = firstStmt.comments;
|
|
2392
|
+
firstStmt.comments = [];
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
body.unshift(importDecl);
|
|
2396
|
+
}
|
|
2397
|
+
function stripPrintMetadata(node) {
|
|
2398
|
+
const n = node;
|
|
2399
|
+
if (n && typeof n === "object") {
|
|
2400
|
+
try {
|
|
2401
|
+
n.original = void 0;
|
|
2402
|
+
} catch {
|
|
2403
|
+
}
|
|
2404
|
+
try {
|
|
2405
|
+
n.loc = void 0;
|
|
2406
|
+
} catch {
|
|
2407
|
+
}
|
|
2408
|
+
if (n.extra) n.extra.parenthesized = false;
|
|
2409
|
+
}
|
|
2410
|
+
return node;
|
|
2411
|
+
}
|
|
2412
|
+
function tryWrapDefaultExport(ast, wrap) {
|
|
2413
|
+
let done = false;
|
|
2414
|
+
recast.types.visit(ast, {
|
|
2415
|
+
visitExportDefaultDeclaration(path) {
|
|
2416
|
+
if (done) return false;
|
|
2417
|
+
const decl = path.node.declaration;
|
|
2418
|
+
const body = decl.body;
|
|
2419
|
+
if ((decl.type === "FunctionDeclaration" || decl.type === "ArrowFunctionExpression" || decl.type === "FunctionExpression") && body && body.type === "BlockStatement" && Array.isArray(body.body)) {
|
|
2420
|
+
for (const stmt of body.body) {
|
|
2421
|
+
if (stmt.type === "ReturnStatement" && stmt.argument) {
|
|
2422
|
+
const arg = stmt.argument;
|
|
2423
|
+
if (arg.type === "JSXElement" || arg.type === "JSXFragment") {
|
|
2424
|
+
stmt.argument = wrap(stmt.argument);
|
|
2425
|
+
done = true;
|
|
2426
|
+
return false;
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
if (decl.type === "ArrowFunctionExpression" && body && (body.type === "JSXElement" || body.type === "JSXFragment")) {
|
|
2432
|
+
decl.body = wrap(body);
|
|
2433
|
+
done = true;
|
|
2434
|
+
return false;
|
|
2435
|
+
}
|
|
2436
|
+
return false;
|
|
2437
|
+
}
|
|
2438
|
+
});
|
|
2439
|
+
return done;
|
|
1988
2440
|
}
|
|
1989
2441
|
|
|
1990
2442
|
// src/cli/init/existing-corpus.ts
|
|
@@ -2008,7 +2460,7 @@ function formatExistingCorpusReport(input) {
|
|
|
2008
2460
|
var REACT_FRAMEWORKS2 = ["react-vite", "react-cra", "next-app", "next-pages", "react-router-7"];
|
|
2009
2461
|
async function runInit(opts) {
|
|
2010
2462
|
intro2();
|
|
2011
|
-
|
|
2463
|
+
let detect = {
|
|
2012
2464
|
framework: await detectFramework(opts.cwd),
|
|
2013
2465
|
hosts: await detectHarnesses(opts.cwd),
|
|
2014
2466
|
sightmapAwareVendor: false,
|
|
@@ -2016,6 +2468,20 @@ async function runInit(opts) {
|
|
|
2016
2468
|
};
|
|
2017
2469
|
detect.sightmapAwareVendor = await detectSightmapAwareVendor(opts.cwd, detect.hosts);
|
|
2018
2470
|
showDetection(detect);
|
|
2471
|
+
if (detect.framework === "unknown" && !detect.sightmapDir.present) {
|
|
2472
|
+
const chosen = await maybePickWorkspace(opts);
|
|
2473
|
+
if (typeof chosen === "symbol") return 1;
|
|
2474
|
+
if (chosen) {
|
|
2475
|
+
opts = { ...opts, cwd: chosen.dir };
|
|
2476
|
+
clack2.log.info(`Targeting workspace: ${chosen.relPath}`);
|
|
2477
|
+
detect = {
|
|
2478
|
+
framework: await detectFramework(opts.cwd),
|
|
2479
|
+
hosts: detect.hosts,
|
|
2480
|
+
sightmapAwareVendor: detect.sightmapAwareVendor,
|
|
2481
|
+
sightmapDir: await detectSightmapDir(opts.cwd)
|
|
2482
|
+
};
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2019
2485
|
if (detect.sightmapDir.present) {
|
|
2020
2486
|
await runExistingCorpusFlow(opts.cwd, detect.sightmapDir.viewCount ?? 0);
|
|
2021
2487
|
} else {
|
|
@@ -2037,11 +2503,11 @@ async function runInit(opts) {
|
|
|
2037
2503
|
}
|
|
2038
2504
|
const withBrowser = resolveBrowserBundle(opts, detect);
|
|
2039
2505
|
if (installPath === "plugin") {
|
|
2040
|
-
|
|
2506
|
+
showPluginInstructions(hosts);
|
|
2041
2507
|
outro2("Run those, then restart your agent.");
|
|
2042
2508
|
return 0;
|
|
2043
2509
|
}
|
|
2044
|
-
const tarballDir =
|
|
2510
|
+
const tarballDir = resolve22(tmpdir(), `sightmap-plugin-${Date.now()}`);
|
|
2045
2511
|
await mkdir4(tarballDir, { recursive: true });
|
|
2046
2512
|
try {
|
|
2047
2513
|
const tar = await fetchPluginTarball({ destDir: tarballDir, version: "latest" });
|
|
@@ -2064,9 +2530,9 @@ async function runInit(opts) {
|
|
|
2064
2530
|
}
|
|
2065
2531
|
async function runFreshFrameworkSetup(opts, detect) {
|
|
2066
2532
|
if (!REACT_FRAMEWORKS2.includes(detect.framework)) {
|
|
2067
|
-
await mkdir4(
|
|
2533
|
+
await mkdir4(resolve22(opts.cwd, ".sightmap"), { recursive: true });
|
|
2068
2534
|
await writeFile7(
|
|
2069
|
-
|
|
2535
|
+
resolve22(opts.cwd, ".sightmap/app.yaml"),
|
|
2070
2536
|
`version: 1
|
|
2071
2537
|
views: []
|
|
2072
2538
|
`,
|
|
@@ -2080,20 +2546,7 @@ views: []
|
|
|
2080
2546
|
await installAdapter({ cwd: opts.cwd, packageManager: pm, packageName: "@sightmap/react" });
|
|
2081
2547
|
spinner2.stop("Installed @sightmap/react");
|
|
2082
2548
|
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
|
-
}
|
|
2549
|
+
await runProviderStep(opts, detect);
|
|
2097
2550
|
}
|
|
2098
2551
|
if (!opts.noCodegen) {
|
|
2099
2552
|
const cmd = buildCodegenCommand({ framework: detect.framework, packageManager: pm });
|
|
@@ -2104,6 +2557,50 @@ views: []
|
|
|
2104
2557
|
}
|
|
2105
2558
|
}
|
|
2106
2559
|
}
|
|
2560
|
+
async function runProviderStep(opts, detect) {
|
|
2561
|
+
const entry = await findAppEntry(opts.cwd, detect.framework);
|
|
2562
|
+
if (!entry) {
|
|
2563
|
+
const snippet = renderProviderSnippet({
|
|
2564
|
+
framework: detect.framework,
|
|
2565
|
+
cwd: opts.cwd,
|
|
2566
|
+
entryAbsPath: "your app's root component file"
|
|
2567
|
+
});
|
|
2568
|
+
showProviderSnippet(snippet, "We couldn't locate the entry file \u2014 drop these lines into your app's root.");
|
|
2569
|
+
return;
|
|
2570
|
+
}
|
|
2571
|
+
const source = await readFile13(entry, "utf8");
|
|
2572
|
+
const result = wrapWithSightmapProvider(source, { framework: detect.framework });
|
|
2573
|
+
if (!result.changed) {
|
|
2574
|
+
const snippet = renderProviderSnippet({
|
|
2575
|
+
framework: detect.framework,
|
|
2576
|
+
cwd: opts.cwd,
|
|
2577
|
+
entryAbsPath: entry
|
|
2578
|
+
});
|
|
2579
|
+
const note = result.reason ? `Codemod skipped: ${result.reason}.` : void 0;
|
|
2580
|
+
showProviderSnippet(snippet, note);
|
|
2581
|
+
return;
|
|
2582
|
+
}
|
|
2583
|
+
if (opts.yes) {
|
|
2584
|
+
await writeFile7(entry, result.code, "utf8");
|
|
2585
|
+
return;
|
|
2586
|
+
}
|
|
2587
|
+
const entryRel = relativePath(opts.cwd, entry);
|
|
2588
|
+
const choice = await selectProviderAction(entryRel);
|
|
2589
|
+
if (choice === "auto") {
|
|
2590
|
+
await writeFile7(entry, result.code, "utf8");
|
|
2591
|
+
} else if (choice === "snippet") {
|
|
2592
|
+
const snippet = renderProviderSnippet({
|
|
2593
|
+
framework: detect.framework,
|
|
2594
|
+
cwd: opts.cwd,
|
|
2595
|
+
entryAbsPath: entry
|
|
2596
|
+
});
|
|
2597
|
+
showProviderSnippet(snippet);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
function relativePath(cwd, abs) {
|
|
2601
|
+
if (abs.startsWith(cwd + "/")) return abs.slice(cwd.length + 1);
|
|
2602
|
+
return abs;
|
|
2603
|
+
}
|
|
2107
2604
|
async function findAppEntry(cwd, framework) {
|
|
2108
2605
|
const candidates = {
|
|
2109
2606
|
"react-vite": ["src/main.tsx", "src/main.jsx", "src/index.tsx", "src/index.jsx"],
|
|
@@ -2114,28 +2611,25 @@ async function findAppEntry(cwd, framework) {
|
|
|
2114
2611
|
unknown: []
|
|
2115
2612
|
};
|
|
2116
2613
|
for (const rel of candidates[framework]) {
|
|
2117
|
-
const abs =
|
|
2614
|
+
const abs = resolve22(cwd, rel);
|
|
2118
2615
|
try {
|
|
2119
|
-
await
|
|
2616
|
+
await readFile13(abs, "utf8");
|
|
2120
2617
|
return abs;
|
|
2121
2618
|
} catch {
|
|
2122
2619
|
}
|
|
2123
2620
|
}
|
|
2124
2621
|
return null;
|
|
2125
2622
|
}
|
|
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
2623
|
async function runExistingCorpusFlow(cwd, viewCount) {
|
|
2134
2624
|
const spinner2 = clack2.spinner();
|
|
2135
2625
|
spinner2.start("Auditing existing corpus...");
|
|
2136
2626
|
await runLint({ path: ".sightmap", cwd, json: false, strict: false });
|
|
2137
2627
|
spinner2.stop("Audit complete");
|
|
2138
|
-
|
|
2628
|
+
const header = `${viewCount} view${viewCount === 1 ? "" : "s"}`;
|
|
2629
|
+
const body = formatExistingCorpusReport({ viewCount, diagnostics: [] });
|
|
2630
|
+
clack2.log.message(`${header}
|
|
2631
|
+
|
|
2632
|
+
${body}`);
|
|
2139
2633
|
}
|
|
2140
2634
|
async function resolveHosts(opts, detect) {
|
|
2141
2635
|
if (opts.hosts) {
|
|
@@ -2159,6 +2653,20 @@ function resolveBrowserBundle(opts, detect) {
|
|
|
2159
2653
|
if (opts.curateOnly) return false;
|
|
2160
2654
|
return !detect.sightmapAwareVendor;
|
|
2161
2655
|
}
|
|
2656
|
+
async function maybePickWorkspace(opts) {
|
|
2657
|
+
const all = await enumerateWorkspaces({ root: opts.cwd });
|
|
2658
|
+
if (all.length === 0) return null;
|
|
2659
|
+
const withFramework = all.filter((w) => w.framework !== "unknown");
|
|
2660
|
+
if (withFramework.length === 0) return null;
|
|
2661
|
+
if (opts.yes) {
|
|
2662
|
+
if (withFramework.length === 1) return withFramework[0];
|
|
2663
|
+
clack2.log.warn(
|
|
2664
|
+
`Found ${withFramework.length} workspaces with a framework. Re-run from one of them, or without -y to pick interactively.`
|
|
2665
|
+
);
|
|
2666
|
+
return null;
|
|
2667
|
+
}
|
|
2668
|
+
return selectWorkspace(withFramework);
|
|
2669
|
+
}
|
|
2162
2670
|
async function writeMcpForHost(host, cwd, serverDef) {
|
|
2163
2671
|
const common = { cwd, serverName: "sightmap", serverDef };
|
|
2164
2672
|
if (host === "claude-code") return writeClaudeCodeMcp(common);
|
|
@@ -2166,7 +2674,7 @@ async function writeMcpForHost(host, cwd, serverDef) {
|
|
|
2166
2674
|
if (host === "opencode") return writeOpenCodeMcp(common);
|
|
2167
2675
|
if (host === "codex") {
|
|
2168
2676
|
return writeCodexMcp({
|
|
2169
|
-
configPath:
|
|
2677
|
+
configPath: resolve22(process.env.HOME ?? cwd, ".codex/config.toml"),
|
|
2170
2678
|
serverName: "sightmap",
|
|
2171
2679
|
serverDef
|
|
2172
2680
|
});
|