@neat.is/core 0.10.1-dev.20260926 → 0.10.1-dev.20260928

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  resolveNeatVersion,
9
9
  startK8sSubstratePolling,
10
10
  writeDaemonRecord
11
- } from "./chunk-EJPNEEJA.js";
11
+ } from "./chunk-HVBD4ULO.js";
12
12
  import {
13
13
  buildSearchIndex
14
14
  } from "./chunk-BC53SCT7.js";
@@ -63,6 +63,7 @@ import {
63
63
  pruneRegistry,
64
64
  readConnectorsConfig,
65
65
  readErrorEvents,
66
+ readRegistry,
66
67
  removeConnectorEntry,
67
68
  removeDaemonRecord,
68
69
  removeProject,
@@ -77,7 +78,7 @@ import {
77
78
  startStalenessLoop,
78
79
  upsertConnectorEntry,
79
80
  validateConnectorEntry
80
- } from "./chunk-J3VVEB45.js";
81
+ } from "./chunk-CNOJ4T6I.js";
81
82
  import {
82
83
  startOtelGrpcReceiver
83
84
  } from "./chunk-ERE47MCR.js";
@@ -91,9 +92,9 @@ import {
91
92
  } from "./chunk-GDGUY4T6.js";
92
93
 
93
94
  // src/cli.ts
94
- import path18 from "path";
95
+ import path19 from "path";
95
96
  import os5 from "os";
96
- import { promises as fs16 } from "fs";
97
+ import { promises as fs17 } from "fs";
97
98
 
98
99
  // src/banner.ts
99
100
  import path from "path";
@@ -5245,7 +5246,7 @@ async function clearActiveProfile(home = neatHome()) {
5245
5246
  }
5246
5247
 
5247
5248
  // src/login-sso.ts
5248
- var DEFAULT_CP_URL = "https://neat-control-plane-bg5yqctn2q-nw.a.run.app";
5249
+ var DEFAULT_CP_URL = "https://api.neat.is";
5249
5250
  var DEFAULT_WEB_URL = "https://app.neat.is";
5250
5251
  var CALLBACK_TIMEOUT_MS = 5 * 6e4;
5251
5252
  function resolveCpUrl(env = process.env, override) {
@@ -5434,6 +5435,11 @@ async function runSsoLogin(opts, deps) {
5434
5435
  out(` OTEL_EXPORTER_OTLP_ENDPOINT=${cred.ingestEndpoint}`);
5435
5436
  out(` OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer ${cred.otelToken}`);
5436
5437
  }
5438
+ out("");
5439
+ out("This directory isn't part of that graph yet \u2014 logging in connected your account,");
5440
+ out("not this repo, and nothing here has been uploaded.");
5441
+ out(` Manage projects and repos: ${opts.webUrl}`);
5442
+ out("");
5437
5443
  out("Run `neat logout` to switch back to your local daemon.");
5438
5444
  }
5439
5445
  return 0;
