@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 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 resolve21 } from "path";
1502
- import { mkdir as mkdir4, readFile as readFile12, rm, writeFile as writeFile7 } from "fs/promises";
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 = `Framework: ${FRAMEWORK_LABELS[d.framework]}`;
1647
- const hosts = d.hosts.length > 0 ? `Coding agents: ${d.hosts.map((h) => HOST_LABELS[h]).join(", ")}` : "Coding agents: none detected";
1648
- const vendor = d.sightmapAwareVendor ? "Browser MCP: Sightmap-aware vendor detected" : "Browser MCP: none detected";
1649
- const dir = d.sightmapDir.present ? `.sightmap/: present (${d.sightmapDir.viewCount ?? 0} views)` : ".sightmap/: not present";
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.note(formatDetectionSummary(d).join("\n"), pc.dim("Detected"));
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 readFile6, writeFile as writeFile2, access as access5 } from "fs/promises";
1694
- import { resolve as resolve15 } from "path";
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 readFile6(path, "utf8"));
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 = resolve15(opts.cwd, ".mcp.json");
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 readFile7, writeFile as writeFile3, access as access6, mkdir } from "fs/promises";
1719
- import { resolve as resolve16, dirname as dirname2 } from "path";
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 = resolve16(opts.cwd, ".cursor/mcp.json");
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 readFile7(path, "utf8"));
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 readFile8, writeFile as writeFile4, access as access7 } from "fs/promises";
1739
- import { resolve as resolve17 } from "path";
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 = resolve17(opts.cwd, "opencode.json");
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 readFile8(path, "utf8"));
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 readFile9, writeFile as writeFile5, access as access8, mkdir as mkdir2 } from "fs/promises";
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 readFile9(opts.configPath, "utf8"));
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 readFile10 } from "fs/promises";
1782
- import { resolve as resolve18 } from "path";
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 = resolve18(opts.destDir, ".claude-plugin/plugin.json");
1787
- const manifest = JSON.parse(await readFile10(manifestPath, "utf8"));
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 readFile11, writeFile as writeFile6, access as access9 } from "fs/promises";
1800
- import { resolve as resolve19 } from "path";
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(resolve19(opts.tarballDir, "skills"), targets.skills, { recursive: true });
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(resolve19(opts.tarballDir, "agents"), targets.agents, { recursive: true });
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
- if (targets.hooksJson) {
1810
- const srcPath = resolve19(opts.tarballDir, "hooks/hooks.json");
1811
- const dstPath = targets.hooksJson;
1812
- let merged;
1813
- try {
1814
- await access9(dstPath);
1815
- const existing = JSON.parse(await readFile11(dstPath, "utf8"));
1816
- const incoming = JSON.parse(await readFile11(srcPath, "utf8"));
1817
- merged = { ...existing, ...incoming };
1818
- } catch {
1819
- merged = JSON.parse(await readFile11(srcPath, "utf8"));
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: resolve19(projectDir, ".claude/skills/sightmap"),
1829
- agents: resolve19(projectDir, ".claude/agents"),
1830
- hooksJson: resolve19(projectDir, ".claude/hooks.json")
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: resolve19(projectDir, ".codex/skills/sightmap"),
1835
- agents: resolve19(projectDir, ".codex/agents"),
1836
- hooksJson: resolve19(projectDir, ".codex/hooks.json")
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: resolve19(projectDir, ".cursor/rules/sightmap") };
2228
+ return { skills: resolve20(projectDir, ".cursor/rules/sightmap") };
1840
2229
  case "opencode":
1841
- return { skills: resolve19(projectDir, ".opencode/plugins/sightmap") };
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 resolve20 } from "path";
1884
- async function exists3(p) {
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
- if (await exists3(resolve20(cwd, "pnpm-lock.yaml"))) return "pnpm";
1894
- if (await exists3(resolve20(cwd, "yarn.lock"))) return "yarn";
1895
- return "npm";
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
- let renderCall = null;
1958
- let renderArgIndex = -1;
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
- renderCall = path.node;
1964
- renderArgIndex = 0;
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 (!renderCall) {
1971
- return { code: source, changed: false, reason: "no .render() call found" };
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.program.body.unshift(importDecl);
1986
- const output = recast.print(ast, { quote: "single" }).code;
1987
- return { code: output, changed: true };
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
- const detect = {
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
- clack2.note(renderPluginInstructions(hosts), "Next");
2506
+ showPluginInstructions(hosts);
2041
2507
  outro2("Run those, then restart your agent.");
2042
2508
  return 0;
2043
2509
  }
2044
- const tarballDir = resolve21(tmpdir(), `sightmap-plugin-${Date.now()}`);
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(resolve21(opts.cwd, ".sightmap"), { recursive: true });
2533
+ await mkdir4(resolve22(opts.cwd, ".sightmap"), { recursive: true });
2068
2534
  await writeFile7(
2069
- resolve21(opts.cwd, ".sightmap/app.yaml"),
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
- const entry = await findAppEntry(opts.cwd, detect.framework);
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 = resolve21(cwd, rel);
2614
+ const abs = resolve22(cwd, rel);
2118
2615
  try {
2119
- await readFile12(abs, "utf8");
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
- clack2.note(formatExistingCorpusReport({ viewCount, diagnostics: [] }), `${viewCount} view${viewCount === 1 ? "" : "s"}`);
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: resolve21(process.env.HOME ?? cwd, ".codex/config.toml"),
2677
+ configPath: resolve22(process.env.HOME ?? cwd, ".codex/config.toml"),
2170
2678
  serverName: "sightmap",
2171
2679
  serverDef
2172
2680
  });