@neat.is/core 0.7.4 → 0.7.5

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
@@ -6,7 +6,7 @@ import {
6
6
  resolveHost,
7
7
  resolveNeatVersion,
8
8
  writeDaemonRecord
9
- } from "./chunk-KN6O7KMK.js";
9
+ } from "./chunk-MYLUXVXE.js";
10
10
  import {
11
11
  buildSearchIndex
12
12
  } from "./chunk-BIY46Q6U.js";
@@ -74,7 +74,7 @@ import {
74
74
  startStalenessLoop,
75
75
  upsertConnectorEntry,
76
76
  validateConnectorEntry
77
- } from "./chunk-UU4XKSTO.js";
77
+ } from "./chunk-KB4HRB6N.js";
78
78
  import {
79
79
  startOtelGrpcReceiver
80
80
  } from "./chunk-6H757ZNM.js";
@@ -88,9 +88,9 @@ import {
88
88
  } from "./chunk-P2ZEKJ35.js";
89
89
 
90
90
  // src/cli.ts
91
- import path11 from "path";
92
- import os2 from "os";
93
- import { promises as fs9 } from "fs";
91
+ import path13 from "path";
92
+ import os4 from "os";
93
+ import { promises as fs11 } from "fs";
94
94
 
95
95
  // src/banner.ts
96
96
  import path from "path";
@@ -4471,6 +4471,453 @@ async function runHooksCommand(args) {
4471
4471
  }
4472
4472
  }
4473
4473
 