@@ -5568,10 +5574,10 @@ function createHttpClient(baseUrl, bearerToken, fetchImpl = fetch) {
5568
5574
  const root = baseUrl.replace(/\/$/, "");
5569
5575
  const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
5570
5576
  return {
5571
- async get(path19) {
5577
+ async get(path20) {
5572
5578
  let res;
5573
5579
  try {
5574
- res = await fetchImpl(`${root}${path19}`, {
5580
+ res = await fetchImpl(`${root}${path20}`, {
5575
5581
  headers: { ...authHeader }
5576
5582
  });
5577
5583
  } catch (err) {
@@ -5583,16 +5589,16 @@ function createHttpClient(baseUrl, bearerToken, fetchImpl = fetch) {
5583
5589
  const body = await res.text().catch(() => "");
5584
5590
  throw new HttpError(
5585
5591
  res.status,
5586
- `${res.status} ${res.statusText} on GET ${path19}: ${body}`,
5592
+ `${res.status} ${res.statusText} on GET ${path20}: ${body}`,
5587
5593
  body
5588
5594
  );
5589
5595
  }
5590
5596
  return await res.json();
5591
5597
  },
5592
- async post(path19, body) {
5598
+ async post(path20, body) {
5593
5599
  let res;
5594
5600
  try {
5595
- res = await fetchImpl(`${root}${path19}`, {
5601
+ res = await fetchImpl(`${root}${path20}`, {
5596
5602
  method: "POST",
5597
5603
  headers: { "content-type": "application/json", ...authHeader },
5598
5604
  body: JSON.stringify(body)
@@ -5606,7 +5612,7 @@ function createHttpClient(baseUrl, bearerToken, fetchImpl = fetch) {
5606
5612
  const text = await res.text().catch(() => "");
5607
5613
  throw new HttpError(
5608
5614
  res.status,
5609
- `${res.status} ${res.statusText} on POST ${path19}: ${text}`,
5615
+ `${res.status} ${res.statusText} on POST ${path20}: ${text}`,
5610
5616
  text
5611
5617
  );
5612
5618
  }
@@ -5620,12 +5626,12 @@ function projectPath(project, suffix) {
5620
5626
  }
5621
5627
  async function runRootCause(client, input) {
5622
5628
  const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
5623
- const path19 = projectPath(
5629
+ const path20 = projectPath(
5624
5630
  input.project,
5625
5631
  `/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
5626
5632
  );
5627
5633
  try {
5628
- const result = await client.get(path19);
5634
+ const result = await client.get(path20);
5629
5635
  const arrowPath = result.traversalPath.join(" \u2190 ");
5630
5636
  const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
5631
5637
  const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
@@ -5651,12 +5657,12 @@ async function runRootCause(client, input) {
5651
5657
  }
5652
5658
  async function runBlastRadius(client, input) {
5653
5659
  const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
5654
- const path19 = projectPath(
5660
+ const path20 = projectPath(
5655
5661
  input.project,
5656
5662
  `/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
5657
5663
  );
5658
5664
  try {
5659
- const result = await client.get(path19);
5665
+ const result = await client.get(path20);
5660
5666
  if (result.totalAffected === 0) {
5661
5667
  return {
5662
5668
  summary: `${result.origin} has no dependents. Nothing else would break if it failed.`
@@ -5690,12 +5696,12 @@ function formatBlastEntry(n) {
5690
5696
  }
5691
5697
  async function runDependencies(client, input) {
5692
5698
  const depth = input.depth ?? 3;
5693
- const path19 = projectPath(
5699
+ const path20 = projectPath(
5694
5700
  input.project,
5695
5701
  `/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
5696
5702
  );
5697
5703
  try {
5698
- const result = await client.get(path19);
5704
+ const result = await client.get(path20);
5699
5705
  if (result.total === 0) {
5700
5706
  return {
5701
5707
  summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
@@ -5787,9 +5793,9 @@ function formatDuration(ms) {
5787
5793
  return `${Math.round(h / 24)}d`;
5788
5794
  }
5789
5795
  async function runIncidents(client, input) {
5790
- const path19 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
5796
+ const path20 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
5791
5797
  try {
5792
- const body = await client.get(path19);
5798
+ const body = await client.get(path20);
5793
5799
  const events = body.events;
5794
5800
  if (events.length === 0) {
5795
5801
  return {
@@ -6566,6 +6572,8 @@ async function defaultReadSecret(prompt) {
6566
6572
  }
6567
6573
 
6568
6574
  // src/welcome.ts
6575
+ import { promises as fs12 } from "fs";
6576
+ import path13 from "path";
6569
6577
  import readline4 from "readline/promises";
6570
6578
  var WORDMARK = [
6571
6579
  "\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557",
@@ -6598,6 +6606,28 @@ function printHeader(out) {
6598
6606
  }
6599
6607
  async function runWelcome(deps = {}) {
6600
6608
  const out = deps.out ?? ((line) => console.log(line));
6609
+ try {
6610
+ return await runFrontDoor(deps, out);
6611
+ } catch (err) {
6612
+ if (isPromptCancelled(err)) {
6613
+ out("");
6614
+ return 130;
6615
+ }
6616
+ throw err;
6617
+ }
6618
+ }
6619
+ var PromptCancelled = class extends Error {
6620
+ constructor() {
6621
+ super("prompt cancelled");
6622
+ this.name = "PromptCancelled";
6623
+ }
6624
+ };
6625
+ function isPromptCancelled(err) {
6626
+ if (err instanceof PromptCancelled) return true;
6627
+ const e = err;
6628
+ return e?.code === "ABORT_ERR" || e?.name === "AbortError";
6629
+ }
6630
+ async function runFrontDoor(deps, out) {
6601
6631
  const readLine = deps.readLine ?? defaultReadLine2;
6602
6632
  const login = deps.login ?? (() => Promise.resolve(0));
6603
6633
  const orchestrator = deps.orchestrator ?? (() => Promise.resolve(0));
@@ -6605,21 +6635,114 @@ async function runWelcome(deps = {}) {
6605
6635
  printHeader(out);
6606
6636
  out("Welcome to NEAT. Let's get you a graph of this system.");
6607
6637
  out("");
6608
- out(" 1) I have a NEAT account \u2014 log in");
6609
- out(" 2) Self-hosted \u2014 set up NEAT on this machine");
6638
+ const keyReader = deps.readKey !== void 0 ? { read: deps.readKey, done: () => {
6639
+ } } : createKeyReader();
6640
+ const picked = keyReader !== null ? await selectOption(out, keyReader, deps.moveCursorUp ?? defaultMoveCursorUp) : await selectByNumber(out, readLine);
6641
+ if (picked === void 0) return runSelfHosted(out, readLine, orchestrator, cwd);
6642
+ if (picked === 0) return login(["--browser"]);
6643
+ return runSelfHosted(out, readLine, orchestrator, cwd);
6644
+ }
6645
+ var MENU_OPTIONS = [
6646
+ "Log me into Hosted Neat",
6647
+ "I'd like to self-host or use it locally (copy a prompt)"
6648
+ ];
6649
+ async function selectOption(out, reader, moveCursorUp) {
6650
+ let cursor = 0;
6651
+ const render = () => {
6652
+ for (const [i, label] of MENU_OPTIONS.entries()) {
6653
+ out(i === cursor ? ` \u276F ${i + 1}) ${label}` : ` ${i + 1}) ${label}`);
6654
+ }
6655
+ };
6656
+ try {
6657
+ render();
6658
+ out("");
6659
+ out(" \u2191/\u2193 to move \xB7 Enter to choose \xB7 1 or 2 to jump");
6660
+ for (; ; ) {
6661
+ const key = await reader.read();
6662
+ if (key === void 0) return void 0;
6663
+ if (key === "cancel") throw new PromptCancelled();
6664
+ if (key === "enter") return cursor;
6665
+ if (key === "select-1") return 0;
6666
+ if (key === "select-2") return 1;
6667
+ if (key === "up") cursor = (cursor + MENU_OPTIONS.length - 1) % MENU_OPTIONS.length;
6668
+ else if (key === "down") cursor = (cursor + 1) % MENU_OPTIONS.length;
6669
+ else continue;
6670
+ moveCursorUp(MENU_OPTIONS.length + 2);
6671
+ render();
6672
+ out("");
6673
+ out(" \u2191/\u2193 to move \xB7 Enter to choose \xB7 1 or 2 to jump");
6674
+ }
6675
+ } finally {
6676
+ reader.done();
6677
+ }
6678
+ }
6679
+ async function selectByNumber(out, readLine) {
6680
+ for (const [i, label] of MENU_OPTIONS.entries()) out(` ${i + 1}) ${label}`);
6610
6681
  out("");
6611
6682
  for (; ; ) {
6612
6683
  const choice = (await readLine("Choose 1 or 2 (default 2): "))?.trim();
6613
- if (choice === void 0) return runSelfHosted(out, readLine, orchestrator, cwd);
6614
- if (choice === "1") {
6615
- return login(["--browser"]);
6616
- }
6617
- if (choice === "2" || choice === "") {
6618
- return runSelfHosted(out, readLine, orchestrator, cwd);
6619
- }
6620
- out(`"${choice}" isn't 1 or 2 \u2014 try again, or press Enter for self-hosted.`);
6684
+ if (choice === void 0) return void 0;
6685
+ if (choice === "1") return 0;
6686
+ if (choice === "2" || choice === "") return 1;
6687
+ out(`"${choice}" isn't 1 or 2 \u2014 try again, or press Enter for the local path.`);
6621
6688
  }
6622
6689
  }
6690
+ function createKeyReader() {
6691
+ const stdin = process.stdin;
6692
+ if (!stdin.isTTY || typeof stdin.setRawMode !== "function") return null;
6693
+ stdin.setRawMode(true);
6694
+ stdin.resume();
6695
+ let closed = false;
6696
+ let ended = false;
6697
+ const markEnded = () => {
6698
+ ended = true;
6699
+ };
6700
+ stdin.once("end", markEnded);
6701
+ stdin.once("close", markEnded);
6702
+ return {
6703
+ read: () => new Promise((resolve) => {
6704
+ if (ended) {
6705
+ resolve(void 0);
6706
+ return;
6707
+ }
6708
+ const finish = (value) => {
6709
+ stdin.removeListener("data", onData);
6710
+ stdin.removeListener("end", onClosed);
6711
+ stdin.removeListener("close", onClosed);
6712
+ resolve(value);
6713
+ };
6714
+ const onData = (buf) => finish(decodeKey(buf));
6715
+ const onClosed = () => {
6716
+ ended = true;
6717
+ finish(void 0);
6718
+ };
6719
+ stdin.on("data", onData);
6720
+ stdin.once("end", onClosed);
6721
+ stdin.once("close", onClosed);
6722
+ }),
6723
+ done: () => {
6724
+ if (closed) return;
6725
+ closed = true;
6726
+ stdin.removeListener("end", markEnded);
6727
+ stdin.removeListener("close", markEnded);
6728
+ stdin.setRawMode(false);
6729
+ stdin.pause();
6730
+ }
6731
+ };
6732
+ }
6733
+ function decodeKey(buf) {
6734
+ const s = buf.toString("utf8");
6735
+ if (s === "\x1B[A" || s === "k") return "up";
6736
+ if (s === "\x1B[B" || s === "j") return "down";
6737
+ if (s === "\r" || s === "\n") return "enter";
6738
+ if (s === "1") return "select-1";
6739
+ if (s === "2") return "select-2";
6740
+ if (s === "" || s === "\x1B") return "cancel";
6741
+ return "ignore";
6742
+ }
6743
+ function defaultMoveCursorUp(rows) {
6744
+ if (rows > 0) process.stdout.write(`\x1B[${rows}A`);
6745
+ }
6623
6746
  async function runSelfHosted(out, readLine, orchestrator, cwd) {
6624
6747
  const wantsPrompt = (await readLine("Print a copy-paste setup prompt for your coding agent? [Y/n]: "))?.trim().toLowerCase();
6625
6748
  if (wantsPrompt !== "n" && wantsPrompt !== "no") {
@@ -6639,10 +6762,23 @@ async function shouldShowWelcome(deps = {}) {
6639
6762
  const stdinTTY = deps.stdinIsTTY ?? Boolean(process.stdin.isTTY);
6640
6763
  const stdoutTTY = deps.stdoutIsTTY ?? Boolean(process.stdout.isTTY);
6641
6764
  if (!stdinTTY || !stdoutTTY) return false;
6642
- const readProfiles = deps.readProfiles ?? (() => readProfilesConfig());
6765
+ const cwd = deps.cwd ?? process.cwd();
6766
+ const readRegistry2 = deps.readRegistry ?? (() => readRegistry());
6767
+ const hasSnapshot = deps.hasSnapshot ?? defaultHasSnapshot;
6643
6768
  try {
6644
- const { profiles } = await readProfiles();
6645
- return profiles.length === 0;
6769
+ const here = path13.resolve(cwd);
6770
+ const { projects } = await readRegistry2();
6771
+ if (projects.some((p) => path13.resolve(p.path) === here)) return false;
6772
+ if (await hasSnapshot(here)) return false;
6773
+ return true;
6774
+ } catch {
6775
+ return false;
6776
+ }
6777
+ }
6778
+ async function defaultHasSnapshot(cwd) {
6779
+ try {
6780
+ const entries = await fs12.readdir(path13.join(cwd, "neat-out"));
6781
+ return entries.some((e) => e.endsWith(".json"));
6646
6782
  } catch {
6647
6783
  return false;
6648
6784
  }
@@ -6658,27 +6794,27 @@ async function defaultReadLine2(prompt) {
6658
6794
  }
6659
6795
 
6660
6796
  // src/hooks-cli.ts
6661
- import path13 from "path";
6797
+ import path14 from "path";
6662
6798
  import os2 from "os";
6663
- import { promises as fs12 } from "fs";
6799
+ import { promises as fs13 } from "fs";
6664
6800
  import { fileURLToPath as fileURLToPath3 } from "url";
6665
6801
  var HOOK_FILENAME = "neat-search-nudge.mjs";
6666
6802
  var GUIDE_FILENAME = "GRAPH_FIRST.md";
6667
6803
  var GUIDE_INSTALL_NAME = "neat-graph-first.md";
6668
6804
  var HOOK_MATCHER = "Grep|Glob|Bash";
6669
6805
  function moduleDir() {
6670
- return typeof __dirname !== "undefined" ? __dirname : path13.dirname(fileURLToPath3(import.meta.url));
6806
+ return typeof __dirname !== "undefined" ? __dirname : path14.dirname(fileURLToPath3(import.meta.url));
6671
6807
  }
6672
6808
  async function readSkillAsset(rel) {
6673
6809
  const here = moduleDir();
6674
6810
  const candidates = [
6675
- path13.resolve(here, "../../claude-skill", rel),
6676
- path13.resolve(here, "../../../claude-skill", rel),
6677
- path13.resolve(here, "../claude-skill", rel)
6811
+ path14.resolve(here, "../../claude-skill", rel),
6812
+ path14.resolve(here, "../../../claude-skill", rel),
6813
+ path14.resolve(here, "../claude-skill", rel)
6678
6814
  ];
6679
6815
  for (const candidate of candidates) {
6680
6816
  try {
6681
- return await fs12.readFile(candidate, "utf8");
6817
+ return await fs13.readFile(candidate, "utf8");
6682
6818
  } catch {
6683
6819
  }
6684
6820
  }
@@ -6688,20 +6824,20 @@ async function readSkillAsset(rel) {
6688
6824
  }
6689
6825
  function neatHome2() {
6690
6826
  const override = process.env.NEAT_HOME;
6691
- if (override && override.length > 0) return path13.resolve(override);
6692
- return path13.join(os2.homedir(), ".neat");
6827
+ if (override && override.length > 0) return path14.resolve(override);
6828
+ return path14.join(os2.homedir(), ".neat");
6693
6829
  }
6694
6830
  function claudeSettingsPath() {
6695
6831
  const override = process.env.NEAT_CLAUDE_SETTINGS;
6696
- if (override && override.length > 0) return path13.resolve(override);
6832
+ if (override && override.length > 0) return path14.resolve(override);
6697
6833
  const home = process.env.HOME ?? process.env.USERPROFILE ?? os2.homedir();
6698
- return path13.join(home, ".claude", "settings.json");
6834
+ return path14.join(home, ".claude", "settings.json");
6699
6835
  }
6700
6836
  function installedHookPath() {
6701
- return path13.join(neatHome2(), "hooks", HOOK_FILENAME);
6837
+ return path14.join(neatHome2(), "hooks", HOOK_FILENAME);
6702
6838
  }
6703
6839
  function gateFlagPath() {
6704
- return path13.join(neatHome2(), "hooks", "gate-enabled");
6840
+ return path14.join(neatHome2(), "hooks", "gate-enabled");
6705
6841
  }
6706
6842
  function isNeatSearchEntry(entry2) {
6707
6843
  return (entry2.hooks ?? []).some(
@@ -6734,14 +6870,14 @@ async function runHooks(opts) {
6734
6870
  const hookScript = await readSkillAsset(`hooks/${HOOK_FILENAME}`);
6735
6871
  const guide = await readSkillAsset(GUIDE_FILENAME);
6736
6872
  const scriptPath = installedHookPath();
6737
- await fs12.mkdir(path13.dirname(scriptPath), { recursive: true });
6738
- await fs12.writeFile(scriptPath, hookScript, { mode: 493 });
6739
- const guidePath = path13.join(neatHome2(), GUIDE_INSTALL_NAME);
6740
- await fs12.writeFile(guidePath, guide, "utf8");
6873
+ await fs13.mkdir(path14.dirname(scriptPath), { recursive: true });
6874
+ await fs13.writeFile(scriptPath, hookScript, { mode: 493 });
6875
+ const guidePath = path14.join(neatHome2(), GUIDE_INSTALL_NAME);
6876
+ await fs13.writeFile(guidePath, guide, "utf8");
6741
6877
  const settingsFile = claudeSettingsPath();
6742
6878
  let settings = {};
6743
6879
  try {
6744
- settings = JSON.parse(await fs12.readFile(settingsFile, "utf8"));
6880
+ settings = JSON.parse(await fs13.readFile(settingsFile, "utf8"));
6745
6881
  } catch (err) {
6746
6882
  if (err.code !== "ENOENT") {
6747
6883
  console.error(
@@ -6763,14 +6899,14 @@ async function runHooks(opts) {
6763
6899
  ...settings,
6764
6900
  hooks: { ...hooks, PreToolUse: preToolUse }
6765
6901
  };
6766
- await fs12.mkdir(path13.dirname(settingsFile), { recursive: true });
6767
- await fs12.writeFile(settingsFile, JSON.stringify(merged, null, 2) + "\n", "utf8");
6902
+ await fs13.mkdir(path14.dirname(settingsFile), { recursive: true });
6903
+ await fs13.writeFile(settingsFile, JSON.stringify(merged, null, 2) + "\n", "utf8");
6768
6904
  const flag = gateFlagPath();
6769
6905
  if (opts.gate) {
6770
- await fs12.mkdir(path13.dirname(flag), { recursive: true });
6771
- await fs12.writeFile(flag, "1\n", "utf8");
6906
+ await fs13.mkdir(path14.dirname(flag), { recursive: true });
6907
+ await fs13.writeFile(flag, "1\n", "utf8");
6772
6908
  } else {
6773
- await fs12.rm(flag, { force: true });
6909
+ await fs13.rm(flag, { force: true });
6774
6910
  }
6775
6911
  const mode = opts.gate ? "GATE (deny search until you ask the graph)" : "nudge (search still runs)";
6776
6912
  console.log(`neat hooks: installed the search hook in ${opts.gate ? "gate" : "nudge"} mode`);
@@ -6859,8 +6995,8 @@ async function runHooksCommand(args) {
6859
6995
  }
6860
6996
 
6861
6997
  // src/claude-cli.ts
6862
- import path14 from "path";
6863
- import { promises as fs13 } from "fs";
6998
+ import path15 from "path";
6999
+ import { promises as fs14 } from "fs";
6864
7000
  var NEAT_SECTION_HEADING = "## neat";
6865
7001
  var NEAT_DIRECTIVE_BODY = `This project has NEAT wired in: a live, fused semantic graph of the system \u2014
6866
7002
  code and runtime behaviour (OpenTelemetry) in one model, every fact tagged with
@@ -6894,8 +7030,8 @@ ${NEAT_DIRECTIVE_BODY}
6894
7030
  }
6895
7031
  function claudeMdPath() {
6896
7032
  const override = process.env.NEAT_CLAUDE_MD;
6897
- if (override && override.length > 0) return path14.resolve(override);
6898
- return path14.join(process.cwd(), "CLAUDE.md");
7033
+ if (override && override.length > 0) return path15.resolve(override);
7034
+ return path15.join(process.cwd(), "CLAUDE.md");
6899
7035
  }
6900
7036
  function splitAroundSection(raw) {
6901
7037
  const lines = raw.split("\n");
@@ -6923,7 +7059,7 @@ function compose(before, after) {
6923
7059
  }
6924
7060
  async function readIfExists(file) {
6925
7061
  try {
6926
- return await fs13.readFile(file, "utf8");
7062
+ return await fs14.readFile(file, "utf8");
6927
7063
  } catch (err) {
6928
7064
  if (err.code === "ENOENT") return null;
6929
7065
  throw err;
@@ -6934,8 +7070,8 @@ async function runInstall() {
6934
7070
  const raw = await readIfExists(file) ?? "";
6935
7071
  const { before, after, found } = splitAroundSection(raw);
6936
7072
  const next = compose(before, after);
6937
- await fs13.mkdir(path14.dirname(file), { recursive: true });
6938
- await fs13.writeFile(file, next, "utf8");
7073
+ await fs14.mkdir(path15.dirname(file), { recursive: true });
7074
+ await fs14.writeFile(file, next, "utf8");
6939
7075
  const verb = raw.length === 0 ? "created" : found ? "refreshed" : "added";
6940
7076
  console.log(`neat claude: ${verb} the \`${NEAT_SECTION_HEADING}\` section in ${file}`);
6941
7077
  console.log("Your agent will now reach for `neat ask` before Read/Grep/Bash. Restart the");
@@ -6956,7 +7092,7 @@ async function runUninstall() {
6956
7092
  }
6957
7093
  const remaining = [before, after].filter((s) => s.length > 0).join("\n\n");
6958
7094
  const next = remaining.length > 0 ? remaining.replace(/\n*$/, "") + "\n" : "";
6959
- await fs13.writeFile(file, next, "utf8");
7095
+ await fs14.writeFile(file, next, "utf8");
6960
7096
  console.log(`neat claude: removed the \`${NEAT_SECTION_HEADING}\` section from ${file}.`);
6961
7097
  return { exitCode: 0 };
6962
7098
  }
@@ -6998,9 +7134,9 @@ async function runClaudeCommand(args) {
6998
7134
  }
6999
7135
 
7000
7136
  // src/codex-cli.ts
7001
- import path15 from "path";
7137
+ import path16 from "path";
7002
7138
  import os3 from "os";
7003
- import { promises as fs14 } from "fs";
7139
+ import { promises as fs15 } from "fs";
7004
7140
  import { isDeepStrictEqual } from "util";
7005
7141
  import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
7006
7142
  var CODEX_MCP_SERVER = {
@@ -7018,14 +7154,14 @@ var NEAT_GRAPH_FIRST_START = "<!-- neat:graph-first -->";
7018
7154
  var NEAT_GRAPH_FIRST_END = "<!-- /neat:graph-first -->";
7019
7155
  function codexConfigPath() {
7020
7156
  const override = process.env.NEAT_CODEX_CONFIG;
7021
- if (override && override.length > 0) return path15.resolve(override);
7157
+ if (override && override.length > 0) return path16.resolve(override);
7022
7158
  const home = process.env.HOME ?? process.env.USERPROFILE ?? os3.homedir();
7023
- return path15.join(home, ".codex", "config.toml");
7159
+ return path16.join(home, ".codex", "config.toml");
7024
7160
  }
7025
7161
  function agentsFilePath() {
7026
7162
  const override = process.env.NEAT_CODEX_AGENTS;
7027
- if (override && override.length > 0) return path15.resolve(override);
7028
- return path15.join(process.cwd(), "AGENTS.md");
7163
+ if (override && override.length > 0) return path16.resolve(override);
7164
+ return path16.join(process.cwd(), "AGENTS.md");
7029
7165
  }
7030
7166
  function isTableHeader(line) {
7031
7167
  return /^\s*\[\[?[^\]]+\]\]?\s*$/.test(line);
@@ -7159,7 +7295,7 @@ async function runCodex(opts) {
7159
7295
  const agentsPath = agentsFilePath();
7160
7296
  let configRaw = "";
7161
7297
  try {
7162
- configRaw = await fs14.readFile(configPath, "utf8");
7298
+ configRaw = await fs15.readFile(configPath, "utf8");
7163
7299
  } catch (err) {
7164
7300
  if (err.code !== "ENOENT") {
7165
7301
  console.error(`neat codex: failed to read ${configPath} \u2014 ${err.message}`);
@@ -7168,7 +7304,7 @@ async function runCodex(opts) {
7168
7304
  }
7169
7305
  let agentsRaw = "";
7170
7306
  try {
7171
- agentsRaw = await fs14.readFile(agentsPath, "utf8");
7307
+ agentsRaw = await fs15.readFile(agentsPath, "utf8");
7172
7308
  } catch (err) {
7173
7309
  if (err.code !== "ENOENT") {
7174
7310
  console.error(`neat codex: failed to read ${agentsPath} \u2014 ${err.message}`);
@@ -7208,15 +7344,15 @@ async function runCodex(opts) {
7208
7344
  return { exitCode: 0 };
7209
7345
  }
7210
7346
  if (config.changed) {
7211
- await fs14.mkdir(path15.dirname(configPath), { recursive: true });
7212
- await fs14.writeFile(configPath, config.text, "utf8");
7347
+ await fs15.mkdir(path16.dirname(configPath), { recursive: true });
7348
+ await fs15.writeFile(configPath, config.text, "utf8");
7213
7349
  console.log(`neat codex: wrote [mcp_servers.neat] to ${configPath}`);
7214
7350
  } else {
7215
7351
  console.log(`neat codex: ${configPath} already has NEAT's MCP server`);
7216
7352
  }
7217
7353
  if (agents.changed) {
7218
- await fs14.mkdir(path15.dirname(agentsPath), { recursive: true });
7219
- await fs14.writeFile(agentsPath, agents.text, "utf8");
7354
+ await fs15.mkdir(path16.dirname(agentsPath), { recursive: true });
7355
+ await fs15.writeFile(agentsPath, agents.text, "utf8");
7220
7356
  console.log(`neat codex: wrote the graph-first block to ${agentsPath}`);
7221
7357
  } else {
7222
7358
  console.log(`neat codex: ${agentsPath} already has the graph-first block`);
@@ -7272,9 +7408,9 @@ async function runCodexCommand(args) {
7272
7408
  }
7273
7409
 
7274
7410
  // src/editors-cli.ts
7275
- import path16 from "path";
7411
+ import path17 from "path";
7276
7412
  import os4 from "os";
7277
- import { promises as fs15 } from "fs";
7413
+ import { promises as fs16 } from "fs";
7278
7414
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
7279
7415
  import * as jsonc from "jsonc-parser";
7280
7416
  var NEAT_MCP_SERVER = {
@@ -7298,17 +7434,17 @@ function homeDir() {
7298
7434
  }
7299
7435
  function xdgConfigDir() {
7300
7436
  const xdg = process.env.XDG_CONFIG_HOME;
7301
- return xdg && xdg.length > 0 ? path16.resolve(xdg) : path16.join(homeDir(), ".config");
7437
+ return xdg && xdg.length > 0 ? path17.resolve(xdg) : path17.join(homeDir(), ".config");
7302
7438
  }
7303
7439
  function envOverride(name) {
7304
7440
  const v = process.env[name];
7305
- return v && v.length > 0 ? path16.resolve(v) : void 0;
7441
+ return v && v.length > 0 ? path17.resolve(v) : void 0;
7306
7442
  }
7307
7443
  var CURSOR_CLIENT = {
7308
7444
  id: "cursor",
7309
7445
  label: "Cursor",
7310
7446
  docsUrl: "https://docs.cursor.com/context/mcp",
7311
- mcpConfigPath: () => envOverride("NEAT_CURSOR_CONFIG") ?? path16.join(homeDir(), ".cursor", "mcp.json"),
7447
+ mcpConfigPath: () => envOverride("NEAT_CURSOR_CONFIG") ?? path17.join(homeDir(), ".cursor", "mcp.json"),
7312
7448
  mcpContainerKey: "mcpServers",
7313
7449
  format: "json",
7314
7450
  // Cursor still reads a single `.cursorrules` at the project root (the modern
@@ -7320,7 +7456,7 @@ var DEVIN_CLIENT = {
7320
7456
  id: "devin",
7321
7457
  label: "Devin Desktop (Cascade)",
7322
7458
  docsUrl: "https://docs.devin.ai/desktop/cascade/mcp",
7323
- mcpConfigPath: () => envOverride("NEAT_DEVIN_CONFIG") ?? path16.join(homeDir(), ".codeium", "windsurf", "mcp_config.json"),
7459
+ mcpConfigPath: () => envOverride("NEAT_DEVIN_CONFIG") ?? path17.join(homeDir(), ".codeium", "windsurf", "mcp_config.json"),
7324
7460
  mcpContainerKey: "mcpServers",
7325
7461
  format: "json",
7326
7462
  rulesFileName: ".windsurfrules"
@@ -7329,7 +7465,7 @@ var GEMINI_CLIENT = {
7329
7465
  id: "gemini",
7330
7466
  label: "Gemini CLI",
7331
7467
  docsUrl: "https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md",
7332
- mcpConfigPath: () => envOverride("NEAT_GEMINI_CONFIG") ?? path16.join(homeDir(), ".gemini", "settings.json"),
7468
+ mcpConfigPath: () => envOverride("NEAT_GEMINI_CONFIG") ?? path17.join(homeDir(), ".gemini", "settings.json"),
7333
7469
  mcpContainerKey: "mcpServers",
7334
7470
  format: "json",
7335
7471
  rulesFileName: "GEMINI.md"
@@ -7338,7 +7474,7 @@ var QWEN_CLIENT = {
7338
7474
  id: "qwen",
7339
7475
  label: "Qwen Code",
7340
7476
  docsUrl: "https://qwenlm.github.io/qwen-code-docs/en/users/features/mcp/",
7341
- mcpConfigPath: () => envOverride("NEAT_QWEN_CONFIG") ?? path16.join(homeDir(), ".qwen", "settings.json"),
7477
+ mcpConfigPath: () => envOverride("NEAT_QWEN_CONFIG") ?? path17.join(homeDir(), ".qwen", "settings.json"),
7342
7478
  mcpContainerKey: "mcpServers",
7343
7479
  format: "json",
7344
7480
  rulesFileName: "QWEN.md"
@@ -7347,7 +7483,7 @@ var AMAZONQ_CLIENT = {
7347
7483
  id: "amazonq",
7348
7484
  label: "Amazon Q Developer CLI",
7349
7485
  docsUrl: "https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line-mcp-configuration.html",
7350
- mcpConfigPath: () => envOverride("NEAT_AMAZONQ_CONFIG") ?? path16.join(homeDir(), ".aws", "amazonq", "mcp.json"),
7486
+ mcpConfigPath: () => envOverride("NEAT_AMAZONQ_CONFIG") ?? path17.join(homeDir(), ".aws", "amazonq", "mcp.json"),
7351
7487
  mcpContainerKey: "mcpServers",
7352
7488
  format: "json"
7353
7489
  };
@@ -7355,7 +7491,7 @@ var ROOCODE_CLIENT = {
7355
7491
  id: "roocode",
7356
7492
  label: "Roo Code",
7357
7493
  docsUrl: "https://roocodeinc.github.io/Roo-Code/features/mcp/using-mcp-in-roo",
7358
- mcpConfigPath: () => envOverride("NEAT_ROOCODE_CONFIG") ?? path16.join(process.cwd(), ".roo", "mcp.json"),
7494
+ mcpConfigPath: () => envOverride("NEAT_ROOCODE_CONFIG") ?? path17.join(process.cwd(), ".roo", "mcp.json"),
7359
7495
  mcpContainerKey: "mcpServers",
7360
7496
  format: "json"
7361
7497
  };
@@ -7368,9 +7504,9 @@ var ZED_CLIENT = {
7368
7504
  if (override) return override;
7369
7505
  if (process.platform === "win32") {
7370
7506
  const appData = process.env.APPDATA;
7371
- if (appData && appData.length > 0) return path16.join(appData, "Zed", "settings.json");
7507
+ if (appData && appData.length > 0) return path17.join(appData, "Zed", "settings.json");
7372
7508
  }
7373
- return path16.join(homeDir(), ".config", "zed", "settings.json");
7509
+ return path17.join(homeDir(), ".config", "zed", "settings.json");
7374
7510
  },
7375
7511
  mcpContainerKey: "context_servers",
7376
7512
  format: "jsonc",
@@ -7380,7 +7516,7 @@ var OPENCODE_CLIENT = {
7380
7516
  id: "opencode",
7381
7517
  label: "OpenCode",
7382
7518
  docsUrl: "https://opencode.ai/docs/mcp-servers/",
7383
- mcpConfigPath: () => envOverride("NEAT_OPENCODE_CONFIG") ?? path16.join(xdgConfigDir(), "opencode", "opencode.json"),
7519
+ mcpConfigPath: () => envOverride("NEAT_OPENCODE_CONFIG") ?? path17.join(xdgConfigDir(), "opencode", "opencode.json"),
7384
7520
  mcpContainerKey: "mcp",
7385
7521
  format: "json",
7386
7522
  serverEntry: NEAT_OPENCODE_SERVER,
@@ -7390,7 +7526,7 @@ var CRUSH_CLIENT = {
7390
7526
  id: "crush",
7391
7527
  label: "Crush",
7392
7528
  docsUrl: "https://charmbracelet-crush.mintlify.app/configuration/mcp",
7393
- mcpConfigPath: () => envOverride("NEAT_CRUSH_CONFIG") ?? path16.join(xdgConfigDir(), "crush", "crush.json"),
7529
+ mcpConfigPath: () => envOverride("NEAT_CRUSH_CONFIG") ?? path17.join(xdgConfigDir(), "crush", "crush.json"),
7394
7530
  mcpContainerKey: "mcp",
7395
7531
  format: "json",
7396
7532
  serverEntry: NEAT_CRUSH_SERVER,
@@ -7453,7 +7589,7 @@ async function planMcp(client, mcpPath) {
7453
7589
  const serverEntry = client.serverEntry ?? NEAT_MCP_SERVER;
7454
7590
  let raw = "";
7455
7591
  try {
7456
- raw = await fs15.readFile(mcpPath, "utf8");
7592
+ raw = await fs16.readFile(mcpPath, "utf8");
7457
7593
  } catch (err) {
7458
7594
  const e = err;
7459
7595
  if (e.code === "ENOENT") {
@@ -7495,7 +7631,7 @@ async function runEditorInstall(client, opts) {
7495
7631
  const mcpPath = client.mcpConfigPath();
7496
7632
  const serverEntry = client.serverEntry ?? NEAT_MCP_SERVER;
7497
7633
  const hasRules = typeof client.rulesFileName === "string";
7498
- const rulesPath = hasRules ? path16.join(opts.projectDir, client.rulesFileName) : "";
7634
+ const rulesPath = hasRules ? path17.join(opts.projectDir, client.rulesFileName) : "";
7499
7635
  const mcp = await planMcp(client, mcpPath);
7500
7636
  if (mcp === null) return { exitCode: 1 };
7501
7637
  let existingRules = "";
@@ -7504,7 +7640,7 @@ async function runEditorInstall(client, opts) {
7504
7640
  let block = "";
7505
7641
  if (hasRules) {
7506
7642
  try {
7507
- existingRules = await fs15.readFile(rulesPath, "utf8");
7643
+ existingRules = await fs16.readFile(rulesPath, "utf8");
7508
7644
  } catch (err) {
7509
7645
  if (err.code !== "ENOENT") {
7510
7646
  console.error(`neat ${client.id}: failed to read ${rulesPath} \u2014 ${err.message}`);
@@ -7538,11 +7674,11 @@ async function runEditorInstall(client, opts) {
7538
7674
  );
7539
7675
  return { exitCode: 0 };
7540
7676
  }
7541
- await fs15.mkdir(path16.dirname(mcpPath), { recursive: true });
7542
- await fs15.writeFile(mcpPath, mcp.text, "utf8");
7677
+ await fs16.mkdir(path17.dirname(mcpPath), { recursive: true });
7678
+ await fs16.writeFile(mcpPath, mcp.text, "utf8");
7543
7679
  if (hasRules) {
7544
- await fs15.mkdir(path16.dirname(rulesPath), { recursive: true });
7545
- await fs15.writeFile(rulesPath, newRules, "utf8");
7680
+ await fs16.mkdir(path17.dirname(rulesPath), { recursive: true });
7681
+ await fs16.writeFile(rulesPath, newRules, "utf8");
7546
7682
  }
7547
7683
  console.log(`neat ${client.id}: wired NEAT into ${client.label}`);
7548
7684
  console.log(` MCP server: ${mcpPath} (${client.mcpContainerKey}.neat \u2192 npx -y @neat.is/mcp)`);
@@ -8042,7 +8178,7 @@ function sleep(ms, signal) {
8042
8178
  }
8043
8179
 
8044
8180
  // src/cli-verbs.ts
8045
- import path17 from "path";
8181
+ import path18 from "path";
8046
8182
  async function resolveProjectEntry(opts) {
8047
8183
  const entries = await listProjects();
8048
8184
  if (opts.project) {
@@ -8052,7 +8188,7 @@ async function resolveProjectEntry(opts) {
8052
8188
  const cwd = opts.cwd ?? process.cwd();
8053
8189
  const resolvedCwd = await normalizeProjectPath(cwd);
8054
8190
  for (const entry2 of entries) {
8055
- if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${path17.sep}`)) {
8191
+ if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${path18.sep}`)) {
8056
8192
  return entry2;
8057
8193
  }
8058
8194
  }
@@ -8629,7 +8765,7 @@ async function buildPatchSections(services, project) {
8629
8765
  }
8630
8766
  async function runInit(opts) {
8631
8767
  const written = [];
8632
- const stat = await fs16.stat(opts.scanPath).catch(() => null);
8768
+ const stat = await fs17.stat(opts.scanPath).catch(() => null);
8633
8769
  if (!stat || !stat.isDirectory()) {
8634
8770
  console.error(`neat init: ${opts.scanPath} is not a directory`);
8635
8771
  return { exitCode: 2, writtenFiles: written };
@@ -8638,13 +8774,13 @@ async function runInit(opts) {
8638
8774
  printDiscoveryReport(opts, services);
8639
8775
  const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
8640
8776
  const patch = renderPatch(sections);
8641
- const patchPath = path18.join(opts.scanPath, "neat.patch");
8777
+ const patchPath = path19.join(opts.scanPath, "neat.patch");
8642
8778
  if (opts.dryRun) {
8643
- await fs16.writeFile(patchPath, patch, "utf8");
8779
+ await fs17.writeFile(patchPath, patch, "utf8");
8644
8780
  written.push(patchPath);
8645
8781
  console.log(`dry-run: patch written to ${patchPath}`);
8646
- const gitignorePath = path18.join(opts.scanPath, ".gitignore");
8647
- const gitignoreExists = await fs16.stat(gitignorePath).then(() => true).catch(() => false);
8782
+ const gitignorePath = path19.join(opts.scanPath, ".gitignore");
8783
+ const gitignoreExists = await fs17.stat(gitignorePath).then(() => true).catch(() => false);
8648
8784
  const verb = gitignoreExists ? "append" : "create";
8649
8785
  console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
8650
8786
  console.log("rerun without --dry-run to register and snapshot.");
@@ -8655,9 +8791,9 @@ async function runInit(opts) {
8655
8791
  const graph = getGraph(graphKey);
8656
8792
  const projectPaths = pathsForProject(
8657
8793
  graphKey,
8658
- path18.join(opts.scanPath, "neat-out")
8794
+ path19.join(opts.scanPath, "neat-out")
8659
8795
  );
8660
- const errorsPath = path18.join(path18.dirname(opts.outPath), path18.basename(projectPaths.errorsPath));
8796
+ const errorsPath = path19.join(path19.dirname(opts.outPath), path19.basename(projectPaths.errorsPath));
8661
8797
  const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
8662
8798
  await saveGraphToDisk(graph, opts.outPath);
8663
8799
  written.push(opts.outPath);
@@ -8736,7 +8872,7 @@ async function runInit(opts) {
8736
8872
  console.log("Run `npm install` (or your language equivalent) to refresh lockfiles.");
8737
8873
  }
8738
8874
  } else {
8739
- await fs16.writeFile(patchPath, patch, "utf8");
8875
+ await fs17.writeFile(patchPath, patch, "utf8");
8740
8876
  written.push(patchPath);
8741
8877
  }
8742
8878
  }
@@ -8777,9 +8913,9 @@ var CLAUDE_SKILL_CONFIG = {
8777
8913
  };
8778
8914
  function claudeConfigPath() {
8779
8915
  const override = process.env.NEAT_CLAUDE_CONFIG;
8780
- if (override && override.length > 0) return path18.resolve(override);
8916
+ if (override && override.length > 0) return path19.resolve(override);
8781
8917
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
8782
- return path18.join(home, ".claude.json");
8918
+ return path19.join(home, ".claude.json");
8783
8919
  }
8784
8920
  async function runSkill(opts) {
8785
8921
  const snippet = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
@@ -8791,7 +8927,7 @@ async function runSkill(opts) {
8791
8927
  const target = claudeConfigPath();
8792
8928
  let existing = {};
8793
8929
  try {
8794
- existing = JSON.parse(await fs16.readFile(target, "utf8"));
8930
+ existing = JSON.parse(await fs17.readFile(target, "utf8"));
8795
8931
  } catch (err) {
8796
8932
  if (err.code !== "ENOENT") {
8797
8933
  console.error(`neat skill: failed to read ${target} \u2014 ${err.message}`);
@@ -8803,8 +8939,8 @@ async function runSkill(opts) {
8803
8939
  ...existing,
8804
8940
  mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
8805
8941
  };
8806
- await fs16.mkdir(path18.dirname(target), { recursive: true });
8807
- await fs16.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
8942
+ await fs17.mkdir(path19.dirname(target), { recursive: true });
8943
+ await fs17.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
8808
8944
  console.log(`neat skill: wrote mcpServers.neat to ${target}`);
8809
8945
  console.log("restart Claude Code to pick up the new MCP server.");
8810
8946
  console.log("");
@@ -8830,7 +8966,7 @@ async function runSkill(opts) {
8830
8966
  async function main() {
8831
8967
  const argv = process.argv.slice(2);
8832
8968
  const cmd0 = argv[0];
8833
- if (cmd0 === "-h" || cmd0 === "--help") {
8969
+ if (argv.some((a) => a === "-h" || a === "--help")) {
8834
8970
  usage5();
8835
8971
  process.exit(0);
8836
8972
  }
@@ -8925,12 +9061,12 @@ async function main() {
8925
9061
  console.error("neat init: --apply and --dry-run are mutually exclusive");
8926
9062
  process.exit(2);
8927
9063
  }
8928
- const scanPath = path18.resolve(target);
9064
+ const scanPath = path19.resolve(target);
8929
9065
  const projectExplicit = parsed.project !== null;
8930
- const projectName = projectExplicit ? project : path18.basename(scanPath);
9066
+ const projectName = projectExplicit ? project : path19.basename(scanPath);
8931
9067
  const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
8932
- const fallback = pathsForProject(projectKey, path18.join(scanPath, "neat-out")).snapshotPath;
8933
- const outPath = path18.resolve(process.env.NEAT_OUT_PATH ?? fallback);
9068
+ const fallback = pathsForProject(projectKey, path19.join(scanPath, "neat-out")).snapshotPath;
9069
+ const outPath = path19.resolve(process.env.NEAT_OUT_PATH ?? fallback);
8934
9070
  const result = await runInit({
8935
9071
  scanPath,
8936
9072
  outPath,
@@ -8951,21 +9087,21 @@ async function main() {
8951
9087
  usage5();
8952
9088
  process.exit(2);
8953
9089
  }
8954
- const scanPath = path18.resolve(target);
8955
- const stat = await fs16.stat(scanPath).catch(() => null);
9090
+ const scanPath = path19.resolve(target);
9091
+ const stat = await fs17.stat(scanPath).catch(() => null);
8956
9092
  if (!stat || !stat.isDirectory()) {
8957
9093
  console.error(`neat watch: ${scanPath} is not a directory`);
8958
9094
  process.exit(2);
8959
9095
  }
8960
- const projectPaths = pathsForProject(project, path18.join(scanPath, "neat-out"));
8961
- const outPath = path18.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
8962
- const errorsPath = path18.resolve(
8963
- process.env.NEAT_ERRORS_PATH ?? path18.join(path18.dirname(outPath), path18.basename(projectPaths.errorsPath))
9096
+ const projectPaths = pathsForProject(project, path19.join(scanPath, "neat-out"));
9097
+ const outPath = path19.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
9098
+ const errorsPath = path19.resolve(
9099
+ process.env.NEAT_ERRORS_PATH ?? path19.join(path19.dirname(outPath), path19.basename(projectPaths.errorsPath))
8964
9100
  );
8965
- const staleEventsPath = path18.resolve(
8966
- process.env.NEAT_STALE_EVENTS_PATH ?? path18.join(path18.dirname(outPath), path18.basename(projectPaths.staleEventsPath))
9101
+ const staleEventsPath = path19.resolve(
9102
+ process.env.NEAT_STALE_EVENTS_PATH ?? path19.join(path19.dirname(outPath), path19.basename(projectPaths.staleEventsPath))
8967
9103
  );
8968
- const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? path18.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
9104
+ const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? path19.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
8969
9105
  const handle = await startWatch(getGraph(project), {
8970
9106
  scanPath,
8971
9107
  outPath,
@@ -8974,7 +9110,7 @@ async function main() {
8974
9110
  project,
8975
9111
  // Resolve NEAT_HOME so a `neat watch` picks up connectors added to
8976
9112
  // ~/.neat/connectors.json (#871). Same resolution the rest of the CLI uses.
8977
- neatHome: process.env.NEAT_HOME ? path18.resolve(process.env.NEAT_HOME) : path18.join(os5.homedir(), ".neat"),
9113
+ neatHome: process.env.NEAT_HOME ? path19.resolve(process.env.NEAT_HOME) : path19.join(os5.homedir(), ".neat"),
8978
9114
  ...embeddingsCachePath ? { embeddingsCachePath } : {},
8979
9115
  host: process.env.HOST ?? "0.0.0.0",
8980
9116
  port: Number(process.env.PORT ?? 8080),
@@ -9156,11 +9292,11 @@ async function main() {
9156
9292
  process.exit(1);
9157
9293
  }
9158
9294
  async function tryOrchestrator(cmd, parsed) {
9159
- const scanPath = path18.resolve(cmd);
9160
- const stat = await fs16.stat(scanPath).catch(() => null);
9295
+ const scanPath = path19.resolve(cmd);
9296
+ const stat = await fs17.stat(scanPath).catch(() => null);
9161
9297
  if (!stat || !stat.isDirectory()) return null;
9162
9298
  const projectExplicit = parsed.project !== null;
9163
- const projectName = projectExplicit ? parsed.project : path18.basename(scanPath);
9299
+ const projectName = projectExplicit ? parsed.project : path19.basename(scanPath);
9164
9300
  const result = await runOrchestrator({
9165
9301
  scanPath,
9166
9302
  project: projectName,