4474
+ // src/codex-cli.ts
4475
+ import path10 from "path";
4476
+ import os2 from "os";
4477
+ import { promises as fs9 } from "fs";
4478
+ import { isDeepStrictEqual } from "util";
4479
+ import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
4480
+ var CODEX_MCP_SERVER = {
4481
+ command: "npx",
4482
+ args: ["-y", "@neat.is/mcp"],
4483
+ env: { NEAT_CORE_URL: "http://localhost:8080" }
4484
+ };
4485
+ var CODEX_NEAT_BLOCK = [
4486
+ "[mcp_servers.neat]",
4487
+ 'command = "npx"',
4488
+ 'args = ["-y", "@neat.is/mcp"]',
4489
+ 'env = { NEAT_CORE_URL = "http://localhost:8080" }'
4490
+ ].join("\n");
4491
+ var NEAT_GRAPH_FIRST_START = "<!-- neat:graph-first -->";
4492
+ var NEAT_GRAPH_FIRST_END = "<!-- /neat:graph-first -->";
4493
+ function codexConfigPath() {
4494
+ const override = process.env.NEAT_CODEX_CONFIG;
4495
+ if (override && override.length > 0) return path10.resolve(override);
4496
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? os2.homedir();
4497
+ return path10.join(home, ".codex", "config.toml");
4498
+ }
4499
+ function agentsFilePath() {
4500
+ const override = process.env.NEAT_CODEX_AGENTS;
4501
+ if (override && override.length > 0) return path10.resolve(override);
4502
+ return path10.join(process.cwd(), "AGENTS.md");
4503
+ }
4504
+ function isTableHeader(line) {
4505
+ return /^\s*\[\[?[^\]]+\]\]?\s*$/.test(line);
4506
+ }
4507
+ function tableName(line) {
4508
+ return line.trim().replace(/^\[\[?/, "").replace(/\]\]?$/, "").trim();
4509
+ }
4510
+ function isNeatHeader(line) {
4511
+ return isTableHeader(line) && tableName(line) === "mcp_servers.neat";
4512
+ }
4513
+ function isNeatChildHeader(line) {
4514
+ if (!isTableHeader(line)) return false;
4515
+ const name = tableName(line);
4516
+ return name === "mcp_servers.neat" || name.startsWith("mcp_servers.neat.");
4517
+ }
4518
+ function upsertCodexConfig(raw) {
4519
+ const trimmed = raw.trim();
4520
+ const parsed = trimmed.length > 0 ? parseToml(raw) : {};
4521
+ const existingServers = parsed.mcp_servers ?? {};
4522
+ const spliced = spliceNeatBlock(raw);
4523
+ let text;
4524
+ if (verifyPreserved(raw, spliced, parsed)) {
4525
+ text = spliced;
4526
+ } else {
4527
+ const merged = {
4528
+ ...parsed,
4529
+ mcp_servers: { ...existingServers, neat: CODEX_MCP_SERVER }
4530
+ };
4531
+ text = stringifyToml(merged);
4532
+ if (!text.endsWith("\n")) text += "\n";
4533
+ }
4534
+ return { text, changed: text !== raw };
4535
+ }
4536
+ function spliceNeatBlock(raw) {
4537
+ const block = CODEX_NEAT_BLOCK;
4538
+ const lines = raw.length > 0 ? raw.split("\n") : [];
4539
+ const start = lines.findIndex(isNeatHeader);
4540
+ if (start === -1) {
4541
+ const base = raw.replace(/\n+$/, "");
4542
+ return base.length > 0 ? `${base}
4543
+
4544
+ ${block}
4545
+ ` : `${block}
4546
+ `;
4547
+ }
4548
+ let end = start + 1;
4549
+ for (; ; ) {
4550
+ while (end < lines.length && !isTableHeader(lines[end])) end++;
4551
+ if (end < lines.length && isNeatChildHeader(lines[end])) {
4552
+ end++;
4553
+ continue;
4554
+ }
4555
+ break;
4556
+ }
4557
+ const before = lines.slice(0, start).join("\n").replace(/\n*$/, "");
4558
+ const after = lines.slice(end).join("\n").replace(/^\n*/, "");
4559
+ let text = "";
4560
+ if (before.length > 0) text += `${before}
4561
+
4562
+ `;
4563
+ text += `${block}
4564
+ `;
4565
+ if (after.length > 0) text += `
4566
+ ${after}`;
4567
+ return `${text.replace(/\n*$/, "")}
4568
+ `;
4569
+ }
4570
+ function verifyPreserved(raw, spliced, original) {
4571
+ let next;
4572
+ try {
4573
+ next = parseToml(spliced);
4574
+ } catch {
4575
+ return false;
4576
+ }
4577
+ const origServers = original.mcp_servers ?? {};
4578
+ const nextServers = next.mcp_servers ?? {};
4579
+ if (!isDeepStrictEqual(nextServers.neat, CODEX_MCP_SERVER)) return false;
4580
+ for (const name of Object.keys(origServers)) {
4581
+ if (name === "neat") continue;
4582
+ if (!isDeepStrictEqual(nextServers[name], origServers[name])) return false;
4583
+ }
4584
+ for (const name of Object.keys(nextServers)) {
4585
+ if (name !== "neat" && !(name in origServers)) return false;
4586
+ }
4587
+ for (const key of Object.keys(original)) {
4588
+ if (key === "mcp_servers") continue;
4589
+ if (!isDeepStrictEqual(next[key], original[key])) return false;
4590
+ }
4591
+ for (const key of Object.keys(next)) {
4592
+ if (key !== "mcp_servers" && !(key in original)) return false;
4593
+ }
4594
+ return true;
4595
+ }
4596
+ function agentsBlock(guide) {
4597
+ return `${NEAT_GRAPH_FIRST_START}
4598
+ ${guide.replace(/\s+$/, "")}
4599
+ ${NEAT_GRAPH_FIRST_END}
4600
+ `;
4601
+ }
4602
+ function upsertAgents(raw, guide) {
4603
+ const block = agentsBlock(guide);
4604
+ if (raw.length === 0) return { text: block, changed: true };
4605
+ const startIdx = raw.indexOf(NEAT_GRAPH_FIRST_START);
4606
+ const endIdx = raw.indexOf(NEAT_GRAPH_FIRST_END);
4607
+ if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
4608
+ const before = raw.slice(0, startIdx);
4609
+ const after = raw.slice(endIdx + NEAT_GRAPH_FIRST_END.length);
4610
+ const text2 = `${before}${block.replace(/\n+$/, "")}${after}`;
4611
+ return { text: text2, changed: text2 !== raw };
4612
+ }
4613
+ const base = raw.replace(/\n+$/, "");
4614
+ const text = base.length > 0 ? `${base}
4615
+
4616
+ ${block}` : block;
4617
+ return { text, changed: text !== raw };
4618
+ }
4619
+ async function readGuide() {
4620
+ return readSkillAsset(GUIDE_FILENAME);
4621
+ }
4622
+ async function runCodex(opts) {
4623
+ if (opts.printConfig) {
4624
+ process.stdout.write(`${CODEX_NEAT_BLOCK}
4625
+ `);
4626
+ return { exitCode: 0 };
4627
+ }
4628
+ if (opts.printGuide) {
4629
+ process.stdout.write(agentsBlock(await readGuide()));
4630
+ return { exitCode: 0 };
4631
+ }
4632
+ const configPath = codexConfigPath();
4633
+ const agentsPath = agentsFilePath();
4634
+ let configRaw = "";
4635
+ try {
4636
+ configRaw = await fs9.readFile(configPath, "utf8");
4637
+ } catch (err) {
4638
+ if (err.code !== "ENOENT") {
4639
+ console.error(`neat codex: failed to read ${configPath} \u2014 ${err.message}`);
4640
+ return { exitCode: 1 };
4641
+ }
4642
+ }
4643
+ let agentsRaw = "";
4644
+ try {
4645
+ agentsRaw = await fs9.readFile(agentsPath, "utf8");
4646
+ } catch (err) {
4647
+ if (err.code !== "ENOENT") {
4648
+ console.error(`neat codex: failed to read ${agentsPath} \u2014 ${err.message}`);
4649
+ return { exitCode: 1 };
4650
+ }
4651
+ }
4652
+ let config;
4653
+ try {
4654
+ config = upsertCodexConfig(configRaw);
4655
+ } catch (err) {
4656
+ console.error(
4657
+ `neat codex: ${configPath} is not valid TOML \u2014 ${err.message}`
4658
+ );
4659
+ console.error("neat codex: fix the file and re-run; nothing was written.");
4660
+ return { exitCode: 1 };
4661
+ }
4662
+ const guide = await readGuide();
4663
+ const agents = upsertAgents(agentsRaw, guide);
4664
+ if (!opts.apply) {
4665
+ console.log("neat codex \u2014 plan (nothing written; re-run with --apply to write)");
4666
+ console.log("");
4667
+ console.log(` Codex MCP config: ${configPath}`);
4668
+ console.log(
4669
+ config.changed ? ` ${configRaw ? "update" : "create"} the [mcp_servers.neat] table:` : " already up to date \u2014 [mcp_servers.neat] matches"
4670
+ );
4671
+ if (config.changed) {
4672
+ for (const line of CODEX_NEAT_BLOCK.split("\n")) console.log(` ${line}`);
4673
+ }
4674
+ console.log("");
4675
+ console.log(` Project instructions: ${agentsPath}`);
4676
+ console.log(
4677
+ agents.changed ? ` ${agentsRaw ? "update" : "create"} the graph-first block (between ${NEAT_GRAPH_FIRST_START} markers)` : " already up to date \u2014 graph-first block matches"
4678
+ );
4679
+ console.log("");
4680
+ console.log("The MCP server reads NEAT_CORE_URL for the daemon URL \u2014 edit that value in");
4681
+ console.log("the generated table to point Codex at a non-default daemon.");
4682
+ return { exitCode: 0 };
4683
+ }
4684
+ if (config.changed) {
4685
+ await fs9.mkdir(path10.dirname(configPath), { recursive: true });
4686
+ await fs9.writeFile(configPath, config.text, "utf8");
4687
+ console.log(`neat codex: wrote [mcp_servers.neat] to ${configPath}`);
4688
+ } else {
4689
+ console.log(`neat codex: ${configPath} already has NEAT's MCP server`);
4690
+ }
4691
+ if (agents.changed) {
4692
+ await fs9.mkdir(path10.dirname(agentsPath), { recursive: true });
4693
+ await fs9.writeFile(agentsPath, agents.text, "utf8");
4694
+ console.log(`neat codex: wrote the graph-first block to ${agentsPath}`);
4695
+ } else {
4696
+ console.log(`neat codex: ${agentsPath} already has the graph-first block`);
4697
+ }
4698
+ console.log("");
4699
+ console.log("restart Codex to pick up the new MCP server. NEAT_CORE_URL in the table");
4700
+ console.log("points the server at the local daemon \u2014 edit it for a non-default one.");
4701
+ return { exitCode: 0 };
4702
+ }
4703
+ function usage2() {
4704
+ console.log("neat codex \u2014 install NEAT into the OpenAI Codex CLI (MCP server + AGENTS.md)");
4705
+ console.log("");
4706
+ console.log(" (no flag) plan: print what would change, write nothing");
4707
+ console.log(" --apply add [mcp_servers.neat] to ~/.codex/config.toml and write");
4708
+ console.log(" the graph-first block into ./AGENTS.md, merging into both");
4709
+ console.log(" without touching your other servers or instructions");
4710
+ console.log(" --print-config print the [mcp_servers.neat] TOML block to stdout");
4711
+ console.log(" --print-guide print the AGENTS.md graph-first block to stdout");
4712
+ console.log("");
4713
+ console.log("Existing config is preserved and a re-run is a no-op. A malformed");
4714
+ console.log("config.toml is a clear error with no partial write.");
4715
+ }
4716
+ async function runCodexCommand(args) {
4717
+ const opts = { apply: false, printConfig: false, printGuide: false };
4718
+ for (const arg of args) {
4719
+ switch (arg) {
4720
+ case "--apply":
4721
+ opts.apply = true;
4722
+ break;
4723
+ case "--print-config":
4724
+ opts.printConfig = true;
4725
+ break;
4726
+ case "--print-guide":
4727
+ opts.printGuide = true;
4728
+ break;
4729
+ case "-h":
4730
+ case "--help":
4731
+ usage2();
4732
+ return 0;
4733
+ default:
4734
+ console.error(`neat codex: unknown flag "${arg}"`);
4735
+ usage2();
4736
+ return 2;
4737
+ }
4738
+ }
4739
+ try {
4740
+ const { exitCode } = await runCodex(opts);
4741
+ return exitCode;
4742
+ } catch (err) {
4743
+ console.error(err.message);
4744
+ return 1;
4745
+ }
4746
+ }
4747
+
4748
+ // src/editors-cli.ts
4749
+ import path11 from "path";
4750
+ import os3 from "os";
4751
+ import { promises as fs10 } from "fs";
4752
+ var NEAT_MCP_SERVER = {
4753
+ command: "npx",
4754
+ args: ["-y", "@neat.is/mcp"]
4755
+ };
4756
+ var GRAPH_FIRST_MARKER_OPEN = "<!-- neat:graph-first -->";
4757
+ var GRAPH_FIRST_MARKER_CLOSE = "<!-- /neat:graph-first -->";
4758
+ function homeDir() {
4759
+ return process.env.HOME ?? process.env.USERPROFILE ?? os3.homedir();
4760
+ }
4761
+ var CURSOR_CLIENT = {
4762
+ id: "cursor",
4763
+ label: "Cursor",
4764
+ docsUrl: "https://docs.cursor.com/context/mcp",
4765
+ mcpConfigPath: () => {
4766
+ const override = process.env.NEAT_CURSOR_CONFIG;
4767
+ if (override && override.length > 0) return path11.resolve(override);
4768
+ return path11.join(homeDir(), ".cursor", "mcp.json");
4769
+ },
4770
+ // Cursor still reads a single `.cursorrules` at the project root (the modern
4771
+ // `.cursor/rules/*.mdc` split is one-rule-per-file with frontmatter — a worse
4772
+ // fit for a marker-fenced block). GRAPH_FIRST.md names this file directly.
4773
+ rulesFileName: ".cursorrules"
4774
+ };
4775
+ var DEVIN_CLIENT = {
4776
+ id: "devin",
4777
+ label: "Devin Desktop (Cascade)",
4778
+ docsUrl: "https://docs.devin.ai/desktop/cascade/mcp",
4779
+ mcpConfigPath: () => {
4780
+ const override = process.env.NEAT_DEVIN_CONFIG;
4781
+ if (override && override.length > 0) return path11.resolve(override);
4782
+ return path11.join(homeDir(), ".codeium", "windsurf", "mcp_config.json");
4783
+ },
4784
+ rulesFileName: ".windsurfrules"
4785
+ };
4786
+ function mergeMcpConfig(existing) {
4787
+ const servers = existing.mcpServers ?? {};
4788
+ const already = JSON.stringify(servers.neat) === JSON.stringify(NEAT_MCP_SERVER);
4789
+ const merged = {
4790
+ ...existing,
4791
+ mcpServers: { ...servers, neat: NEAT_MCP_SERVER }
4792
+ };
4793
+ return { merged, changed: !already };
4794
+ }
4795
+ function escapeRegExp(s) {
4796
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4797
+ }
4798
+ function buildGuidanceBlock(guide) {
4799
+ return `${GRAPH_FIRST_MARKER_OPEN}
4800
+ ${guide.trim()}
4801
+ ${GRAPH_FIRST_MARKER_CLOSE}
4802
+ `;
4803
+ }
4804
+ function mergeRulesFile(existing, block) {
4805
+ const region = new RegExp(
4806
+ `${escapeRegExp(GRAPH_FIRST_MARKER_OPEN)}[\\s\\S]*?${escapeRegExp(GRAPH_FIRST_MARKER_CLOSE)}\\n?`
4807
+ );
4808
+ if (region.test(existing)) return existing.replace(region, block);
4809
+ if (existing.trim().length === 0) return block;
4810
+ return `${existing.replace(/\s+$/, "")}
4811
+
4812
+ ${block}`;
4813
+ }
4814
+ async function runEditorInstall(client, opts) {
4815
+ const mcpPath = client.mcpConfigPath();
4816
+ const rulesPath = path11.join(opts.projectDir, client.rulesFileName);
4817
+ let existingMcp = {};
4818
+ try {
4819
+ existingMcp = JSON.parse(await fs10.readFile(mcpPath, "utf8"));
4820
+ } catch (err) {
4821
+ const e = err;
4822
+ if (e.code === "ENOENT") {
4823
+ existingMcp = {};
4824
+ } else if (err instanceof SyntaxError) {
4825
+ console.error(
4826
+ `neat ${client.id}: ${mcpPath} is not valid JSON \u2014 ${e.message}. Fix it (or move it aside) and re-run; nothing was written.`
4827
+ );
4828
+ return { exitCode: 1 };
4829
+ } else {
4830
+ console.error(`neat ${client.id}: failed to read ${mcpPath} \u2014 ${e.message}`);
4831
+ return { exitCode: 1 };
4832
+ }
4833
+ }
4834
+ let existingRules = "";
4835
+ try {
4836
+ existingRules = await fs10.readFile(rulesPath, "utf8");
4837
+ } catch (err) {
4838
+ if (err.code !== "ENOENT") {
4839
+ console.error(`neat ${client.id}: failed to read ${rulesPath} \u2014 ${err.message}`);
4840
+ return { exitCode: 1 };
4841
+ }
4842
+ }
4843
+ const guide = await readSkillAsset(GUIDE_FILENAME);
4844
+ const block = buildGuidanceBlock(guide);
4845
+ const { merged, changed: mcpChanged } = mergeMcpConfig(existingMcp);
4846
+ const mcpJson = JSON.stringify(merged, null, 2) + "\n";
4847
+ const newRules = mergeRulesFile(existingRules, block);
4848
+ const rulesChanged = newRules !== existingRules;
4849
+ if (!opts.apply) {
4850
+ console.log(`neat ${client.id} \u2014 wire NEAT into ${client.label} (plan; nothing written)`);
4851
+ console.log("");
4852
+ console.log(`MCP server \u2192 ${mcpPath}`);
4853
+ console.log(
4854
+ mcpChanged ? " would add mcpServers.neat:" : " mcpServers.neat already present and current \u2014 no change:"
4855
+ );
4856
+ console.log(indent(JSON.stringify({ neat: NEAT_MCP_SERVER }, null, 2)));
4857
+ console.log("");
4858
+ console.log(`Graph-first guidance \u2192 ${rulesPath}`);
4859
+ console.log(
4860
+ rulesChanged ? existingRules.includes(GRAPH_FIRST_MARKER_OPEN) ? " would refresh the neat:graph-first block:" : " would add the neat:graph-first block:" : " neat:graph-first block already present and current \u2014 no change."
4861
+ );
4862
+ if (rulesChanged) console.log(indent(block.trimEnd()));
4863
+ console.log("");
4864
+ console.log(`Re-run with --apply to write both files. Existing servers and rules are kept.`);
4865
+ return { exitCode: 0 };
4866
+ }
4867
+ await fs10.mkdir(path11.dirname(mcpPath), { recursive: true });
4868
+ await fs10.writeFile(mcpPath, mcpJson, "utf8");
4869
+ await fs10.mkdir(path11.dirname(rulesPath), { recursive: true });
4870
+ await fs10.writeFile(rulesPath, newRules, "utf8");
4871
+ console.log(`neat ${client.id}: wired NEAT into ${client.label}`);
4872
+ console.log(` MCP server: ${mcpPath} (mcpServers.neat \u2192 npx -y @neat.is/mcp)`);
4873
+ console.log(` guidance: ${rulesPath} (neat:graph-first block)`);
4874
+ console.log("");
4875
+ console.log(`restart ${client.label} to pick up the MCP server. Point it at a non-default`);
4876
+ console.log(`daemon by setting NEAT_CORE_URL in the neat server's env in that config.`);
4877
+ return { exitCode: 0 };
4878
+ }
4879
+ function indent(text) {
4880
+ return text.split("\n").map((line) => line.length > 0 ? ` ${line}` : line).join("\n");
4881
+ }
4882
+ function usage3(client) {
4883
+ console.log(`neat ${client.id} \u2014 install NEAT's MCP server + graph-first guidance into ${client.label}`);
4884
+ console.log("");
4885
+ console.log(" --apply write the MCP config and the rules file (default: plan only)");
4886
+ console.log("");
4887
+ console.log("Writes NEAT's stdio MCP server (npx -y @neat.is/mcp) into");
4888
+ console.log(` ${client.mcpConfigPath()}`);
4889
+ console.log(`and the graph-first guidance block into ./${client.rulesFileName}, both`);
4890
+ console.log("additively \u2014 existing servers and rules are preserved, a re-run is a no-op.");
4891
+ console.log("");
4892
+ console.log(`See ${client.docsUrl} for ${client.label}'s MCP config format.`);
4893
+ }
4894
+ async function runEditorCommand(clientId, args, projectDir = process.cwd()) {
4895
+ const client = clientId === "cursor" ? CURSOR_CLIENT : DEVIN_CLIENT;
4896
+ let apply4 = false;
4897
+ for (const arg of args) {
4898
+ switch (arg) {
4899
+ case "--apply":
4900
+ apply4 = true;
4901
+ break;
4902
+ case "-h":
4903
+ case "--help":
4904
+ usage3(client);
4905
+ return 0;
4906
+ default:
4907
+ console.error(`neat ${client.id}: unknown flag "${arg}"`);
4908
+ usage3(client);
4909
+ return 2;
4910
+ }
4911
+ }
4912
+ try {
4913
+ const { exitCode } = await runEditorInstall(client, { apply: apply4, projectDir });
4914
+ return exitCode;
4915
+ } catch (err) {
4916
+ console.error(err.message);
4917
+ return 1;
4918
+ }
4919
+ }
4920
+
4474
4921
  // src/monitor.ts
4475
4922
  import { EdgeType, parseEdgeId, Provenance as Provenance3 } from "@neat.is/types";
4476
4923
 
@@ -4500,10 +4947,10 @@ function createHttpClient(baseUrl, bearerToken) {
4500
4947
  const root = baseUrl.replace(/\/$/, "");
4501
4948
  const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
4502
4949
  return {
4503
- async get(path12) {
4950
+ async get(path14) {
4504
4951
  let res;
4505
4952
  try {
4506
- res = await fetch(`${root}${path12}`, {
4953
+ res = await fetch(`${root}${path14}`, {
4507
4954
  headers: { ...authHeader }
4508
4955
  });
4509
4956
  } catch (err) {
@@ -4515,16 +4962,16 @@ function createHttpClient(baseUrl, bearerToken) {
4515
4962
  const body = await res.text().catch(() => "");
4516
4963
  throw new HttpError(
4517
4964
  res.status,
4518
- `${res.status} ${res.statusText} on GET ${path12}: ${body}`,
4965
+ `${res.status} ${res.statusText} on GET ${path14}: ${body}`,
4519
4966
  body
4520
4967
  );
4521
4968
  }
4522
4969
  return await res.json();
4523
4970
  },
4524
- async post(path12, body) {
4971
+ async post(path14, body) {
4525
4972
  let res;
4526
4973
  try {
4527
- res = await fetch(`${root}${path12}`, {
4974
+ res = await fetch(`${root}${path14}`, {
4528
4975
  method: "POST",
4529
4976
  headers: { "content-type": "application/json", ...authHeader },
4530
4977
  body: JSON.stringify(body)
@@ -4538,7 +4985,7 @@ function createHttpClient(baseUrl, bearerToken) {
4538
4985
  const text = await res.text().catch(() => "");
4539
4986
  throw new HttpError(
4540
4987
  res.status,
4541
- `${res.status} ${res.statusText} on POST ${path12}: ${text}`,
4988
+ `${res.status} ${res.statusText} on POST ${path14}: ${text}`,
4542
4989
  text
4543
4990
  );
4544
4991
  }
@@ -4552,12 +4999,12 @@ function projectPath(project, suffix) {
4552
4999
  }
4553
5000
  async function runRootCause(client, input) {
4554
5001
  const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
4555
- const path12 = projectPath(
5002
+ const path14 = projectPath(
4556
5003
  input.project,
4557
5004
  `/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
4558
5005
  );
4559
5006
  try {
4560
- const result = await client.get(path12);
5007
+ const result = await client.get(path14);
4561
5008
  const arrowPath = result.traversalPath.join(" \u2190 ");
4562
5009
  const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
4563
5010
  const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
@@ -4583,12 +5030,12 @@ async function runRootCause(client, input) {
4583
5030
  }
4584
5031
  async function runBlastRadius(client, input) {
4585
5032
  const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
4586
- const path12 = projectPath(
5033
+ const path14 = projectPath(
4587
5034
  input.project,
4588
5035
  `/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
4589
5036
  );
4590
5037
  try {
4591
- const result = await client.get(path12);
5038
+ const result = await client.get(path14);
4592
5039
  if (result.totalAffected === 0) {
4593
5040
  return {
4594
5041
  summary: `${result.origin} has no dependents. Nothing else would break if it failed.`
@@ -4622,12 +5069,12 @@ function formatBlastEntry(n) {
4622
5069
  }
4623
5070
  async function runDependencies(client, input) {
4624
5071
  const depth = input.depth ?? 3;
4625
- const path12 = projectPath(
5072
+ const path14 = projectPath(
4626
5073
  input.project,
4627
5074
  `/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
4628
5075
  );
4629
5076
  try {
4630
- const result = await client.get(path12);
5077
+ const result = await client.get(path14);
4631
5078
  if (result.total === 0) {
4632
5079
  return {
4633
5080
  summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
@@ -4719,9 +5166,9 @@ function formatDuration(ms) {
4719
5166
  return `${Math.round(h / 24)}d`;
4720
5167
  }
4721
5168
  async function runIncidents(client, input) {
4722
- const path12 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
5169
+ const path14 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
4723
5170
  try {
4724
- const body = await client.get(path12);
5171
+ const body = await client.get(path14);
4725
5172
  const events = body.events;
4726
5173
  if (events.length === 0) {
4727
5174
  return {
@@ -5084,6 +5531,24 @@ function observedEdgeJson(edge) {
5084
5531
  provenance: edge.provenance
5085
5532
  });
5086
5533
  }
5534
+ function policyKey(v) {
5535
+ return `policy|${v.id}`;
5536
+ }
5537
+ function policySubject(v) {
5538
+ const s = v.subject;
5539
+ if (s.nodeId) return s.nodeId;
5540
+ if (s.edgeId) return s.edgeId;
5541
+ if (s.path && s.path.length > 0) return s.path.join(" \u2192 ");
5542
+ return "";
5543
+ }
5544
+ function formatPolicyLine(v) {
5545
+ const subject = policySubject(v);
5546
+ const where = subject ? ` (${subject})` : "";
5547
+ return `\u26A0 policy [${v.severity}] ${v.policyName} \u2014 ${v.message}${where}`;
5548
+ }
5549
+ function policyJson(v) {
5550
+ return JSON.stringify({ kind: "policy", ...v });
5551
+ }
5087
5552
  var MonitorEmitter = class {
5088
5553
  constructor(opts) {
5089
5554
  this.opts = opts;
@@ -5127,6 +5592,20 @@ var MonitorEmitter = class {
5127
5592
  this.out(this.opts.json ? observedEdgeJson(edge) : formatObservedEdgeLine(edge));
5128
5593
  return true;
5129
5594
  }
5595
+ // Emit every not-yet-seen policy violation in a fresh /policies/violations
5596
+ // read. Returns the count newly emitted (0 → nothing printed, the silent
5597
+ // path). Idempotent across re-reads: the seen-set keys off the violation id.
5598
+ emitPolicies(response) {
5599
+ let emitted = 0;
5600
+ for (const v of response.violations) {
5601
+ const key = policyKey(v);
5602
+ if (this.seen.has(key)) continue;
5603
+ this.seen.add(key);
5604
+ this.out(this.opts.json ? policyJson(v) : formatPolicyLine(v));
5605
+ emitted++;
5606
+ }
5607
+ return emitted;
5608
+ }
5130
5609
  };
5131
5610
  function parseFrame(raw) {
5132
5611
  let event = "message";
@@ -5181,54 +5660,83 @@ function projectPath2(project, suffix) {
5181
5660
  function backoffDelay(attempt, capMs) {
5182
5661
  return Math.min(capMs, 500 * 2 ** Math.min(attempt, 6));
5183
5662
  }
5184
- async function runMonitor(opts) {
5185
- const write = opts.write ?? ((line) => process.stdout.write(line));
5186
- const debounceMs = opts.debounceMs ?? 400;
5187
- const backoffCapMs = opts.backoffCapMs ?? 1e4;
5188
- const maxReconnects = opts.maxReconnects ?? Number.POSITIVE_INFINITY;
5189
- const client = createHttpClient(opts.baseUrl, opts.authToken);
5190
- const emitter = new MonitorEmitter({ json: opts.json, write });
5191
- const divergencesPath = projectPath2(opts.project, "/graph/divergences");
5192
- const eventsUrl = `${opts.baseUrl.replace(/\/$/, "")}${projectPath2(opts.project, "/events")}`;
5193
- let readTimer = null;
5194
- let reading = false;
5195
- let readPending = false;
5196
- const doRead = async () => {
5197
- if (reading) {
5198
- readPending = true;
5663
+ var defaultOpenEvents = async (url, headers, signal) => {
5664
+ const res = await fetch(url, { headers, signal });
5665
+ return { ok: res.ok, status: res.status, body: res.body };
5666
+ };
5667
+ function makeDebouncedReader(read, debounceMs) {
5668
+ let timer = null;
5669
+ let running = false;
5670
+ let pending = false;
5671
+ const cancel = () => {
5672
+ if (timer) {
5673
+ clearTimeout(timer);
5674
+ timer = null;
5675
+ }
5676
+ };
5677
+ const run = async () => {
5678
+ if (running) {
5679
+ pending = true;
5199
5680
  return;
5200
5681
  }
5201
- reading = true;
5682
+ running = true;
5202
5683
  try {
5203
- const result = await client.get(divergencesPath);
5204
- emitter.emitDivergences(result);
5684
+ await read();
5205
5685
  } catch {
5206
5686
  } finally {
5207
- reading = false;
5208
- if (readPending) {
5209
- readPending = false;
5210
- scheduleRead();
5687
+ running = false;
5688
+ if (pending) {
5689
+ pending = false;
5690
+ schedule();
5211
5691
  }
5212
5692
  }
5213
5693
  };
5214
- const scheduleRead = () => {
5215
- if (readTimer) clearTimeout(readTimer);
5216
- readTimer = setTimeout(() => {
5217
- readTimer = null;
5218
- void doRead();
5694
+ const schedule = () => {
5695
+ cancel();
5696
+ timer = setTimeout(() => {
5697
+ timer = null;
5698
+ void run();
5219
5699
  }, debounceMs);
5220
- if (typeof readTimer.unref === "function") readTimer.unref();
5700
+ if (typeof timer.unref === "function") timer.unref();
5221
5701
  };
5702
+ return {
5703
+ schedule,
5704
+ runNow: async () => {
5705
+ cancel();
5706
+ await run();
5707
+ },
5708
+ cancel
5709
+ };
5710
+ }
5711
+ async function runMonitor(opts) {
5712
+ const write = opts.write ?? ((line) => process.stdout.write(line));
5713
+ const debounceMs = opts.debounceMs ?? 400;
5714
+ const backoffCapMs = opts.backoffCapMs ?? 1e4;
5715
+ const maxReconnects = opts.maxReconnects ?? Number.POSITIVE_INFINITY;
5716
+ const client = opts.httpClient ?? createHttpClient(opts.baseUrl, opts.authToken);
5717
+ const openEvents = opts.openEvents ?? defaultOpenEvents;
5718
+ const emitter = new MonitorEmitter({ json: opts.json, write });
5719
+ const divergencesPath = projectPath2(opts.project, "/graph/divergences");
5720
+ const policiesPath = projectPath2(opts.project, "/policies/violations");
5721
+ const eventsUrl = `${opts.baseUrl.replace(/\/$/, "")}${projectPath2(opts.project, "/events")}`;
5722
+ const divergences = makeDebouncedReader(async () => {
5723
+ const result = await client.get(divergencesPath);
5724
+ emitter.emitDivergences(result);
5725
+ }, debounceMs);
5726
+ const policies = makeDebouncedReader(async () => {
5727
+ const result = await client.get(policiesPath);
5728
+ emitter.emitPolicies(result);
5729
+ }, debounceMs);
5222
5730
  const onFrame = (frame) => {
5223
5731
  switch (frame.event) {
5224
5732
  case "extraction-complete":
5225
- scheduleRead();
5733
+ divergences.schedule();
5226
5734
  break;
5227
5735
  case "stale-transition": {
5228
5736
  const payload = safeParse(frame.data);
5229
5737
  const edgeId = payload && typeof payload.edgeId === "string" ? payload.edgeId : void 0;
5230
5738
  if (edgeId) emitter.emitStale(edgeId);
5231
- scheduleRead();
5739
+ divergences.schedule();
5232
5740
  break;
5233
5741
  }
5234
5742
  case "edge-added": {
@@ -5236,10 +5744,13 @@ async function runMonitor(opts) {
5236
5744
  const edge = payload?.edge;
5237
5745
  if (edge && edge.provenance === Provenance3.OBSERVED) {
5238
5746
  emitter.emitObservedEdge(edge);
5239
- scheduleRead();
5747
+ divergences.schedule();
5240
5748
  }
5241
5749
  break;
5242
5750
  }
5751
+ case "policy-violation":
5752
+ policies.schedule();
5753
+ break;
5243
5754
  default:
5244
5755
  break;
5245
5756
  }
@@ -5252,17 +5763,17 @@ async function runMonitor(opts) {
5252
5763
  let firstConnect = true;
5253
5764
  for (let attempt = 0; ; attempt++) {
5254
5765
  if (opts.signal?.aborted) break;
5255
- let res;
5766
+ let conn;
5256
5767
  try {
5257
- res = await fetch(eventsUrl, { headers, signal: opts.signal });
5768
+ conn = await openEvents(eventsUrl, headers, opts.signal);
5258
5769
  } catch (err) {
5259
5770
  if (err.name === "AbortError") break;
5260
5771
  if (!connectedOnce || attempt >= maxReconnects) break;
5261
5772
  await sleep(backoffDelay(attempt, backoffCapMs), opts.signal);
5262
5773
  continue;
5263
5774
  }
5264
- if (!res.ok || !res.body) {
5265
- await res.body?.cancel().catch(() => {
5775
+ if (!conn.ok || !conn.body) {
5776
+ await conn.body?.cancel().catch(() => {
5266
5777
  });
5267
5778
  if (!connectedOnce || attempt >= maxReconnects) break;
5268
5779
  await sleep(backoffDelay(attempt, backoffCapMs), opts.signal);
@@ -5272,19 +5783,22 @@ async function runMonitor(opts) {
5272
5783
  attempt = 0;
5273
5784
  if (firstConnect) {
5274
5785
  firstConnect = false;
5275
- await doRead();
5786
+ await divergences.runNow();
5787
+ await policies.runNow();
5276
5788
  } else {
5277
- scheduleRead();
5789
+ divergences.schedule();
5790
+ policies.schedule();
5278
5791
  }
5279
5792
  try {
5280
- await drainSse(res.body, onFrame);
5793
+ await drainSse(conn.body, onFrame);
5281
5794
  } catch {
5282
5795
  }
5283
5796
  if (opts.signal?.aborted) break;
5284
5797
  if (attempt >= maxReconnects) break;
5285
5798
  await sleep(backoffDelay(attempt, backoffCapMs), opts.signal);
5286
5799
  }
5287
- if (readTimer) clearTimeout(readTimer);
5800
+ divergences.cancel();
5801
+ policies.cancel();
5288
5802
  return 0;
5289
5803
  }
5290
5804
  function sleep(ms, signal) {
@@ -5307,7 +5821,7 @@ function sleep(ms, signal) {
5307
5821
  }
5308
5822
 
5309
5823
  // src/cli-verbs.ts
5310
- import path10 from "path";
5824
+ import path12 from "path";
5311
5825
  async function resolveProjectEntry(opts) {
5312
5826
  const entries = await listProjects();
5313
5827
  if (opts.project) {
@@ -5317,7 +5831,7 @@ async function resolveProjectEntry(opts) {
5317
5831
  const cwd = opts.cwd ?? process.cwd();
5318
5832
  const resolvedCwd = await normalizeProjectPath(cwd);
5319
5833
  for (const entry2 of entries) {
5320
- if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${path10.sep}`)) {
5834
+ if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${path12.sep}`)) {
5321
5835
  return entry2;
5322
5836
  }
5323
5837
  }
@@ -5482,7 +5996,7 @@ function isNpxInvocation() {
5482
5996
  function commandPrefix() {
5483
5997
  return isNpxInvocation() ? "npx neat.is" : "neat";
5484
5998
  }
5485
- function usage2() {
5999
+ function usage4() {
5486
6000
  const neat = commandPrefix();
5487
6001
  console.log("Installed via npx? Prefix commands with `npx neat.is`, or install once: `npm i -g neat.is`.");
5488
6002
  console.log("");
@@ -5502,10 +6016,12 @@ function usage2() {
5502
6016
  console.log(" PORT (default 8080), OTEL_PORT (4318), HOST (0.0.0.0)");
5503
6017
  console.log(" control listeners. NEAT_OTLP_GRPC=true also opens 4317.");
5504
6018
  console.log(" monitor Stream live graph facts to stdout \u2014 one line per new");
5505
- console.log(" fact \u2014 as the daemon learns them: fresh divergences,");
5506
- console.log(" integrations that just went stale, and new observed");
5507
- console.log(" runtime dependencies. Silent when nothing is new; exits");
5508
- console.log(" clean with no output when no daemon is reachable.");
6019
+ console.log(" fact \u2014 as the daemon learns them: fresh divergences");
6020
+ console.log(" (down to the drifting column), integrations that just");
6021
+ console.log(" went stale, new observed runtime dependencies, and");
6022
+ console.log(" freshly-tripped policy violations. Silent when nothing");
6023
+ console.log(" is new; exits clean with no output when no daemon is");
6024
+ console.log(" reachable.");
5509
6025
  console.log(" Flags:");
5510
6026
  console.log(" --project <name> watch a registered project by name");
5511
6027
  console.log(" --json emit one JSON object per line");
@@ -5535,6 +6051,22 @@ function usage2() {
5535
6051
  console.log(" --print-hook print the hook script");
5536
6052
  console.log(" --print-guide print the graph-first guidance");
5537
6053
  console.log(" --print-settings print the settings.json block --apply adds");
6054
+ console.log(" codex Install NEAT into the OpenAI Codex CLI: add [mcp_servers.neat]");
6055
+ console.log(" to ~/.codex/config.toml and the graph-first block to ./AGENTS.md.");
6056
+ console.log(" Plan by default; --apply to write.");
6057
+ console.log(" Flags:");
6058
+ console.log(" --apply add the MCP server + AGENTS.md block");
6059
+ console.log(" --print-config print the config.toml table");
6060
+ console.log(" --print-guide print the AGENTS.md block");
6061
+ console.log(" cursor Wire NEAT into Cursor: add the MCP server to ~/.cursor/mcp.json");
6062
+ console.log(" and the graph-first guidance to ./.cursorrules. Plan by default.");
6063
+ console.log(" Flags:");
6064
+ console.log(" --apply write the MCP config + rules file (default: plan)");
6065
+ console.log(" devin Wire NEAT into Devin Desktop (Cascade): add the MCP server to");
6066
+ console.log(" ~/.codeium/windsurf/mcp_config.json and the graph-first guidance");
6067
+ console.log(" to ./.windsurfrules. Plan by default.");
6068
+ console.log(" Flags:");
6069
+ console.log(" --apply write the MCP config + rules file (default: plan)");
5538
6070
  console.log(" deploy Detect the deploy substrate, generate NEAT_AUTH_TOKEN,");
5539
6071
  console.log(" emit a docker-compose / systemd / docker run artifact, and");
5540
6072
  console.log(" print the OTel env-vars block to paste into your platform.");
@@ -5548,7 +6080,8 @@ function usage2() {
5548
6080
  console.log(" --no-instrument skip the SDK install apply step");
5549
6081
  console.log(" --json emit the delta summary as JSON");
5550
6082
  console.log(" connector Configure OBSERVED connectors \u2014 pull (supabase, railway,");
5551
- console.log(" firebase, cloudflare) and push (vercel, via a trace Drain).");
6083
+ console.log(" firebase, cloudflare, neon, cloud-run) and push (vercel,");
6084
+ console.log(" via a trace Drain).");
5552
6085
  console.log(" Subcommands:");
5553
6086
  console.log(" add <provider> add a connector; validates the credential");
5554
6087
  console.log(" against the provider first (--skip-validate to skip)");
@@ -5780,7 +6313,7 @@ async function buildPatchSections(services, project) {
5780
6313
  }
5781
6314
  async function runInit(opts) {
5782
6315
  const written = [];
5783
- const stat = await fs9.stat(opts.scanPath).catch(() => null);
6316
+ const stat = await fs11.stat(opts.scanPath).catch(() => null);
5784
6317
  if (!stat || !stat.isDirectory()) {
5785
6318
  console.error(`neat init: ${opts.scanPath} is not a directory`);
5786
6319
  return { exitCode: 2, writtenFiles: written };
@@ -5789,13 +6322,13 @@ async function runInit(opts) {
5789
6322
  printDiscoveryReport(opts, services);
5790
6323
  const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
5791
6324
  const patch = renderPatch(sections);
5792
- const patchPath = path11.join(opts.scanPath, "neat.patch");
6325
+ const patchPath = path13.join(opts.scanPath, "neat.patch");
5793
6326
  if (opts.dryRun) {
5794
- await fs9.writeFile(patchPath, patch, "utf8");
6327
+ await fs11.writeFile(patchPath, patch, "utf8");
5795
6328
  written.push(patchPath);
5796
6329
  console.log(`dry-run: patch written to ${patchPath}`);
5797
- const gitignorePath = path11.join(opts.scanPath, ".gitignore");
5798
- const gitignoreExists = await fs9.stat(gitignorePath).then(() => true).catch(() => false);
6330
+ const gitignorePath = path13.join(opts.scanPath, ".gitignore");
6331
+ const gitignoreExists = await fs11.stat(gitignorePath).then(() => true).catch(() => false);
5799
6332
  const verb = gitignoreExists ? "append" : "create";
5800
6333
  console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
5801
6334
  console.log("rerun without --dry-run to register and snapshot.");
@@ -5806,9 +6339,9 @@ async function runInit(opts) {
5806
6339
  const graph = getGraph(graphKey);
5807
6340
  const projectPaths = pathsForProject(
5808
6341
  graphKey,
5809
- path11.join(opts.scanPath, "neat-out")
6342
+ path13.join(opts.scanPath, "neat-out")
5810
6343
  );
5811
- const errorsPath = path11.join(path11.dirname(opts.outPath), path11.basename(projectPaths.errorsPath));
6344
+ const errorsPath = path13.join(path13.dirname(opts.outPath), path13.basename(projectPaths.errorsPath));
5812
6345
  const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
5813
6346
  await saveGraphToDisk(graph, opts.outPath);
5814
6347
  written.push(opts.outPath);
@@ -5887,7 +6420,7 @@ async function runInit(opts) {
5887
6420
  console.log("Run `npm install` (or your language equivalent) to refresh lockfiles.");
5888
6421
  }
5889
6422
  } else {
5890
- await fs9.writeFile(patchPath, patch, "utf8");
6423
+ await fs11.writeFile(patchPath, patch, "utf8");
5891
6424
  written.push(patchPath);
5892
6425
  }
5893
6426
  }
@@ -5927,9 +6460,9 @@ var CLAUDE_SKILL_CONFIG = {
5927
6460
  };
5928
6461
  function claudeConfigPath() {
5929
6462
  const override = process.env.NEAT_CLAUDE_CONFIG;
5930
- if (override && override.length > 0) return path11.resolve(override);
6463
+ if (override && override.length > 0) return path13.resolve(override);
5931
6464
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
5932
- return path11.join(home, ".claude.json");
6465
+ return path13.join(home, ".claude.json");
5933
6466
  }
5934
6467
  async function runSkill(opts) {
5935
6468
  const snippet = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
@@ -5941,7 +6474,7 @@ async function runSkill(opts) {
5941
6474
  const target = claudeConfigPath();
5942
6475
  let existing = {};
5943
6476
  try {
5944
- existing = JSON.parse(await fs9.readFile(target, "utf8"));
6477
+ existing = JSON.parse(await fs11.readFile(target, "utf8"));
5945
6478
  } catch (err) {
5946
6479
  if (err.code !== "ENOENT") {
5947
6480
  console.error(`neat skill: failed to read ${target} \u2014 ${err.message}`);
@@ -5953,8 +6486,8 @@ async function runSkill(opts) {
5953
6486
  ...existing,
5954
6487
  mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
5955
6488
  };
5956
- await fs9.mkdir(path11.dirname(target), { recursive: true });
5957
- await fs9.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
6489
+ await fs11.mkdir(path13.dirname(target), { recursive: true });
6490
+ await fs11.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
5958
6491
  console.log(`neat skill: wrote mcpServers.neat to ${target}`);
5959
6492
  console.log("restart Claude Code to pick up the new MCP server.");
5960
6493
  console.log("");
@@ -5981,7 +6514,7 @@ async function main() {
5981
6514
  const argv = process.argv.slice(2);
5982
6515
  const cmd0 = argv[0];
5983
6516
  if (cmd0 === "-h" || cmd0 === "--help") {
5984
- usage2();
6517
+ usage4();
5985
6518
  process.exit(0);
5986
6519
  }
5987
6520
  if (cmd0 === "--version" || cmd0 === "-v" || cmd0 === "version") {
@@ -5998,6 +6531,16 @@ async function main() {
5998
6531
  if (code !== 0) process.exit(code);
5999
6532
  return;
6000
6533
  }
6534
+ if (cmd0 === "codex") {
6535
+ const code = await runCodexCommand(argv.slice(1));
6536
+ if (code !== 0) process.exit(code);
6537
+ return;
6538
+ }
6539
+ if (cmd0 === "cursor" || cmd0 === "devin") {
6540
+ const code = await runEditorCommand(cmd0, argv.slice(1));
6541
+ if (code !== 0) process.exit(code);
6542
+ return;
6543
+ }
6001
6544
  const argvParsed = parseArgs(argv);
6002
6545
  if (argvParsed.positional.length === 0) {
6003
6546
  const orchestratorCode2 = await tryOrchestrator(process.cwd(), argvParsed);
@@ -6012,19 +6555,19 @@ async function main() {
6012
6555
  const target = positional[0];
6013
6556
  if (!target) {
6014
6557
  console.error("neat init: missing <path>");
6015
- usage2();
6558
+ usage4();
6016
6559
  process.exit(2);
6017
6560
  }
6018
6561
  if (apply4 && dryRun) {
6019
6562
  console.error("neat init: --apply and --dry-run are mutually exclusive");
6020
6563
  process.exit(2);
6021
6564
  }
6022
- const scanPath = path11.resolve(target);
6565
+ const scanPath = path13.resolve(target);
6023
6566
  const projectExplicit = parsed.project !== null;
6024
- const projectName = projectExplicit ? project : path11.basename(scanPath);
6567
+ const projectName = projectExplicit ? project : path13.basename(scanPath);
6025
6568
  const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
6026
- const fallback = pathsForProject(projectKey, path11.join(scanPath, "neat-out")).snapshotPath;
6027
- const outPath = path11.resolve(process.env.NEAT_OUT_PATH ?? fallback);
6569
+ const fallback = pathsForProject(projectKey, path13.join(scanPath, "neat-out")).snapshotPath;
6570
+ const outPath = path13.resolve(process.env.NEAT_OUT_PATH ?? fallback);
6028
6571
  const result = await runInit({
6029
6572
  scanPath,
6030
6573
  outPath,
@@ -6042,24 +6585,24 @@ async function main() {
6042
6585
  const target = positional[0];
6043
6586
  if (!target) {
6044
6587
  console.error("neat watch: missing <path>");
6045
- usage2();
6588
+ usage4();
6046
6589
  process.exit(2);
6047
6590
  }
6048
- const scanPath = path11.resolve(target);
6049
- const stat = await fs9.stat(scanPath).catch(() => null);
6591
+ const scanPath = path13.resolve(target);
6592
+ const stat = await fs11.stat(scanPath).catch(() => null);
6050
6593
  if (!stat || !stat.isDirectory()) {
6051
6594
  console.error(`neat watch: ${scanPath} is not a directory`);
6052
6595
  process.exit(2);
6053
6596
  }
6054
- const projectPaths = pathsForProject(project, path11.join(scanPath, "neat-out"));
6055
- const outPath = path11.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
6056
- const errorsPath = path11.resolve(
6057
- process.env.NEAT_ERRORS_PATH ?? path11.join(path11.dirname(outPath), path11.basename(projectPaths.errorsPath))
6597
+ const projectPaths = pathsForProject(project, path13.join(scanPath, "neat-out"));
6598
+ const outPath = path13.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
6599
+ const errorsPath = path13.resolve(
6600
+ process.env.NEAT_ERRORS_PATH ?? path13.join(path13.dirname(outPath), path13.basename(projectPaths.errorsPath))
6058
6601
  );
6059
- const staleEventsPath = path11.resolve(
6060
- process.env.NEAT_STALE_EVENTS_PATH ?? path11.join(path11.dirname(outPath), path11.basename(projectPaths.staleEventsPath))
6602
+ const staleEventsPath = path13.resolve(
6603
+ process.env.NEAT_STALE_EVENTS_PATH ?? path13.join(path13.dirname(outPath), path13.basename(projectPaths.staleEventsPath))
6061
6604
  );
6062
- const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? path11.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
6605
+ const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? path13.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
6063
6606
  const handle = await startWatch(getGraph(project), {
6064
6607
  scanPath,
6065
6608
  outPath,
@@ -6068,7 +6611,7 @@ async function main() {
6068
6611
  project,
6069
6612
  // Resolve NEAT_HOME so a `neat watch` picks up connectors added to
6070
6613
  // ~/.neat/connectors.json (#871). Same resolution the rest of the CLI uses.
6071
- neatHome: process.env.NEAT_HOME ? path11.resolve(process.env.NEAT_HOME) : path11.join(os2.homedir(), ".neat"),
6614
+ neatHome: process.env.NEAT_HOME ? path13.resolve(process.env.NEAT_HOME) : path13.join(os4.homedir(), ".neat"),
6072
6615
  ...embeddingsCachePath ? { embeddingsCachePath } : {},
6073
6616
  host: process.env.HOST ?? "0.0.0.0",
6074
6617
  port: Number(process.env.PORT ?? 8080),
@@ -6104,7 +6647,7 @@ async function main() {
6104
6647
  const name = positional[0];
6105
6648
  if (!name) {
6106
6649
  console.error("neat pause: missing <name>");
6107
- usage2();
6650
+ usage4();
6108
6651
  process.exit(2);
6109
6652
  }
6110
6653
  const daemon = await findDaemonByProject(name);
@@ -6129,7 +6672,7 @@ async function main() {
6129
6672
  const name = positional[0];
6130
6673
  if (!name) {
6131
6674
  console.error("neat resume: missing <name>");
6132
- usage2();
6675
+ usage4();
6133
6676
  process.exit(2);
6134
6677
  }
6135
6678
  const daemon = await findDaemonByProject(name);
@@ -6159,7 +6702,7 @@ async function main() {
6159
6702
  const name = positional[0];
6160
6703
  if (!name) {
6161
6704
  console.error("neat uninstall: missing <name>");
6162
- usage2();
6705
+ usage4();
6163
6706
  process.exit(2);
6164
6707
  }
6165
6708
  const daemon = await findDaemonByProject(name);
@@ -6246,15 +6789,15 @@ async function main() {
6246
6789
  return;
6247
6790
  }
6248
6791
  console.error(`neat: unknown command "${cmd}"`);
6249
- usage2();
6792
+ usage4();
6250
6793
  process.exit(1);
6251
6794
  }
6252
6795
  async function tryOrchestrator(cmd, parsed) {
6253
- const scanPath = path11.resolve(cmd);
6254
- const stat = await fs9.stat(scanPath).catch(() => null);
6796
+ const scanPath = path13.resolve(cmd);
6797
+ const stat = await fs11.stat(scanPath).catch(() => null);
6255
6798
  if (!stat || !stat.isDirectory()) return null;
6256
6799
  const projectExplicit = parsed.project !== null;
6257
- const projectName = projectExplicit ? parsed.project : path11.basename(scanPath);
6800
+ const projectName = projectExplicit ? parsed.project : path13.basename(scanPath);
6258
6801
  const result = await runOrchestrator({
6259
6802
  scanPath,
6260
6803
  project: projectName,
@@ -6542,6 +7085,6 @@ export {
6542
7085
  runMonitorVerb,
6543
7086
  runQueryVerb,
6544
7087
  runSkill,
6545
- usage2 as usage
7088
+ usage4 as usage
6546
7089
  };
6547
7090
  //# sourceMappingURL=cli.js.map