@neat.is/core 0.4.15 → 0.4.16

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.cjs CHANGED
@@ -58,9 +58,9 @@ function mountBearerAuth(app, opts) {
58
58
  const suffixes = [...DEFAULT_UNAUTH_SUFFIXES, ...opts.extraUnauthenticatedSuffixes ?? []];
59
59
  const publicRead = opts.publicRead === true;
60
60
  app.addHook("preHandler", (req, reply, done) => {
61
- const path49 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
61
+ const path50 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
62
62
  for (const suffix of suffixes) {
63
- if (path49 === suffix || path49.endsWith(suffix)) {
63
+ if (path50 === suffix || path50.endsWith(suffix)) {
64
64
  done();
65
65
  return;
66
66
  }
@@ -188,8 +188,8 @@ function reshapeGrpcRequest(req) {
188
188
  };
189
189
  }
190
190
  function resolveProtoRoot() {
191
- const here = import_node_path39.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
192
- return import_node_path39.default.resolve(here, "..", "proto");
191
+ const here = import_node_path40.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
192
+ return import_node_path40.default.resolve(here, "..", "proto");
193
193
  }
194
194
  function loadTraceService() {
195
195
  const protoRoot = resolveProtoRoot();
@@ -257,13 +257,13 @@ async function startOtelGrpcReceiver(opts) {
257
257
  })
258
258
  };
259
259
  }
260
- var import_node_url, import_node_path39, import_node_crypto2, grpc, protoLoader;
260
+ var import_node_url2, import_node_path40, import_node_crypto2, grpc, protoLoader;
261
261
  var init_otel_grpc = __esm({
262
262
  "src/otel-grpc.ts"() {
263
263
  "use strict";
264
264
  init_cjs_shims();
265
- import_node_url = require("url");
266
- import_node_path39 = __toESM(require("path"), 1);
265
+ import_node_url2 = require("url");
266
+ import_node_path40 = __toESM(require("path"), 1);
267
267
  import_node_crypto2 = require("crypto");
268
268
  grpc = __toESM(require("@grpc/grpc-js"), 1);
269
269
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
@@ -373,10 +373,10 @@ function parseOtlpRequest(body) {
373
373
  return out;
374
374
  }
375
375
  function loadProtoRoot() {
376
- const here = import_node_path40.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
377
- const protoRoot = import_node_path40.default.resolve(here, "..", "proto");
376
+ const here = import_node_path41.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
377
+ const protoRoot = import_node_path41.default.resolve(here, "..", "proto");
378
378
  const root = new import_protobufjs.default.Root();
379
- root.resolvePath = (_origin, target) => import_node_path40.default.resolve(protoRoot, target);
379
+ root.resolvePath = (_origin, target) => import_node_path41.default.resolve(protoRoot, target);
380
380
  root.loadSync(
381
381
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
382
382
  { keepCase: true }
@@ -572,13 +572,13 @@ async function buildOtelReceiver(opts) {
572
572
  };
573
573
  return decorated;
574
574
  }
575
- var import_node_path40, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
575
+ var import_node_path41, import_node_url3, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
576
576
  var init_otel = __esm({
577
577
  "src/otel.ts"() {
578
578
  "use strict";
579
579
  init_cjs_shims();
580
- import_node_path40 = __toESM(require("path"), 1);
581
- import_node_url2 = require("url");
580
+ import_node_path41 = __toESM(require("path"), 1);
581
+ import_node_url3 = require("url");
582
582
  import_fastify2 = __toESM(require("fastify"), 1);
583
583
  import_protobufjs = __toESM(require("protobufjs"), 1);
584
584
  init_auth();
@@ -596,18 +596,57 @@ var cli_exports = {};
596
596
  __export(cli_exports, {
597
597
  CLAUDE_SKILL_CONFIG: () => CLAUDE_SKILL_CONFIG,
598
598
  QUERY_VERBS: () => QUERY_VERBS,
599
+ commandPrefix: () => commandPrefix,
600
+ isNpxInvocation: () => isNpxInvocation,
601
+ main: () => main,
599
602
  parseArgs: () => parseArgs,
600
603
  printBanner: () => printBanner,
601
604
  readPackageVersion: () => readPackageVersion,
602
605
  runInit: () => runInit,
603
606
  runQueryVerb: () => runQueryVerb,
604
- runSkill: () => runSkill
607
+ runSkill: () => runSkill,
608
+ usage: () => usage
605
609
  });
606
610
  module.exports = __toCommonJS(cli_exports);
607
611
  init_cjs_shims();
608
- var import_node_path48 = __toESM(require("path"), 1);
609
- var import_node_fs31 = require("fs");
610
- var import_node_url3 = require("url");
612
+ var import_node_path49 = __toESM(require("path"), 1);
613
+ var import_node_fs32 = require("fs");
614
+
615
+ // src/banner.ts
616
+ init_cjs_shims();
617
+ var import_node_path = __toESM(require("path"), 1);
618
+ var import_node_fs = require("fs");
619
+ var import_node_url = require("url");
620
+ function readPackageVersion() {
621
+ const here = typeof __dirname !== "undefined" ? __dirname : import_node_path.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
622
+ const candidates = [
623
+ import_node_path.default.resolve(here, "../package.json"),
624
+ import_node_path.default.resolve(here, "../../package.json")
625
+ ];
626
+ for (const candidate of candidates) {
627
+ try {
628
+ const raw = (0, import_node_fs.readFileSync)(candidate, "utf8");
629
+ const parsed = JSON.parse(raw);
630
+ if (parsed.name === "@neat.is/core" && typeof parsed.version === "string") {
631
+ return parsed.version;
632
+ }
633
+ } catch {
634
+ }
635
+ }
636
+ return "unknown";
637
+ }
638
+ function printBanner() {
639
+ console.log("\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");
640
+ console.log("\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D");
641
+ console.log("\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 ");
642
+ console.log("\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2551 ");
643
+ console.log("\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 ");
644
+ console.log("\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D ");
645
+ console.log("");
646
+ console.log(" Network Expressive Architecting Tool");
647
+ console.log(` neat.is \xB7 v${readPackageVersion()} \xB7 Apache 2.0`);
648
+ console.log("");
649
+ }
611
650
 
612
651
  // src/graph.ts
613
652
  init_cjs_shims();
@@ -642,21 +681,21 @@ init_cjs_shims();
642
681
 
643
682
  // src/ingest.ts
644
683
  init_cjs_shims();
645
- var import_node_fs3 = require("fs");
646
- var import_node_path3 = __toESM(require("path"), 1);
684
+ var import_node_fs4 = require("fs");
685
+ var import_node_path4 = __toESM(require("path"), 1);
647
686
  var sourceMapJs = __toESM(require("source-map-js"), 1);
648
687
 
649
688
  // src/policy.ts
650
689
  init_cjs_shims();
651
- var import_node_fs2 = require("fs");
652
- var import_node_path2 = __toESM(require("path"), 1);
690
+ var import_node_fs3 = require("fs");
691
+ var import_node_path3 = __toESM(require("path"), 1);
653
692
  var import_types2 = require("@neat.is/types");
654
693
 
655
694
  // src/compat.ts
656
695
  init_cjs_shims();
657
- var import_node_fs = require("fs");
696
+ var import_node_fs2 = require("fs");
658
697
  var import_node_os = __toESM(require("os"), 1);
659
- var import_node_path = __toESM(require("path"), 1);
698
+ var import_node_path2 = __toESM(require("path"), 1);
660
699
  var import_semver = __toESM(require("semver"), 1);
661
700
 
662
701
  // compat.json
@@ -785,8 +824,8 @@ var compat_default = {
785
824
  var bundledMatrix = compat_default;
786
825
  var mergedMatrix = null;
787
826
  var remoteLoadAttempted = false;
788
- var REMOTE_CACHE_DIR = import_node_path.default.join(import_node_os.default.homedir(), ".neat");
789
- var REMOTE_CACHE_PATH = import_node_path.default.join(REMOTE_CACHE_DIR, "compat-cache.json");
827
+ var REMOTE_CACHE_DIR = import_node_path2.default.join(import_node_os.default.homedir(), ".neat");
828
+ var REMOTE_CACHE_PATH = import_node_path2.default.join(REMOTE_CACHE_DIR, "compat-cache.json");
790
829
  var REMOTE_TTL_MS = 24 * 60 * 60 * 1e3;
791
830
  function engineMeetsThreshold(engineVersion, threshold) {
792
831
  const e = parseInt(engineVersion, 10);
@@ -897,7 +936,7 @@ function mergeMatrices(a, b) {
897
936
  }
898
937
  async function readRemoteCache(url) {
899
938
  try {
900
- const raw = await import_node_fs.promises.readFile(REMOTE_CACHE_PATH, "utf8");
939
+ const raw = await import_node_fs2.promises.readFile(REMOTE_CACHE_PATH, "utf8");
901
940
  const parsed = JSON.parse(raw);
902
941
  if (parsed.url !== url) return null;
903
942
  const age = Date.now() - new Date(parsed.fetchedAt).getTime();
@@ -914,8 +953,8 @@ async function writeRemoteCache(url, matrix) {
914
953
  matrix
915
954
  };
916
955
  try {
917
- await import_node_fs.promises.mkdir(REMOTE_CACHE_DIR, { recursive: true });
918
- await import_node_fs.promises.writeFile(REMOTE_CACHE_PATH, JSON.stringify(file), "utf8");
956
+ await import_node_fs2.promises.mkdir(REMOTE_CACHE_DIR, { recursive: true });
957
+ await import_node_fs2.promises.writeFile(REMOTE_CACHE_PATH, JSON.stringify(file), "utf8");
919
958
  } catch (err) {
920
959
  console.warn(`[neat] failed to cache compat matrix: ${err.message}`);
921
960
  }
@@ -1136,19 +1175,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1136
1175
  function longestIncomingWalk(graph, start, maxDepth) {
1137
1176
  let best = { path: [start], edges: [] };
1138
1177
  const visited = /* @__PURE__ */ new Set([start]);
1139
- function step(node, path49, edges) {
1140
- if (path49.length > best.path.length) {
1141
- best = { path: [...path49], edges: [...edges] };
1178
+ function step(node, path50, edges) {
1179
+ if (path50.length > best.path.length) {
1180
+ best = { path: [...path50], edges: [...edges] };
1142
1181
  }
1143
- if (path49.length - 1 >= maxDepth) return;
1182
+ if (path50.length - 1 >= maxDepth) return;
1144
1183
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1145
1184
  for (const [srcId, edge] of incoming) {
1146
1185
  if (visited.has(srcId)) continue;
1147
1186
  visited.add(srcId);
1148
- path49.push(srcId);
1187
+ path50.push(srcId);
1149
1188
  edges.push(edge);
1150
- step(srcId, path49, edges);
1151
- path49.pop();
1189
+ step(srcId, path50, edges);
1190
+ path50.pop();
1152
1191
  edges.pop();
1153
1192
  visited.delete(srcId);
1154
1193
  }
@@ -1607,7 +1646,7 @@ function evaluateAllPolicies(graph, policies, ctx) {
1607
1646
  async function loadPolicyFile(policyPath) {
1608
1647
  let raw;
1609
1648
  try {
1610
- raw = await import_node_fs2.promises.readFile(policyPath, "utf8");
1649
+ raw = await import_node_fs3.promises.readFile(policyPath, "utf8");
1611
1650
  } catch (err) {
1612
1651
  if (err.code === "ENOENT") return [];
1613
1652
  throw err;
@@ -1628,8 +1667,8 @@ var PolicyViolationsLog = class {
1628
1667
  if (!this.seen) await this.hydrate();
1629
1668
  if (this.seen.has(v.id)) return false;
1630
1669
  this.seen.add(v.id);
1631
- await import_node_fs2.promises.mkdir(import_node_path2.default.dirname(this.path), { recursive: true });
1632
- await import_node_fs2.promises.appendFile(this.path, JSON.stringify(v) + "\n", "utf8");
1670
+ await import_node_fs3.promises.mkdir(import_node_path3.default.dirname(this.path), { recursive: true });
1671
+ await import_node_fs3.promises.appendFile(this.path, JSON.stringify(v) + "\n", "utf8");
1633
1672
  emitNeatEvent({
1634
1673
  type: "policy-violation",
1635
1674
  project: this.project,
@@ -1639,7 +1678,7 @@ var PolicyViolationsLog = class {
1639
1678
  }
1640
1679
  async readAll() {
1641
1680
  try {
1642
- const raw = await import_node_fs2.promises.readFile(this.path, "utf8");
1681
+ const raw = await import_node_fs3.promises.readFile(this.path, "utf8");
1643
1682
  return raw.split("\n").filter(Boolean).map((line) => JSON.parse(line));
1644
1683
  } catch (err) {
1645
1684
  if (err.code === "ENOENT") return [];
@@ -1752,7 +1791,7 @@ function languageForExt(relPath) {
1752
1791
  function relPathForRuntimeFile(filepath, serviceNode, scanPath) {
1753
1792
  let p = toPosix(filepath).replace(/^file:\/\//, "");
1754
1793
  if (scanPath && scanPath.length > 0) {
1755
- const absRoot = toPosix(import_node_path3.default.resolve(scanPath, serviceNode?.repoPath ?? ""));
1794
+ const absRoot = toPosix(import_node_path4.default.resolve(scanPath, serviceNode?.repoPath ?? ""));
1756
1795
  const anchor = absRoot.endsWith("/") ? absRoot : `${absRoot}/`;
1757
1796
  if (p.startsWith(anchor)) return p.slice(anchor.length);
1758
1797
  }
@@ -1780,10 +1819,10 @@ function resolveDistToSrc(absFilepath, line) {
1780
1819
  entry2 = null;
1781
1820
  const mapPath = `${absFilepath}.map`;
1782
1821
  try {
1783
- if ((0, import_node_fs3.existsSync)(mapPath)) {
1784
- const raw = JSON.parse((0, import_node_fs3.readFileSync)(mapPath, "utf8"));
1822
+ if ((0, import_node_fs4.existsSync)(mapPath)) {
1823
+ const raw = JSON.parse((0, import_node_fs4.readFileSync)(mapPath, "utf8"));
1785
1824
  const consumer = new sourceMapJs.SourceMapConsumer(raw);
1786
- entry2 = { consumer, dir: import_node_path3.default.dirname(mapPath) };
1825
+ entry2 = { consumer, dir: import_node_path4.default.dirname(mapPath) };
1787
1826
  }
1788
1827
  } catch {
1789
1828
  entry2 = null;
@@ -1798,7 +1837,7 @@ function resolveDistToSrc(absFilepath, line) {
1798
1837
  });
1799
1838
  if (!pos || !pos.source) return null;
1800
1839
  const root = entry2.consumer.sourceRoot ?? "";
1801
- const resolved = import_node_path3.default.resolve(entry2.dir, root, pos.source);
1840
+ const resolved = import_node_path4.default.resolve(entry2.dir, root, pos.source);
1802
1841
  return { filepath: resolved, ...pos.line ? { line: pos.line } : {} };
1803
1842
  } catch {
1804
1843
  return null;
@@ -2065,8 +2104,8 @@ function upsertInferredEdge(graph, type, source, target, ts) {
2065
2104
  graph.addEdgeWithKey(id, source, target, edge);
2066
2105
  }
2067
2106
  async function appendErrorEvent(ctx, ev) {
2068
- await import_node_fs3.promises.mkdir(import_node_path3.default.dirname(ctx.errorsPath), { recursive: true });
2069
- await import_node_fs3.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
2107
+ await import_node_fs4.promises.mkdir(import_node_path4.default.dirname(ctx.errorsPath), { recursive: true });
2108
+ await import_node_fs4.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
2070
2109
  }
2071
2110
  function sanitizeAttributes(attrs) {
2072
2111
  const out = {};
@@ -2097,8 +2136,8 @@ function makeErrorSpanWriter(errorsPath) {
2097
2136
  return async (span) => {
2098
2137
  const ev = buildErrorEventForReceiver(span);
2099
2138
  if (!ev) return;
2100
- await import_node_fs3.promises.mkdir(import_node_path3.default.dirname(errorsPath), { recursive: true });
2101
- await import_node_fs3.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
2139
+ await import_node_fs4.promises.mkdir(import_node_path4.default.dirname(errorsPath), { recursive: true });
2140
+ await import_node_fs4.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
2102
2141
  };
2103
2142
  }
2104
2143
  async function handleSpan(ctx, span) {
@@ -2317,13 +2356,13 @@ async function markStaleEdges(graph, options = {}) {
2317
2356
  return { count: events.length, events };
2318
2357
  }
2319
2358
  async function appendStaleEvents(staleEventsPath, events) {
2320
- await import_node_fs3.promises.mkdir(import_node_path3.default.dirname(staleEventsPath), { recursive: true });
2359
+ await import_node_fs4.promises.mkdir(import_node_path4.default.dirname(staleEventsPath), { recursive: true });
2321
2360
  const lines = events.map((e) => JSON.stringify(e)).join("\n") + "\n";
2322
- await import_node_fs3.promises.appendFile(staleEventsPath, lines, "utf8");
2361
+ await import_node_fs4.promises.appendFile(staleEventsPath, lines, "utf8");
2323
2362
  }
2324
2363
  async function readStaleEvents(staleEventsPath) {
2325
2364
  try {
2326
- const raw = await import_node_fs3.promises.readFile(staleEventsPath, "utf8");
2365
+ const raw = await import_node_fs4.promises.readFile(staleEventsPath, "utf8");
2327
2366
  return raw.split("\n").filter((line) => line.length > 0).map((line) => JSON.parse(line));
2328
2367
  } catch (err) {
2329
2368
  if (err.code === "ENOENT") return [];
@@ -2357,7 +2396,7 @@ function startStalenessLoop(graph, options = {}) {
2357
2396
  }
2358
2397
  async function readErrorEvents(errorsPath) {
2359
2398
  try {
2360
- const raw = await import_node_fs3.promises.readFile(errorsPath, "utf8");
2399
+ const raw = await import_node_fs4.promises.readFile(errorsPath, "utf8");
2361
2400
  return raw.split("\n").filter((line) => line.length > 0).map((line) => JSON.parse(line));
2362
2401
  } catch (err) {
2363
2402
  if (err.code === "ENOENT") return [];
@@ -2389,16 +2428,16 @@ function mergeSnapshot(graph, snapshot) {
2389
2428
 
2390
2429
  // src/extract/services.ts
2391
2430
  init_cjs_shims();
2392
- var import_node_fs8 = require("fs");
2393
- var import_node_path8 = __toESM(require("path"), 1);
2431
+ var import_node_fs9 = require("fs");
2432
+ var import_node_path9 = __toESM(require("path"), 1);
2394
2433
  var import_ignore = __toESM(require("ignore"), 1);
2395
2434
  var import_minimatch2 = require("minimatch");
2396
2435
  var import_types5 = require("@neat.is/types");
2397
2436
 
2398
2437
  // src/extract/shared.ts
2399
2438
  init_cjs_shims();
2400
- var import_node_fs4 = require("fs");
2401
- var import_node_path4 = __toESM(require("path"), 1);
2439
+ var import_node_fs5 = require("fs");
2440
+ var import_node_path5 = __toESM(require("path"), 1);
2402
2441
  var import_yaml = require("yaml");
2403
2442
  var import_types4 = require("@neat.is/types");
2404
2443
  var SERVICE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs", ".ts", ".tsx", ".py"]);
@@ -2430,7 +2469,7 @@ async function isPythonVenvDir(dir) {
2430
2469
  const cached = PYVENV_MARKER_CACHE.get(dir);
2431
2470
  if (cached !== void 0) return cached;
2432
2471
  try {
2433
- const stat = await import_node_fs4.promises.stat(import_node_path4.default.join(dir, "pyvenv.cfg"));
2472
+ const stat = await import_node_fs5.promises.stat(import_node_path5.default.join(dir, "pyvenv.cfg"));
2434
2473
  const ok = stat.isFile();
2435
2474
  PYVENV_MARKER_CACHE.set(dir, ok);
2436
2475
  return ok;
@@ -2440,7 +2479,7 @@ async function isPythonVenvDir(dir) {
2440
2479
  }
2441
2480
  }
2442
2481
  function isConfigFile(name) {
2443
- const ext = import_node_path4.default.extname(name);
2482
+ const ext = import_node_path5.default.extname(name);
2444
2483
  if (CONFIG_FILE_EXTENSIONS.has(ext)) return { match: true, fileType: ext.slice(1) };
2445
2484
  if (name === ".env" || name.startsWith(".env.")) {
2446
2485
  if (isEnvTemplateFile(name)) return { match: false, fileType: "" };
@@ -2548,16 +2587,16 @@ function cleanVersion(raw) {
2548
2587
  return raw.replace(/^[\^~><=v\s]+/, "").trim() || void 0;
2549
2588
  }
2550
2589
  async function readJson(filePath) {
2551
- const raw = await import_node_fs4.promises.readFile(filePath, "utf8");
2590
+ const raw = await import_node_fs5.promises.readFile(filePath, "utf8");
2552
2591
  return JSON.parse(raw);
2553
2592
  }
2554
2593
  async function readYaml(filePath) {
2555
- const raw = await import_node_fs4.promises.readFile(filePath, "utf8");
2594
+ const raw = await import_node_fs5.promises.readFile(filePath, "utf8");
2556
2595
  return (0, import_yaml.parse)(raw);
2557
2596
  }
2558
2597
  async function exists(p) {
2559
2598
  try {
2560
- await import_node_fs4.promises.access(p);
2599
+ await import_node_fs5.promises.access(p);
2561
2600
  return true;
2562
2601
  } catch {
2563
2602
  return false;
@@ -2566,8 +2605,8 @@ async function exists(p) {
2566
2605
 
2567
2606
  // src/extract/python.ts
2568
2607
  init_cjs_shims();
2569
- var import_node_fs5 = require("fs");
2570
- var import_node_path5 = __toESM(require("path"), 1);
2608
+ var import_node_fs6 = require("fs");
2609
+ var import_node_path6 = __toESM(require("path"), 1);
2571
2610
  var import_smol_toml = require("smol-toml");
2572
2611
  var REQUIREMENT_LINE = /^\s*([A-Za-z0-9_.-]+)(?:\[[^\]]*\])?\s*(?:(==)\s*([A-Za-z0-9_.+-]+))?/;
2573
2612
  function parseRequirementsTxt(content) {
@@ -2600,25 +2639,25 @@ function depsFromPyProject(pyproject) {
2600
2639
  return out;
2601
2640
  }
2602
2641
  async function discoverPythonService(serviceDir) {
2603
- const pyprojectPath = import_node_path5.default.join(serviceDir, "pyproject.toml");
2604
- const requirementsPath = import_node_path5.default.join(serviceDir, "requirements.txt");
2605
- const setupPath = import_node_path5.default.join(serviceDir, "setup.py");
2642
+ const pyprojectPath = import_node_path6.default.join(serviceDir, "pyproject.toml");
2643
+ const requirementsPath = import_node_path6.default.join(serviceDir, "requirements.txt");
2644
+ const setupPath = import_node_path6.default.join(serviceDir, "setup.py");
2606
2645
  const hasPyproject = await exists(pyprojectPath);
2607
2646
  const hasRequirements = await exists(requirementsPath);
2608
2647
  const hasSetup = await exists(setupPath);
2609
2648
  if (!hasPyproject && !hasRequirements && !hasSetup) return null;
2610
- let name = import_node_path5.default.basename(serviceDir);
2649
+ let name = import_node_path6.default.basename(serviceDir);
2611
2650
  let version;
2612
2651
  const dependencies = {};
2613
2652
  if (hasPyproject) {
2614
- const raw = await import_node_fs5.promises.readFile(pyprojectPath, "utf8");
2653
+ const raw = await import_node_fs6.promises.readFile(pyprojectPath, "utf8");
2615
2654
  const pyproject = (0, import_smol_toml.parse)(raw);
2616
2655
  name = pyproject.project?.name ?? pyproject.tool?.poetry?.name ?? name;
2617
2656
  version = pyproject.project?.version ?? pyproject.tool?.poetry?.version ?? void 0;
2618
2657
  Object.assign(dependencies, depsFromPyProject(pyproject));
2619
2658
  }
2620
2659
  if (hasRequirements) {
2621
- const raw = await import_node_fs5.promises.readFile(requirementsPath, "utf8");
2660
+ const raw = await import_node_fs6.promises.readFile(requirementsPath, "utf8");
2622
2661
  Object.assign(dependencies, parseRequirementsTxt(raw));
2623
2662
  }
2624
2663
  return { name, version, dependencies };
@@ -2633,17 +2672,17 @@ function pythonToPackage(service) {
2633
2672
 
2634
2673
  // src/extract/owners.ts
2635
2674
  init_cjs_shims();
2636
- var import_node_fs6 = require("fs");
2637
- var import_node_path6 = __toESM(require("path"), 1);
2675
+ var import_node_fs7 = require("fs");
2676
+ var import_node_path7 = __toESM(require("path"), 1);
2638
2677
  var import_minimatch = require("minimatch");
2639
2678
  async function loadCodeowners(scanPath) {
2640
2679
  const candidates = [
2641
- import_node_path6.default.join(scanPath, "CODEOWNERS"),
2642
- import_node_path6.default.join(scanPath, ".github", "CODEOWNERS")
2680
+ import_node_path7.default.join(scanPath, "CODEOWNERS"),
2681
+ import_node_path7.default.join(scanPath, ".github", "CODEOWNERS")
2643
2682
  ];
2644
2683
  for (const file of candidates) {
2645
2684
  if (await exists(file)) {
2646
- const raw = await import_node_fs6.promises.readFile(file, "utf8");
2685
+ const raw = await import_node_fs7.promises.readFile(file, "utf8");
2647
2686
  return parseCodeowners(raw);
2648
2687
  }
2649
2688
  }
@@ -2661,7 +2700,7 @@ function parseCodeowners(raw) {
2661
2700
  return { rules };
2662
2701
  }
2663
2702
  function matchOwner(file, repoPath) {
2664
- const normalized = repoPath.split(import_node_path6.default.sep).join("/");
2703
+ const normalized = repoPath.split(import_node_path7.default.sep).join("/");
2665
2704
  for (const rule of file.rules) {
2666
2705
  if (matchesPattern(rule.pattern, normalized)) return rule.owners;
2667
2706
  }
@@ -2677,7 +2716,7 @@ function matchesPattern(rawPattern, repoPath) {
2677
2716
  return false;
2678
2717
  }
2679
2718
  async function readPackageJsonAuthor(serviceDir) {
2680
- const pkgPath = import_node_path6.default.join(serviceDir, "package.json");
2719
+ const pkgPath = import_node_path7.default.join(serviceDir, "package.json");
2681
2720
  if (!await exists(pkgPath)) return null;
2682
2721
  try {
2683
2722
  const pkg = await readJson(pkgPath);
@@ -2700,8 +2739,8 @@ async function computeServiceOwner(codeowners, repoPath, serviceDir) {
2700
2739
 
2701
2740
  // src/extract/errors.ts
2702
2741
  init_cjs_shims();
2703
- var import_node_fs7 = require("fs");
2704
- var import_node_path7 = __toESM(require("path"), 1);
2742
+ var import_node_fs8 = require("fs");
2743
+ var import_node_path8 = __toESM(require("path"), 1);
2705
2744
  var sink = [];
2706
2745
  function recordExtractionError(producer, file, err) {
2707
2746
  const e = err instanceof Error ? err : new Error(String(err));
@@ -2720,9 +2759,9 @@ function drainExtractionErrors() {
2720
2759
  }
2721
2760
  async function writeExtractionErrors(errors, errorsPath) {
2722
2761
  if (errors.length === 0) return;
2723
- await import_node_fs7.promises.mkdir(import_node_path7.default.dirname(errorsPath), { recursive: true });
2762
+ await import_node_fs8.promises.mkdir(import_node_path8.default.dirname(errorsPath), { recursive: true });
2724
2763
  const lines = errors.map((e) => JSON.stringify(e)).join("\n") + "\n";
2725
- await import_node_fs7.promises.appendFile(errorsPath, lines, "utf8");
2764
+ await import_node_fs8.promises.appendFile(errorsPath, lines, "utf8");
2726
2765
  }
2727
2766
  function isStrictExtractionEnabled() {
2728
2767
  const raw = process.env.NEAT_STRICT_EXTRACTION;
@@ -2745,9 +2784,9 @@ function isRejectedLogEnabled() {
2745
2784
  }
2746
2785
  async function writeRejectedExtracted(drops, rejectedPath) {
2747
2786
  if (drops.length === 0) return;
2748
- await import_node_fs7.promises.mkdir(import_node_path7.default.dirname(rejectedPath), { recursive: true });
2787
+ await import_node_fs8.promises.mkdir(import_node_path8.default.dirname(rejectedPath), { recursive: true });
2749
2788
  const lines = drops.map((d) => JSON.stringify({ ...d, ts: (/* @__PURE__ */ new Date()).toISOString() })).join("\n") + "\n";
2750
- await import_node_fs7.promises.appendFile(rejectedPath, lines, "utf8");
2789
+ await import_node_fs8.promises.appendFile(rejectedPath, lines, "utf8");
2751
2790
  }
2752
2791
  function formatPrecisionFloorBanner(count) {
2753
2792
  if (count === 1) return `[neat] 1 extracted edge dropped below precision floor`;
@@ -2770,21 +2809,21 @@ function workspaceGlobs(pkg) {
2770
2809
  return null;
2771
2810
  }
2772
2811
  async function loadGitignore(scanPath) {
2773
- const gitignorePath = import_node_path8.default.join(scanPath, ".gitignore");
2812
+ const gitignorePath = import_node_path9.default.join(scanPath, ".gitignore");
2774
2813
  if (!await exists(gitignorePath)) return null;
2775
- const raw = await import_node_fs8.promises.readFile(gitignorePath, "utf8");
2814
+ const raw = await import_node_fs9.promises.readFile(gitignorePath, "utf8");
2776
2815
  return (0, import_ignore.default)().add(raw);
2777
2816
  }
2778
2817
  async function walkDirs(start, scanPath, options, visit) {
2779
2818
  async function recurse(current, depth) {
2780
2819
  if (depth > options.maxDepth) return;
2781
- const entries = await import_node_fs8.promises.readdir(current, { withFileTypes: true }).catch(() => []);
2820
+ const entries = await import_node_fs9.promises.readdir(current, { withFileTypes: true }).catch(() => []);
2782
2821
  for (const entry2 of entries) {
2783
2822
  if (!entry2.isDirectory()) continue;
2784
2823
  if (IGNORED_DIRS.has(entry2.name)) continue;
2785
- const child = import_node_path8.default.join(current, entry2.name);
2824
+ const child = import_node_path9.default.join(current, entry2.name);
2786
2825
  if (options.ig) {
2787
- const rel = import_node_path8.default.relative(scanPath, child).split(import_node_path8.default.sep).join("/");
2826
+ const rel = import_node_path9.default.relative(scanPath, child).split(import_node_path9.default.sep).join("/");
2788
2827
  if (rel && options.ig.ignores(rel + "/")) continue;
2789
2828
  }
2790
2829
  if (await isPythonVenvDir(child)) continue;
@@ -2800,8 +2839,8 @@ async function expandWorkspaceGlobs(scanPath, globs) {
2800
2839
  for (const raw of globs) {
2801
2840
  const pattern = raw.replace(/^\.\//, "");
2802
2841
  if (!pattern.includes("*")) {
2803
- const candidate = import_node_path8.default.join(scanPath, pattern);
2804
- if (await exists(import_node_path8.default.join(candidate, "package.json"))) found.add(candidate);
2842
+ const candidate = import_node_path9.default.join(scanPath, pattern);
2843
+ if (await exists(import_node_path9.default.join(candidate, "package.json"))) found.add(candidate);
2805
2844
  continue;
2806
2845
  }
2807
2846
  const segments = pattern.split("/");
@@ -2810,13 +2849,13 @@ async function expandWorkspaceGlobs(scanPath, globs) {
2810
2849
  if (seg.includes("*")) break;
2811
2850
  staticSegments.push(seg);
2812
2851
  }
2813
- const start = import_node_path8.default.join(scanPath, ...staticSegments);
2852
+ const start = import_node_path9.default.join(scanPath, ...staticSegments);
2814
2853
  if (!await exists(start)) continue;
2815
2854
  const hasDoubleStar = pattern.includes("**");
2816
2855
  const walkDepth = hasDoubleStar ? scanDepth : Math.max(0, segments.length - staticSegments.length - 1);
2817
2856
  await walkDirs(start, scanPath, { maxDepth: walkDepth, ig: null }, async (dir) => {
2818
- const rel = import_node_path8.default.relative(scanPath, dir).split(import_node_path8.default.sep).join("/");
2819
- if ((0, import_minimatch2.minimatch)(rel, pattern) && await exists(import_node_path8.default.join(dir, "package.json"))) {
2857
+ const rel = import_node_path9.default.relative(scanPath, dir).split(import_node_path9.default.sep).join("/");
2858
+ if ((0, import_minimatch2.minimatch)(rel, pattern) && await exists(import_node_path9.default.join(dir, "package.json"))) {
2820
2859
  found.add(dir);
2821
2860
  }
2822
2861
  });
@@ -2836,13 +2875,13 @@ function detectJsFramework(pkg) {
2836
2875
  return void 0;
2837
2876
  }
2838
2877
  async function discoverNodeService(scanPath, dir) {
2839
- const pkgPath = import_node_path8.default.join(dir, "package.json");
2878
+ const pkgPath = import_node_path9.default.join(dir, "package.json");
2840
2879
  if (!await exists(pkgPath)) return null;
2841
2880
  let pkg;
2842
2881
  try {
2843
2882
  pkg = await readJson(pkgPath);
2844
2883
  } catch (err) {
2845
- recordExtractionError("services", import_node_path8.default.relative(scanPath, pkgPath), err);
2884
+ recordExtractionError("services", import_node_path9.default.relative(scanPath, pkgPath), err);
2846
2885
  return null;
2847
2886
  }
2848
2887
  if (!pkg.name) return null;
@@ -2854,7 +2893,7 @@ async function discoverNodeService(scanPath, dir) {
2854
2893
  language: "javascript",
2855
2894
  version: pkg.version,
2856
2895
  dependencies: pkg.dependencies ?? {},
2857
- repoPath: import_node_path8.default.relative(scanPath, dir),
2896
+ repoPath: import_node_path9.default.relative(scanPath, dir),
2858
2897
  ...pkg.engines?.node ? { nodeEngine: pkg.engines.node } : {},
2859
2898
  ...framework ? { framework } : {}
2860
2899
  };
@@ -2871,12 +2910,12 @@ async function discoverPyService(scanPath, dir) {
2871
2910
  language: "python",
2872
2911
  version: py.version,
2873
2912
  dependencies: py.dependencies,
2874
- repoPath: import_node_path8.default.relative(scanPath, dir)
2913
+ repoPath: import_node_path9.default.relative(scanPath, dir)
2875
2914
  };
2876
2915
  return { pkg, dir, node };
2877
2916
  }
2878
2917
  async function discoverServices(scanPath) {
2879
- const rootPkgPath = import_node_path8.default.join(scanPath, "package.json");
2918
+ const rootPkgPath = import_node_path9.default.join(scanPath, "package.json");
2880
2919
  let rootPkg = null;
2881
2920
  if (await exists(rootPkgPath)) {
2882
2921
  try {
@@ -2884,7 +2923,7 @@ async function discoverServices(scanPath) {
2884
2923
  } catch (err) {
2885
2924
  recordExtractionError(
2886
2925
  "services workspaces",
2887
- import_node_path8.default.relative(scanPath, rootPkgPath),
2926
+ import_node_path9.default.relative(scanPath, rootPkgPath),
2888
2927
  err
2889
2928
  );
2890
2929
  }
@@ -2901,9 +2940,9 @@ async function discoverServices(scanPath) {
2901
2940
  scanPath,
2902
2941
  { maxDepth: parseScanDepth(), ig },
2903
2942
  async (dir) => {
2904
- if (await exists(import_node_path8.default.join(dir, "package.json"))) {
2943
+ if (await exists(import_node_path9.default.join(dir, "package.json"))) {
2905
2944
  candidateDirs.push(dir);
2906
- } else if (await exists(import_node_path8.default.join(dir, "pyproject.toml")) || await exists(import_node_path8.default.join(dir, "requirements.txt")) || await exists(import_node_path8.default.join(dir, "setup.py"))) {
2945
+ } else if (await exists(import_node_path9.default.join(dir, "pyproject.toml")) || await exists(import_node_path9.default.join(dir, "requirements.txt")) || await exists(import_node_path9.default.join(dir, "setup.py"))) {
2907
2946
  candidateDirs.push(dir);
2908
2947
  }
2909
2948
  }
@@ -2917,8 +2956,8 @@ async function discoverServices(scanPath) {
2917
2956
  if (!service) continue;
2918
2957
  const existingDir = seen.get(service.node.name);
2919
2958
  if (existingDir !== void 0) {
2920
- const a = import_node_path8.default.relative(scanPath, existingDir) || ".";
2921
- const b = import_node_path8.default.relative(scanPath, dir) || ".";
2959
+ const a = import_node_path9.default.relative(scanPath, existingDir) || ".";
2960
+ const b = import_node_path9.default.relative(scanPath, dir) || ".";
2922
2961
  console.warn(
2923
2962
  `[neat] duplicate package name "${service.node.name}" \u2014 keeping ${a}, ignoring ${b}`
2924
2963
  );
@@ -2955,8 +2994,8 @@ function addServiceNodes(graph, services) {
2955
2994
 
2956
2995
  // src/extract/aliases.ts
2957
2996
  init_cjs_shims();
2958
- var import_node_path9 = __toESM(require("path"), 1);
2959
- var import_node_fs9 = require("fs");
2997
+ var import_node_path10 = __toESM(require("path"), 1);
2998
+ var import_node_fs10 = require("fs");
2960
2999
  var import_yaml2 = require("yaml");
2961
3000
  var import_types6 = require("@neat.is/types");
2962
3001
  var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
@@ -2983,14 +3022,14 @@ function indexServicesByName(services) {
2983
3022
  const map = /* @__PURE__ */ new Map();
2984
3023
  for (const s of services) {
2985
3024
  map.set(s.node.name, s.node.id);
2986
- map.set(import_node_path9.default.basename(s.dir), s.node.id);
3025
+ map.set(import_node_path10.default.basename(s.dir), s.node.id);
2987
3026
  }
2988
3027
  return map;
2989
3028
  }
2990
3029
  async function collectComposeAliases(graph, scanPath, serviceIndex) {
2991
3030
  let composePath = null;
2992
3031
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
2993
- const abs = import_node_path9.default.join(scanPath, name);
3032
+ const abs = import_node_path10.default.join(scanPath, name);
2994
3033
  if (await exists(abs)) {
2995
3034
  composePath = abs;
2996
3035
  break;
@@ -3003,7 +3042,7 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
3003
3042
  } catch (err) {
3004
3043
  recordExtractionError(
3005
3044
  "aliases compose",
3006
- import_node_path9.default.relative(scanPath, composePath),
3045
+ import_node_path10.default.relative(scanPath, composePath),
3007
3046
  err
3008
3047
  );
3009
3048
  return;
@@ -3046,11 +3085,11 @@ function parseDockerfileLabels(content) {
3046
3085
  }
3047
3086
  async function collectDockerfileAliases(graph, services) {
3048
3087
  for (const service of services) {
3049
- const dockerfilePath = import_node_path9.default.join(service.dir, "Dockerfile");
3088
+ const dockerfilePath = import_node_path10.default.join(service.dir, "Dockerfile");
3050
3089
  if (!await exists(dockerfilePath)) continue;
3051
3090
  let content;
3052
3091
  try {
3053
- content = await import_node_fs9.promises.readFile(dockerfilePath, "utf8");
3092
+ content = await import_node_fs10.promises.readFile(dockerfilePath, "utf8");
3054
3093
  } catch (err) {
3055
3094
  recordExtractionError("aliases dockerfile", dockerfilePath, err);
3056
3095
  continue;
@@ -3062,15 +3101,15 @@ async function collectDockerfileAliases(graph, services) {
3062
3101
  async function walkYamlFiles(start, depth = 0, max = 5) {
3063
3102
  if (depth > max) return [];
3064
3103
  const out = [];
3065
- const entries = await import_node_fs9.promises.readdir(start, { withFileTypes: true }).catch(() => []);
3104
+ const entries = await import_node_fs10.promises.readdir(start, { withFileTypes: true }).catch(() => []);
3066
3105
  for (const entry2 of entries) {
3067
3106
  if (entry2.isDirectory()) {
3068
3107
  if (IGNORED_DIRS.has(entry2.name)) continue;
3069
- const child = import_node_path9.default.join(start, entry2.name);
3108
+ const child = import_node_path10.default.join(start, entry2.name);
3070
3109
  if (await isPythonVenvDir(child)) continue;
3071
3110
  out.push(...await walkYamlFiles(child, depth + 1, max));
3072
- } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path9.default.extname(entry2.name))) {
3073
- out.push(import_node_path9.default.join(start, entry2.name));
3111
+ } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path10.default.extname(entry2.name))) {
3112
+ out.push(import_node_path10.default.join(start, entry2.name));
3074
3113
  }
3075
3114
  }
3076
3115
  return out;
@@ -3097,7 +3136,7 @@ function k8sServiceTarget(doc, byName) {
3097
3136
  async function collectK8sAliases(graph, scanPath, serviceIndex) {
3098
3137
  const files = await walkYamlFiles(scanPath);
3099
3138
  for (const file of files) {
3100
- const content = await import_node_fs9.promises.readFile(file, "utf8");
3139
+ const content = await import_node_fs10.promises.readFile(file, "utf8");
3101
3140
  let docs;
3102
3141
  try {
3103
3142
  docs = (0, import_yaml2.parseAllDocuments)(content).map((d) => d.toJSON());
@@ -3125,20 +3164,20 @@ init_cjs_shims();
3125
3164
 
3126
3165
  // src/extract/calls/shared.ts
3127
3166
  init_cjs_shims();
3128
- var import_node_fs10 = require("fs");
3129
- var import_node_path10 = __toESM(require("path"), 1);
3167
+ var import_node_fs11 = require("fs");
3168
+ var import_node_path11 = __toESM(require("path"), 1);
3130
3169
  var import_types7 = require("@neat.is/types");
3131
3170
  async function walkSourceFiles(dir) {
3132
3171
  const out = [];
3133
3172
  async function walk(current) {
3134
- const entries = await import_node_fs10.promises.readdir(current, { withFileTypes: true }).catch(() => []);
3173
+ const entries = await import_node_fs11.promises.readdir(current, { withFileTypes: true }).catch(() => []);
3135
3174
  for (const entry2 of entries) {
3136
- const full = import_node_path10.default.join(current, entry2.name);
3175
+ const full = import_node_path11.default.join(current, entry2.name);
3137
3176
  if (entry2.isDirectory()) {
3138
3177
  if (IGNORED_DIRS.has(entry2.name)) continue;
3139
3178
  if (await isPythonVenvDir(full)) continue;
3140
3179
  await walk(full);
3141
- } else if (entry2.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path10.default.extname(entry2.name))) {
3180
+ } else if (entry2.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path11.default.extname(entry2.name))) {
3142
3181
  out.push(full);
3143
3182
  }
3144
3183
  }
@@ -3151,7 +3190,7 @@ async function loadSourceFiles(dir) {
3151
3190
  const out = [];
3152
3191
  for (const p of paths) {
3153
3192
  try {
3154
- const content = await import_node_fs10.promises.readFile(p, "utf8");
3193
+ const content = await import_node_fs11.promises.readFile(p, "utf8");
3155
3194
  out.push({ path: p, content });
3156
3195
  } catch {
3157
3196
  }
@@ -3171,7 +3210,7 @@ function toPosix2(p) {
3171
3210
  return p.split("\\").join("/");
3172
3211
  }
3173
3212
  function languageForPath(relPath) {
3174
- switch (import_node_path10.default.extname(relPath).toLowerCase()) {
3213
+ switch (import_node_path11.default.extname(relPath).toLowerCase()) {
3175
3214
  case ".py":
3176
3215
  return "python";
3177
3216
  case ".ts":
@@ -3221,14 +3260,14 @@ function ensureFileNode(graph, serviceName, serviceNodeId, relPath) {
3221
3260
  }
3222
3261
 
3223
3262
  // src/extract/files.ts
3224
- var import_node_path11 = __toESM(require("path"), 1);
3263
+ var import_node_path12 = __toESM(require("path"), 1);
3225
3264
  async function addFiles(graph, services) {
3226
3265
  let nodesAdded = 0;
3227
3266
  let edgesAdded = 0;
3228
3267
  for (const service of services) {
3229
3268
  const filePaths = await walkSourceFiles(service.dir);
3230
3269
  for (const filePath of filePaths) {
3231
- const relPath = toPosix2(import_node_path11.default.relative(service.dir, filePath));
3270
+ const relPath = toPosix2(import_node_path12.default.relative(service.dir, filePath));
3232
3271
  const { nodesAdded: n, edgesAdded: e } = ensureFileNode(
3233
3272
  graph,
3234
3273
  service.pkg.name,
@@ -3244,8 +3283,8 @@ async function addFiles(graph, services) {
3244
3283
 
3245
3284
  // src/extract/imports.ts
3246
3285
  init_cjs_shims();
3247
- var import_node_path12 = __toESM(require("path"), 1);
3248
- var import_node_fs11 = require("fs");
3286
+ var import_node_path13 = __toESM(require("path"), 1);
3287
+ var import_node_fs12 = require("fs");
3249
3288
  var import_tree_sitter = __toESM(require("tree-sitter"), 1);
3250
3289
  var import_tree_sitter_javascript = __toESM(require("tree-sitter-javascript"), 1);
3251
3290
  var import_tree_sitter_python = __toESM(require("tree-sitter-python"), 1);
@@ -3349,15 +3388,15 @@ function collectPyImports(node, out) {
3349
3388
  }
3350
3389
  async function fileExists(p) {
3351
3390
  try {
3352
- await import_node_fs11.promises.access(p);
3391
+ await import_node_fs12.promises.access(p);
3353
3392
  return true;
3354
3393
  } catch {
3355
3394
  return false;
3356
3395
  }
3357
3396
  }
3358
3397
  function isWithinServiceDir(candidate, serviceDir) {
3359
- const rel = import_node_path12.default.relative(serviceDir, candidate);
3360
- return rel !== "" && !rel.startsWith("..") && !import_node_path12.default.isAbsolute(rel);
3398
+ const rel = import_node_path13.default.relative(serviceDir, candidate);
3399
+ return rel !== "" && !rel.startsWith("..") && !import_node_path13.default.isAbsolute(rel);
3361
3400
  }
3362
3401
  var JS_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
3363
3402
  var JS_INDEX_FILES = JS_EXTENSIONS.map((ext) => `index${ext}`);
@@ -3365,22 +3404,22 @@ async function firstExistingCandidate(base, serviceDir) {
3365
3404
  for (const ext of JS_EXTENSIONS) {
3366
3405
  const candidate = base + ext;
3367
3406
  if (isWithinServiceDir(candidate, serviceDir) && await fileExists(candidate)) {
3368
- return toPosix2(import_node_path12.default.relative(serviceDir, candidate));
3407
+ return toPosix2(import_node_path13.default.relative(serviceDir, candidate));
3369
3408
  }
3370
3409
  }
3371
3410
  for (const indexFile of JS_INDEX_FILES) {
3372
- const candidate = import_node_path12.default.join(base, indexFile);
3411
+ const candidate = import_node_path13.default.join(base, indexFile);
3373
3412
  if (isWithinServiceDir(candidate, serviceDir) && await fileExists(candidate)) {
3374
- return toPosix2(import_node_path12.default.relative(serviceDir, candidate));
3413
+ return toPosix2(import_node_path13.default.relative(serviceDir, candidate));
3375
3414
  }
3376
3415
  }
3377
3416
  return null;
3378
3417
  }
3379
3418
  async function loadTsPathConfig(serviceDir) {
3380
- const tsconfigPath = import_node_path12.default.join(serviceDir, "tsconfig.json");
3419
+ const tsconfigPath = import_node_path13.default.join(serviceDir, "tsconfig.json");
3381
3420
  let raw;
3382
3421
  try {
3383
- raw = await import_node_fs11.promises.readFile(tsconfigPath, "utf8");
3422
+ raw = await import_node_fs12.promises.readFile(tsconfigPath, "utf8");
3384
3423
  } catch {
3385
3424
  return null;
3386
3425
  }
@@ -3389,7 +3428,7 @@ async function loadTsPathConfig(serviceDir) {
3389
3428
  const paths = parsed.compilerOptions?.paths;
3390
3429
  if (!paths || Object.keys(paths).length === 0) return null;
3391
3430
  const baseUrl = parsed.compilerOptions?.baseUrl;
3392
- return { paths, baseDir: baseUrl ? import_node_path12.default.resolve(serviceDir, baseUrl) : serviceDir };
3431
+ return { paths, baseDir: baseUrl ? import_node_path13.default.resolve(serviceDir, baseUrl) : serviceDir };
3393
3432
  } catch (err) {
3394
3433
  recordExtractionError("import alias resolution", tsconfigPath, err);
3395
3434
  return null;
@@ -3407,11 +3446,11 @@ async function resolveTsAlias(specifier, config, serviceDir) {
3407
3446
  if (suffix === null) continue;
3408
3447
  for (const target of targets) {
3409
3448
  const targetBase = target.endsWith("/*") ? target.slice(0, -2) : target.replace(/\*$/, "");
3410
- const resolvedBase = import_node_path12.default.resolve(config.baseDir, targetBase, suffix);
3449
+ const resolvedBase = import_node_path13.default.resolve(config.baseDir, targetBase, suffix);
3411
3450
  const hit = await firstExistingCandidate(resolvedBase, serviceDir);
3412
3451
  if (hit) return hit;
3413
3452
  if (isWithinServiceDir(resolvedBase, serviceDir) && await fileExists(resolvedBase)) {
3414
- return toPosix2(import_node_path12.default.relative(serviceDir, resolvedBase));
3453
+ return toPosix2(import_node_path13.default.relative(serviceDir, resolvedBase));
3415
3454
  }
3416
3455
  }
3417
3456
  }
@@ -3420,18 +3459,18 @@ async function resolveTsAlias(specifier, config, serviceDir) {
3420
3459
  async function resolveJsImport(specifier, importerDir, serviceDir, tsPaths) {
3421
3460
  if (!specifier) return null;
3422
3461
  if (specifier.startsWith("./") || specifier.startsWith("../")) {
3423
- const base = import_node_path12.default.resolve(importerDir, specifier);
3424
- const ext = import_node_path12.default.extname(specifier);
3462
+ const base = import_node_path13.default.resolve(importerDir, specifier);
3463
+ const ext = import_node_path13.default.extname(specifier);
3425
3464
  if (ext) {
3426
3465
  if (ext === ".js" || ext === ".jsx") {
3427
3466
  const tsExt = ext === ".jsx" ? ".tsx" : ".ts";
3428
3467
  const tsSibling = base.slice(0, -ext.length) + tsExt;
3429
3468
  if (isWithinServiceDir(tsSibling, serviceDir) && await fileExists(tsSibling)) {
3430
- return toPosix2(import_node_path12.default.relative(serviceDir, tsSibling));
3469
+ return toPosix2(import_node_path13.default.relative(serviceDir, tsSibling));
3431
3470
  }
3432
3471
  }
3433
3472
  if (isWithinServiceDir(base, serviceDir) && await fileExists(base)) {
3434
- return toPosix2(import_node_path12.default.relative(serviceDir, base));
3473
+ return toPosix2(import_node_path13.default.relative(serviceDir, base));
3435
3474
  }
3436
3475
  return null;
3437
3476
  }
@@ -3445,15 +3484,15 @@ async function resolvePyImport(imp, importerPath, serviceDir) {
3445
3484
  const relPath = imp.modulePath.split(".").join("/");
3446
3485
  let baseDir;
3447
3486
  if (imp.level > 0) {
3448
- baseDir = import_node_path12.default.dirname(importerPath);
3449
- for (let i = 1; i < imp.level; i++) baseDir = import_node_path12.default.dirname(baseDir);
3487
+ baseDir = import_node_path13.default.dirname(importerPath);
3488
+ for (let i = 1; i < imp.level; i++) baseDir = import_node_path13.default.dirname(baseDir);
3450
3489
  } else {
3451
3490
  baseDir = serviceDir;
3452
3491
  }
3453
- const candidates = [import_node_path12.default.join(baseDir, `${relPath}.py`), import_node_path12.default.join(baseDir, relPath, "__init__.py")];
3492
+ const candidates = [import_node_path13.default.join(baseDir, `${relPath}.py`), import_node_path13.default.join(baseDir, relPath, "__init__.py")];
3454
3493
  for (const candidate of candidates) {
3455
3494
  if (isWithinServiceDir(candidate, serviceDir) && await fileExists(candidate)) {
3456
- return toPosix2(import_node_path12.default.relative(serviceDir, candidate));
3495
+ return toPosix2(import_node_path13.default.relative(serviceDir, candidate));
3457
3496
  }
3458
3497
  }
3459
3498
  return null;
@@ -3484,9 +3523,9 @@ async function addImports(graph, services) {
3484
3523
  const files = await loadSourceFiles(service.dir);
3485
3524
  for (const file of files) {
3486
3525
  if (isTestPath(file.path)) continue;
3487
- const relFile = toPosix2(import_node_path12.default.relative(service.dir, file.path));
3526
+ const relFile = toPosix2(import_node_path13.default.relative(service.dir, file.path));
3488
3527
  const importerFileId = (0, import_types8.fileId)(service.pkg.name, relFile);
3489
- const isPython = import_node_path12.default.extname(file.path) === ".py";
3528
+ const isPython = import_node_path13.default.extname(file.path) === ".py";
3490
3529
  if (isPython) {
3491
3530
  let pyImports = [];
3492
3531
  try {
@@ -3520,7 +3559,7 @@ async function addImports(graph, services) {
3520
3559
  continue;
3521
3560
  }
3522
3561
  for (const imp of jsImports) {
3523
- const resolved = await resolveJsImport(imp.specifier, import_node_path12.default.dirname(file.path), service.dir, tsPaths);
3562
+ const resolved = await resolveJsImport(imp.specifier, import_node_path13.default.dirname(file.path), service.dir, tsPaths);
3524
3563
  if (!resolved) continue;
3525
3564
  edgesAdded += emitImportEdge(
3526
3565
  graph,
@@ -3539,14 +3578,14 @@ async function addImports(graph, services) {
3539
3578
 
3540
3579
  // src/extract/databases/index.ts
3541
3580
  init_cjs_shims();
3542
- var import_node_path20 = __toESM(require("path"), 1);
3581
+ var import_node_path21 = __toESM(require("path"), 1);
3543
3582
  var import_types9 = require("@neat.is/types");
3544
3583
 
3545
3584
  // src/extract/databases/db-config-yaml.ts
3546
3585
  init_cjs_shims();
3547
- var import_node_path13 = __toESM(require("path"), 1);
3586
+ var import_node_path14 = __toESM(require("path"), 1);
3548
3587
  async function parse(serviceDir) {
3549
- const yamlPath = import_node_path13.default.join(serviceDir, "db-config.yaml");
3588
+ const yamlPath = import_node_path14.default.join(serviceDir, "db-config.yaml");
3550
3589
  if (!await exists(yamlPath)) return [];
3551
3590
  const raw = await readYaml(yamlPath);
3552
3591
  return [
@@ -3564,13 +3603,13 @@ var dbConfigYamlParser = { name: "db-config.yaml", parse };
3564
3603
 
3565
3604
  // src/extract/databases/dotenv.ts
3566
3605
  init_cjs_shims();
3567
- var import_node_fs13 = require("fs");
3568
- var import_node_path15 = __toESM(require("path"), 1);
3606
+ var import_node_fs14 = require("fs");
3607
+ var import_node_path16 = __toESM(require("path"), 1);
3569
3608
 
3570
3609
  // src/extract/databases/shared.ts
3571
3610
  init_cjs_shims();
3572
- var import_node_fs12 = require("fs");
3573
- var import_node_path14 = __toESM(require("path"), 1);
3611
+ var import_node_fs13 = require("fs");
3612
+ var import_node_path15 = __toESM(require("path"), 1);
3574
3613
  function schemeToEngine(scheme) {
3575
3614
  const s = scheme.toLowerCase().split("+")[0];
3576
3615
  switch (s) {
@@ -3609,14 +3648,14 @@ function parseConnectionString(url) {
3609
3648
  }
3610
3649
  async function readIfExists(filePath) {
3611
3650
  try {
3612
- return await import_node_fs12.promises.readFile(filePath, "utf8");
3651
+ return await import_node_fs13.promises.readFile(filePath, "utf8");
3613
3652
  } catch {
3614
3653
  return null;
3615
3654
  }
3616
3655
  }
3617
3656
  async function findFirst(serviceDir, candidates) {
3618
3657
  for (const rel of candidates) {
3619
- const abs = import_node_path14.default.join(serviceDir, rel);
3658
+ const abs = import_node_path15.default.join(serviceDir, rel);
3620
3659
  const content = await readIfExists(abs);
3621
3660
  if (content !== null) return abs;
3622
3661
  }
@@ -3667,15 +3706,15 @@ function parseDotenvLine(line) {
3667
3706
  return { key, value };
3668
3707
  }
3669
3708
  async function parse2(serviceDir) {
3670
- const entries = await import_node_fs13.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
3709
+ const entries = await import_node_fs14.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
3671
3710
  const configs = [];
3672
3711
  const seen = /* @__PURE__ */ new Set();
3673
3712
  for (const entry2 of entries) {
3674
3713
  if (!entry2.isFile()) continue;
3675
3714
  const match = isConfigFile(entry2.name);
3676
3715
  if (!match.match || match.fileType !== "env") continue;
3677
- const filePath = import_node_path15.default.join(serviceDir, entry2.name);
3678
- const content = await import_node_fs13.promises.readFile(filePath, "utf8");
3716
+ const filePath = import_node_path16.default.join(serviceDir, entry2.name);
3717
+ const content = await import_node_fs14.promises.readFile(filePath, "utf8");
3679
3718
  for (const line of content.split("\n")) {
3680
3719
  const parsed = parseDotenvLine(line);
3681
3720
  if (!parsed) continue;
@@ -3694,9 +3733,9 @@ var dotenvParser = { name: ".env", parse: parse2 };
3694
3733
 
3695
3734
  // src/extract/databases/prisma.ts
3696
3735
  init_cjs_shims();
3697
- var import_node_path16 = __toESM(require("path"), 1);
3736
+ var import_node_path17 = __toESM(require("path"), 1);
3698
3737
  async function parse3(serviceDir) {
3699
- const schemaPath = import_node_path16.default.join(serviceDir, "prisma", "schema.prisma");
3738
+ const schemaPath = import_node_path17.default.join(serviceDir, "prisma", "schema.prisma");
3700
3739
  const content = await readIfExists(schemaPath);
3701
3740
  if (!content) return [];
3702
3741
  const block = content.match(/datasource\s+\w+\s*\{([^}]*)\}/s);
@@ -3828,10 +3867,10 @@ var knexParser = { name: "knex", parse: parse5 };
3828
3867
 
3829
3868
  // src/extract/databases/ormconfig.ts
3830
3869
  init_cjs_shims();
3831
- var import_node_path17 = __toESM(require("path"), 1);
3870
+ var import_node_path18 = __toESM(require("path"), 1);
3832
3871
  async function parse6(serviceDir) {
3833
3872
  for (const candidate of ["ormconfig.json", "ormconfig.yaml", "ormconfig.yml"]) {
3834
- const abs = import_node_path17.default.join(serviceDir, candidate);
3873
+ const abs = import_node_path18.default.join(serviceDir, candidate);
3835
3874
  if (!await exists(abs)) continue;
3836
3875
  const raw = candidate.endsWith(".json") ? await readJson(abs) : await readYaml(abs);
3837
3876
  const entries = Array.isArray(raw) ? raw : [raw];
@@ -3891,9 +3930,9 @@ var typeormParser = { name: "typeorm", parse: parse7 };
3891
3930
 
3892
3931
  // src/extract/databases/sequelize.ts
3893
3932
  init_cjs_shims();
3894
- var import_node_path18 = __toESM(require("path"), 1);
3933
+ var import_node_path19 = __toESM(require("path"), 1);
3895
3934
  async function parse8(serviceDir) {
3896
- const configPath = import_node_path18.default.join(serviceDir, "config", "config.json");
3935
+ const configPath = import_node_path19.default.join(serviceDir, "config", "config.json");
3897
3936
  if (!await exists(configPath)) return [];
3898
3937
  const raw = await readJson(configPath);
3899
3938
  const out = [];
@@ -3920,7 +3959,7 @@ var sequelizeParser = { name: "sequelize", parse: parse8 };
3920
3959
 
3921
3960
  // src/extract/databases/docker-compose.ts
3922
3961
  init_cjs_shims();
3923
- var import_node_path19 = __toESM(require("path"), 1);
3962
+ var import_node_path20 = __toESM(require("path"), 1);
3924
3963
  function portFromService(svc) {
3925
3964
  for (const raw of svc.ports ?? []) {
3926
3965
  const str = String(raw);
@@ -3947,7 +3986,7 @@ function databaseFromEnv(svc) {
3947
3986
  }
3948
3987
  async function parse9(serviceDir) {
3949
3988
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
3950
- const abs = import_node_path19.default.join(serviceDir, name);
3989
+ const abs = import_node_path20.default.join(serviceDir, name);
3951
3990
  if (!await exists(abs)) continue;
3952
3991
  const raw = await readYaml(abs);
3953
3992
  if (!raw?.services) return [];
@@ -4119,7 +4158,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
4119
4158
  discoveredVia: mergedDiscoveredVia
4120
4159
  });
4121
4160
  }
4122
- const relConfigFile = toPosix2(import_node_path20.default.relative(service.dir, config.sourceFile));
4161
+ const relConfigFile = toPosix2(import_node_path21.default.relative(service.dir, config.sourceFile));
4123
4162
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
4124
4163
  graph,
4125
4164
  service.pkg.name,
@@ -4128,7 +4167,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
4128
4167
  );
4129
4168
  nodesAdded += fn;
4130
4169
  edgesAdded += fe;
4131
- const evidenceFile = toPosix2(import_node_path20.default.relative(scanPath, config.sourceFile));
4170
+ const evidenceFile = toPosix2(import_node_path21.default.relative(scanPath, config.sourceFile));
4132
4171
  const edge = {
4133
4172
  id: (0, import_types4.extractedEdgeId)(fileNodeId, dbNode.id, import_types9.EdgeType.CONNECTS_TO),
4134
4173
  source: fileNodeId,
@@ -4162,15 +4201,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
4162
4201
 
4163
4202
  // src/extract/configs.ts
4164
4203
  init_cjs_shims();
4165
- var import_node_fs14 = require("fs");
4166
- var import_node_path21 = __toESM(require("path"), 1);
4204
+ var import_node_fs15 = require("fs");
4205
+ var import_node_path22 = __toESM(require("path"), 1);
4167
4206
  var import_types10 = require("@neat.is/types");
4168
4207
  async function walkConfigFiles(dir) {
4169
4208
  const out = [];
4170
4209
  async function walk(current) {
4171
- const entries = await import_node_fs14.promises.readdir(current, { withFileTypes: true });
4210
+ const entries = await import_node_fs15.promises.readdir(current, { withFileTypes: true });
4172
4211
  for (const entry2 of entries) {
4173
- const full = import_node_path21.default.join(current, entry2.name);
4212
+ const full = import_node_path22.default.join(current, entry2.name);
4174
4213
  if (entry2.isDirectory()) {
4175
4214
  if (IGNORED_DIRS.has(entry2.name)) continue;
4176
4215
  if (await isPythonVenvDir(full)) continue;
@@ -4189,19 +4228,19 @@ async function addConfigNodes(graph, services, scanPath) {
4189
4228
  for (const service of services) {
4190
4229
  const configFiles = await walkConfigFiles(service.dir);
4191
4230
  for (const file of configFiles) {
4192
- const relPath = import_node_path21.default.relative(scanPath, file);
4231
+ const relPath = import_node_path22.default.relative(scanPath, file);
4193
4232
  const node = {
4194
4233
  id: (0, import_types10.configId)(relPath),
4195
4234
  type: import_types10.NodeType.ConfigNode,
4196
- name: import_node_path21.default.basename(file),
4235
+ name: import_node_path22.default.basename(file),
4197
4236
  path: relPath,
4198
- fileType: isConfigFile(import_node_path21.default.basename(file)).fileType
4237
+ fileType: isConfigFile(import_node_path22.default.basename(file)).fileType
4199
4238
  };
4200
4239
  if (!graph.hasNode(node.id)) {
4201
4240
  graph.addNode(node.id, node);
4202
4241
  nodesAdded++;
4203
4242
  }
4204
- const relToService = toPosix2(import_node_path21.default.relative(service.dir, file));
4243
+ const relToService = toPosix2(import_node_path22.default.relative(service.dir, file));
4205
4244
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
4206
4245
  graph,
4207
4246
  service.pkg.name,
@@ -4217,7 +4256,7 @@ async function addConfigNodes(graph, services, scanPath) {
4217
4256
  type: import_types10.EdgeType.CONFIGURED_BY,
4218
4257
  provenance: import_types10.Provenance.EXTRACTED,
4219
4258
  confidence: (0, import_types10.confidenceForExtracted)("structural"),
4220
- evidence: { file: relPath.split(import_node_path21.default.sep).join("/") }
4259
+ evidence: { file: relPath.split(import_node_path22.default.sep).join("/") }
4221
4260
  };
4222
4261
  if (!graph.hasEdge(edge.id)) {
4223
4262
  graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
@@ -4234,7 +4273,7 @@ var import_types16 = require("@neat.is/types");
4234
4273
 
4235
4274
  // src/extract/calls/http.ts
4236
4275
  init_cjs_shims();
4237
- var import_node_path22 = __toESM(require("path"), 1);
4276
+ var import_node_path23 = __toESM(require("path"), 1);
4238
4277
  var import_tree_sitter2 = __toESM(require("tree-sitter"), 1);
4239
4278
  var import_tree_sitter_javascript2 = __toESM(require("tree-sitter-javascript"), 1);
4240
4279
  var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
@@ -4303,9 +4342,9 @@ async function addHttpCallEdges(graph, services) {
4303
4342
  const knownHosts = /* @__PURE__ */ new Set();
4304
4343
  const hostToNodeId = /* @__PURE__ */ new Map();
4305
4344
  for (const service of services) {
4306
- knownHosts.add(import_node_path22.default.basename(service.dir));
4345
+ knownHosts.add(import_node_path23.default.basename(service.dir));
4307
4346
  knownHosts.add(service.pkg.name);
4308
- hostToNodeId.set(import_node_path22.default.basename(service.dir), service.node.id);
4347
+ hostToNodeId.set(import_node_path23.default.basename(service.dir), service.node.id);
4309
4348
  hostToNodeId.set(service.pkg.name, service.node.id);
4310
4349
  }
4311
4350
  let nodesAdded = 0;
@@ -4315,7 +4354,7 @@ async function addHttpCallEdges(graph, services) {
4315
4354
  const seen = /* @__PURE__ */ new Set();
4316
4355
  for (const file of files) {
4317
4356
  if (isTestPath(file.path)) continue;
4318
- const parser = import_node_path22.default.extname(file.path) === ".py" ? pyParser : jsParser;
4357
+ const parser = import_node_path23.default.extname(file.path) === ".py" ? pyParser : jsParser;
4319
4358
  let sites;
4320
4359
  try {
4321
4360
  sites = callsFromSource(file.content, parser, knownHosts);
@@ -4324,7 +4363,7 @@ async function addHttpCallEdges(graph, services) {
4324
4363
  continue;
4325
4364
  }
4326
4365
  if (sites.length === 0) continue;
4327
- const relFile = toPosix2(import_node_path22.default.relative(service.dir, file.path));
4366
+ const relFile = toPosix2(import_node_path23.default.relative(service.dir, file.path));
4328
4367
  for (const site of sites) {
4329
4368
  const targetId = hostToNodeId.get(site.host);
4330
4369
  if (!targetId || targetId === service.node.id) continue;
@@ -4378,7 +4417,7 @@ async function addHttpCallEdges(graph, services) {
4378
4417
 
4379
4418
  // src/extract/calls/kafka.ts
4380
4419
  init_cjs_shims();
4381
- var import_node_path23 = __toESM(require("path"), 1);
4420
+ var import_node_path24 = __toESM(require("path"), 1);
4382
4421
  var import_types12 = require("@neat.is/types");
4383
4422
  var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
4384
4423
  var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
@@ -4409,7 +4448,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
4409
4448
  // tier (ADR-066).
4410
4449
  confidenceKind: "verified-call-site",
4411
4450
  evidence: {
4412
- file: import_node_path23.default.relative(serviceDir, file.path),
4451
+ file: import_node_path24.default.relative(serviceDir, file.path),
4413
4452
  line,
4414
4453
  snippet: snippet(file.content, line)
4415
4454
  }
@@ -4422,7 +4461,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
4422
4461
 
4423
4462
  // src/extract/calls/redis.ts
4424
4463
  init_cjs_shims();
4425
- var import_node_path24 = __toESM(require("path"), 1);
4464
+ var import_node_path25 = __toESM(require("path"), 1);
4426
4465
  var import_types13 = require("@neat.is/types");
4427
4466
  var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
4428
4467
  function redisEndpointsFromFile(file, serviceDir) {
@@ -4445,7 +4484,7 @@ function redisEndpointsFromFile(file, serviceDir) {
4445
4484
  // support tier (ADR-066).
4446
4485
  confidenceKind: "url-with-structural-support",
4447
4486
  evidence: {
4448
- file: import_node_path24.default.relative(serviceDir, file.path),
4487
+ file: import_node_path25.default.relative(serviceDir, file.path),
4449
4488
  line,
4450
4489
  snippet: snippet(file.content, line)
4451
4490
  }
@@ -4456,7 +4495,7 @@ function redisEndpointsFromFile(file, serviceDir) {
4456
4495
 
4457
4496
  // src/extract/calls/aws.ts
4458
4497
  init_cjs_shims();
4459
- var import_node_path25 = __toESM(require("path"), 1);
4498
+ var import_node_path26 = __toESM(require("path"), 1);
4460
4499
  var import_types14 = require("@neat.is/types");
4461
4500
  var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
4462
4501
  var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
@@ -4490,7 +4529,7 @@ function awsEndpointsFromFile(file, serviceDir) {
4490
4529
  // (ADR-066).
4491
4530
  confidenceKind: "verified-call-site",
4492
4531
  evidence: {
4493
- file: import_node_path25.default.relative(serviceDir, file.path),
4532
+ file: import_node_path26.default.relative(serviceDir, file.path),
4494
4533
  line,
4495
4534
  snippet: snippet(file.content, line)
4496
4535
  }
@@ -4515,7 +4554,7 @@ function awsEndpointsFromFile(file, serviceDir) {
4515
4554
 
4516
4555
  // src/extract/calls/grpc.ts
4517
4556
  init_cjs_shims();
4518
- var import_node_path26 = __toESM(require("path"), 1);
4557
+ var import_node_path27 = __toESM(require("path"), 1);
4519
4558
  var import_types15 = require("@neat.is/types");
4520
4559
  var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
4521
4560
  var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
@@ -4574,7 +4613,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
4574
4613
  // tier (ADR-066).
4575
4614
  confidenceKind: "verified-call-site",
4576
4615
  evidence: {
4577
- file: import_node_path26.default.relative(serviceDir, file.path),
4616
+ file: import_node_path27.default.relative(serviceDir, file.path),
4578
4617
  line,
4579
4618
  snippet: snippet(file.content, line)
4580
4619
  }
@@ -4685,7 +4724,7 @@ init_cjs_shims();
4685
4724
 
4686
4725
  // src/extract/infra/docker-compose.ts
4687
4726
  init_cjs_shims();
4688
- var import_node_path27 = __toESM(require("path"), 1);
4727
+ var import_node_path28 = __toESM(require("path"), 1);
4689
4728
  var import_types18 = require("@neat.is/types");
4690
4729
 
4691
4730
  // src/extract/infra/shared.ts
@@ -4723,7 +4762,7 @@ function dependsOnList(value) {
4723
4762
  }
4724
4763
  function serviceNameToServiceNode(name, services) {
4725
4764
  for (const s of services) {
4726
- if (s.node.name === name || import_node_path27.default.basename(s.dir) === name) return s.node.id;
4765
+ if (s.node.name === name || import_node_path28.default.basename(s.dir) === name) return s.node.id;
4727
4766
  }
4728
4767
  return null;
4729
4768
  }
@@ -4732,7 +4771,7 @@ async function addComposeInfra(graph, scanPath, services) {
4732
4771
  let edgesAdded = 0;
4733
4772
  let composePath = null;
4734
4773
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
4735
- const abs = import_node_path27.default.join(scanPath, name);
4774
+ const abs = import_node_path28.default.join(scanPath, name);
4736
4775
  if (await exists(abs)) {
4737
4776
  composePath = abs;
4738
4777
  break;
@@ -4745,13 +4784,13 @@ async function addComposeInfra(graph, scanPath, services) {
4745
4784
  } catch (err) {
4746
4785
  recordExtractionError(
4747
4786
  "infra docker-compose",
4748
- import_node_path27.default.relative(scanPath, composePath),
4787
+ import_node_path28.default.relative(scanPath, composePath),
4749
4788
  err
4750
4789
  );
4751
4790
  return { nodesAdded, edgesAdded };
4752
4791
  }
4753
4792
  if (!compose?.services) return { nodesAdded, edgesAdded };
4754
- const evidenceFile = import_node_path27.default.relative(scanPath, composePath).split(import_node_path27.default.sep).join("/");
4793
+ const evidenceFile = import_node_path28.default.relative(scanPath, composePath).split(import_node_path28.default.sep).join("/");
4755
4794
  const composeNameToNodeId = /* @__PURE__ */ new Map();
4756
4795
  for (const [composeName, svc] of Object.entries(compose.services)) {
4757
4796
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -4793,8 +4832,8 @@ async function addComposeInfra(graph, scanPath, services) {
4793
4832
 
4794
4833
  // src/extract/infra/dockerfile.ts
4795
4834
  init_cjs_shims();
4796
- var import_node_path28 = __toESM(require("path"), 1);
4797
- var import_node_fs15 = require("fs");
4835
+ var import_node_path29 = __toESM(require("path"), 1);
4836
+ var import_node_fs16 = require("fs");
4798
4837
  var import_types19 = require("@neat.is/types");
4799
4838
  function runtimeImage(content) {
4800
4839
  const lines = content.split("\n");
@@ -4814,15 +4853,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
4814
4853
  let nodesAdded = 0;
4815
4854
  let edgesAdded = 0;
4816
4855
  for (const service of services) {
4817
- const dockerfilePath = import_node_path28.default.join(service.dir, "Dockerfile");
4856
+ const dockerfilePath = import_node_path29.default.join(service.dir, "Dockerfile");
4818
4857
  if (!await exists(dockerfilePath)) continue;
4819
4858
  let content;
4820
4859
  try {
4821
- content = await import_node_fs15.promises.readFile(dockerfilePath, "utf8");
4860
+ content = await import_node_fs16.promises.readFile(dockerfilePath, "utf8");
4822
4861
  } catch (err) {
4823
4862
  recordExtractionError(
4824
4863
  "infra dockerfile",
4825
- import_node_path28.default.relative(scanPath, dockerfilePath),
4864
+ import_node_path29.default.relative(scanPath, dockerfilePath),
4826
4865
  err
4827
4866
  );
4828
4867
  continue;
@@ -4834,7 +4873,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
4834
4873
  graph.addNode(node.id, node);
4835
4874
  nodesAdded++;
4836
4875
  }
4837
- const relDockerfile = toPosix2(import_node_path28.default.relative(service.dir, dockerfilePath));
4876
+ const relDockerfile = toPosix2(import_node_path29.default.relative(service.dir, dockerfilePath));
4838
4877
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
4839
4878
  graph,
4840
4879
  service.pkg.name,
@@ -4853,7 +4892,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
4853
4892
  provenance: import_types19.Provenance.EXTRACTED,
4854
4893
  confidence: (0, import_types19.confidenceForExtracted)("structural"),
4855
4894
  evidence: {
4856
- file: toPosix2(import_node_path28.default.relative(scanPath, dockerfilePath))
4895
+ file: toPosix2(import_node_path29.default.relative(scanPath, dockerfilePath))
4857
4896
  }
4858
4897
  };
4859
4898
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -4865,21 +4904,21 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
4865
4904
 
4866
4905
  // src/extract/infra/terraform.ts
4867
4906
  init_cjs_shims();
4868
- var import_node_fs16 = require("fs");
4869
- var import_node_path29 = __toESM(require("path"), 1);
4907
+ var import_node_fs17 = require("fs");
4908
+ var import_node_path30 = __toESM(require("path"), 1);
4870
4909
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
4871
4910
  async function walkTfFiles(start, depth = 0, max = 5) {
4872
4911
  if (depth > max) return [];
4873
4912
  const out = [];
4874
- const entries = await import_node_fs16.promises.readdir(start, { withFileTypes: true }).catch(() => []);
4913
+ const entries = await import_node_fs17.promises.readdir(start, { withFileTypes: true }).catch(() => []);
4875
4914
  for (const entry2 of entries) {
4876
4915
  if (entry2.isDirectory()) {
4877
4916
  if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
4878
- const child = import_node_path29.default.join(start, entry2.name);
4917
+ const child = import_node_path30.default.join(start, entry2.name);
4879
4918
  if (await isPythonVenvDir(child)) continue;
4880
4919
  out.push(...await walkTfFiles(child, depth + 1, max));
4881
4920
  } else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
4882
- out.push(import_node_path29.default.join(start, entry2.name));
4921
+ out.push(import_node_path30.default.join(start, entry2.name));
4883
4922
  }
4884
4923
  }
4885
4924
  return out;
@@ -4888,7 +4927,7 @@ async function addTerraformResources(graph, scanPath) {
4888
4927
  let nodesAdded = 0;
4889
4928
  const files = await walkTfFiles(scanPath);
4890
4929
  for (const file of files) {
4891
- const content = await import_node_fs16.promises.readFile(file, "utf8");
4930
+ const content = await import_node_fs17.promises.readFile(file, "utf8");
4892
4931
  RESOURCE_RE.lastIndex = 0;
4893
4932
  let m;
4894
4933
  while ((m = RESOURCE_RE.exec(content)) !== null) {
@@ -4906,8 +4945,8 @@ async function addTerraformResources(graph, scanPath) {
4906
4945
 
4907
4946
  // src/extract/infra/k8s.ts
4908
4947
  init_cjs_shims();
4909
- var import_node_fs17 = require("fs");
4910
- var import_node_path30 = __toESM(require("path"), 1);
4948
+ var import_node_fs18 = require("fs");
4949
+ var import_node_path31 = __toESM(require("path"), 1);
4911
4950
  var import_yaml3 = require("yaml");
4912
4951
  var K8S_KIND_TO_INFRA_KIND = {
4913
4952
  Service: "k8s-service",
@@ -4921,15 +4960,15 @@ var K8S_KIND_TO_INFRA_KIND = {
4921
4960
  async function walkYamlFiles2(start, depth = 0, max = 5) {
4922
4961
  if (depth > max) return [];
4923
4962
  const out = [];
4924
- const entries = await import_node_fs17.promises.readdir(start, { withFileTypes: true }).catch(() => []);
4963
+ const entries = await import_node_fs18.promises.readdir(start, { withFileTypes: true }).catch(() => []);
4925
4964
  for (const entry2 of entries) {
4926
4965
  if (entry2.isDirectory()) {
4927
4966
  if (IGNORED_DIRS.has(entry2.name)) continue;
4928
- const child = import_node_path30.default.join(start, entry2.name);
4967
+ const child = import_node_path31.default.join(start, entry2.name);
4929
4968
  if (await isPythonVenvDir(child)) continue;
4930
4969
  out.push(...await walkYamlFiles2(child, depth + 1, max));
4931
- } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path30.default.extname(entry2.name))) {
4932
- out.push(import_node_path30.default.join(start, entry2.name));
4970
+ } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path31.default.extname(entry2.name))) {
4971
+ out.push(import_node_path31.default.join(start, entry2.name));
4933
4972
  }
4934
4973
  }
4935
4974
  return out;
@@ -4938,7 +4977,7 @@ async function addK8sResources(graph, scanPath) {
4938
4977
  let nodesAdded = 0;
4939
4978
  const files = await walkYamlFiles2(scanPath);
4940
4979
  for (const file of files) {
4941
- const content = await import_node_fs17.promises.readFile(file, "utf8");
4980
+ const content = await import_node_fs18.promises.readFile(file, "utf8");
4942
4981
  let docs;
4943
4982
  try {
4944
4983
  docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
@@ -4973,12 +5012,12 @@ async function addInfra(graph, scanPath, services) {
4973
5012
  }
4974
5013
 
4975
5014
  // src/extract/index.ts
4976
- var import_node_path32 = __toESM(require("path"), 1);
5015
+ var import_node_path33 = __toESM(require("path"), 1);
4977
5016
 
4978
5017
  // src/extract/retire.ts
4979
5018
  init_cjs_shims();
4980
- var import_node_fs18 = require("fs");
4981
- var import_node_path31 = __toESM(require("path"), 1);
5019
+ var import_node_fs19 = require("fs");
5020
+ var import_node_path32 = __toESM(require("path"), 1);
4982
5021
  var import_types20 = require("@neat.is/types");
4983
5022
  function dropOrphanedFileNodes(graph) {
4984
5023
  const orphans = [];
@@ -5012,11 +5051,11 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
5012
5051
  if (edge.provenance !== import_types20.Provenance.EXTRACTED) return;
5013
5052
  const evidenceFile = edge.evidence?.file;
5014
5053
  if (!evidenceFile) return;
5015
- if (import_node_path31.default.isAbsolute(evidenceFile)) {
5016
- if (!(0, import_node_fs18.existsSync)(evidenceFile)) toDrop.push(id);
5054
+ if (import_node_path32.default.isAbsolute(evidenceFile)) {
5055
+ if (!(0, import_node_fs19.existsSync)(evidenceFile)) toDrop.push(id);
5017
5056
  return;
5018
5057
  }
5019
- const found = bases.some((base) => (0, import_node_fs18.existsSync)(import_node_path31.default.join(base, evidenceFile)));
5058
+ const found = bases.some((base) => (0, import_node_fs19.existsSync)(import_node_path32.default.join(base, evidenceFile)));
5020
5059
  if (!found) toDrop.push(id);
5021
5060
  });
5022
5061
  for (const id of toDrop) graph.dropEdge(id);
@@ -5056,7 +5095,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
5056
5095
  }
5057
5096
  const droppedEntries = drainDroppedExtracted();
5058
5097
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
5059
- const rejectedPath = import_node_path32.default.join(import_node_path32.default.dirname(opts.errorsPath), "rejected.ndjson");
5098
+ const rejectedPath = import_node_path33.default.join(import_node_path33.default.dirname(opts.errorsPath), "rejected.ndjson");
5060
5099
  try {
5061
5100
  await writeRejectedExtracted(droppedEntries, rejectedPath);
5062
5101
  } catch (err) {
@@ -5328,8 +5367,8 @@ function computeDivergences(graph, opts = {}) {
5328
5367
 
5329
5368
  // src/persist.ts
5330
5369
  init_cjs_shims();
5331
- var import_node_fs19 = require("fs");
5332
- var import_node_path33 = __toESM(require("path"), 1);
5370
+ var import_node_fs20 = require("fs");
5371
+ var import_node_path34 = __toESM(require("path"), 1);
5333
5372
  var import_types22 = require("@neat.is/types");
5334
5373
  var SCHEMA_VERSION = 4;
5335
5374
  function migrateV1ToV2(payload) {
@@ -5366,7 +5405,7 @@ function migrateV2ToV3(payload) {
5366
5405
  return { ...payload, schemaVersion: 3 };
5367
5406
  }
5368
5407
  async function ensureDir(filePath) {
5369
- await import_node_fs19.promises.mkdir(import_node_path33.default.dirname(filePath), { recursive: true });
5408
+ await import_node_fs20.promises.mkdir(import_node_path34.default.dirname(filePath), { recursive: true });
5370
5409
  }
5371
5410
  async function saveGraphToDisk(graph, outPath) {
5372
5411
  await ensureDir(outPath);
@@ -5376,13 +5415,13 @@ async function saveGraphToDisk(graph, outPath) {
5376
5415
  graph: graph.export()
5377
5416
  };
5378
5417
  const tmp = `${outPath}.tmp`;
5379
- await import_node_fs19.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
5380
- await import_node_fs19.promises.rename(tmp, outPath);
5418
+ await import_node_fs20.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
5419
+ await import_node_fs20.promises.rename(tmp, outPath);
5381
5420
  }
5382
5421
  async function loadGraphFromDisk(graph, outPath) {
5383
5422
  let raw;
5384
5423
  try {
5385
- raw = await import_node_fs19.promises.readFile(outPath, "utf8");
5424
+ raw = await import_node_fs20.promises.readFile(outPath, "utf8");
5386
5425
  } catch (err) {
5387
5426
  if (err.code === "ENOENT") return;
5388
5427
  throw err;
@@ -5441,8 +5480,8 @@ function startPersistLoop(graph, outPath, intervalMs = 6e4) {
5441
5480
 
5442
5481
  // src/gitignore.ts
5443
5482
  init_cjs_shims();
5444
- var import_node_fs20 = require("fs");
5445
- var import_node_path34 = __toESM(require("path"), 1);
5483
+ var import_node_fs21 = require("fs");
5484
+ var import_node_path35 = __toESM(require("path"), 1);
5446
5485
  var NEAT_OUT_LINE = "neat-out/";
5447
5486
  var NEAT_HEADER = "# NEAT \u2014 machine-local snapshots and events";
5448
5487
  function isNeatOutLine(line) {
@@ -5450,15 +5489,15 @@ function isNeatOutLine(line) {
5450
5489
  return trimmed === "neat-out/" || trimmed === "neat-out";
5451
5490
  }
5452
5491
  async function ensureNeatOutIgnored(projectDir) {
5453
- const file = import_node_path34.default.join(projectDir, ".gitignore");
5492
+ const file = import_node_path35.default.join(projectDir, ".gitignore");
5454
5493
  let existing = null;
5455
5494
  try {
5456
- existing = await import_node_fs20.promises.readFile(file, "utf8");
5495
+ existing = await import_node_fs21.promises.readFile(file, "utf8");
5457
5496
  } catch (err) {
5458
5497
  if (err.code !== "ENOENT") throw err;
5459
5498
  }
5460
5499
  if (existing === null) {
5461
- await import_node_fs20.promises.writeFile(file, `${NEAT_HEADER}
5500
+ await import_node_fs21.promises.writeFile(file, `${NEAT_HEADER}
5462
5501
  ${NEAT_OUT_LINE}
5463
5502
  `, "utf8");
5464
5503
  return { action: "created", file };
@@ -5471,7 +5510,7 @@ ${NEAT_OUT_LINE}
5471
5510
  ${NEAT_HEADER}
5472
5511
  ${NEAT_OUT_LINE}
5473
5512
  `;
5474
- await import_node_fs20.promises.writeFile(file, existing + appended, "utf8");
5513
+ await import_node_fs21.promises.writeFile(file, existing + appended, "utf8");
5475
5514
  return { action: "added", file };
5476
5515
  }
5477
5516
 
@@ -5573,8 +5612,8 @@ function formatIncompat(inc) {
5573
5612
 
5574
5613
  // src/watch.ts
5575
5614
  init_cjs_shims();
5576
- var import_node_fs26 = __toESM(require("fs"), 1);
5577
- var import_node_path42 = __toESM(require("path"), 1);
5615
+ var import_node_fs27 = __toESM(require("fs"), 1);
5616
+ var import_node_path43 = __toESM(require("path"), 1);
5578
5617
  var import_chokidar = __toESM(require("chokidar"), 1);
5579
5618
 
5580
5619
  // src/api.ts
@@ -5585,15 +5624,15 @@ var import_types25 = require("@neat.is/types");
5585
5624
 
5586
5625
  // src/extend/index.ts
5587
5626
  init_cjs_shims();
5588
- var import_node_fs22 = require("fs");
5589
- var import_node_path36 = __toESM(require("path"), 1);
5627
+ var import_node_fs23 = require("fs");
5628
+ var import_node_path37 = __toESM(require("path"), 1);
5590
5629
  var import_node_os2 = __toESM(require("os"), 1);
5591
5630
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
5592
5631
 
5593
5632
  // src/installers/package-manager.ts
5594
5633
  init_cjs_shims();
5595
- var import_node_fs21 = require("fs");
5596
- var import_node_path35 = __toESM(require("path"), 1);
5634
+ var import_node_fs22 = require("fs");
5635
+ var import_node_path36 = __toESM(require("path"), 1);
5597
5636
  var import_node_child_process = require("child_process");
5598
5637
  var LOCKFILE_PRIORITY = [
5599
5638
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -5608,29 +5647,29 @@ var LOCKFILE_PRIORITY = [
5608
5647
  var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
5609
5648
  async function exists2(p) {
5610
5649
  try {
5611
- await import_node_fs21.promises.access(p);
5650
+ await import_node_fs22.promises.access(p);
5612
5651
  return true;
5613
5652
  } catch {
5614
5653
  return false;
5615
5654
  }
5616
5655
  }
5617
5656
  async function detectPackageManager(serviceDir) {
5618
- let dir = import_node_path35.default.resolve(serviceDir);
5657
+ let dir = import_node_path36.default.resolve(serviceDir);
5619
5658
  const stops = /* @__PURE__ */ new Set();
5620
5659
  for (let i = 0; i < 64; i++) {
5621
5660
  if (stops.has(dir)) break;
5622
5661
  stops.add(dir);
5623
5662
  for (const candidate of LOCKFILE_PRIORITY) {
5624
- const lockPath = import_node_path35.default.join(dir, candidate.lockfile);
5663
+ const lockPath = import_node_path36.default.join(dir, candidate.lockfile);
5625
5664
  if (await exists2(lockPath)) {
5626
5665
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
5627
5666
  }
5628
5667
  }
5629
- const parent = import_node_path35.default.dirname(dir);
5668
+ const parent = import_node_path36.default.dirname(dir);
5630
5669
  if (parent === dir) break;
5631
5670
  dir = parent;
5632
5671
  }
5633
- return { pm: "npm", cwd: import_node_path35.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
5672
+ return { pm: "npm", cwd: import_node_path36.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
5634
5673
  }
5635
5674
  async function runPackageManagerInstall(cmd) {
5636
5675
  return new Promise((resolve) => {
@@ -5672,30 +5711,30 @@ ${err.message}`
5672
5711
  // src/extend/index.ts
5673
5712
  async function fileExists2(p) {
5674
5713
  try {
5675
- await import_node_fs22.promises.access(p);
5714
+ await import_node_fs23.promises.access(p);
5676
5715
  return true;
5677
5716
  } catch {
5678
5717
  return false;
5679
5718
  }
5680
5719
  }
5681
5720
  async function readPackageJson(scanPath) {
5682
- const pkgPath = import_node_path36.default.join(scanPath, "package.json");
5683
- const raw = await import_node_fs22.promises.readFile(pkgPath, "utf8");
5721
+ const pkgPath = import_node_path37.default.join(scanPath, "package.json");
5722
+ const raw = await import_node_fs23.promises.readFile(pkgPath, "utf8");
5684
5723
  return JSON.parse(raw);
5685
5724
  }
5686
5725
  async function findHookFiles(scanPath) {
5687
- const entries = await import_node_fs22.promises.readdir(scanPath);
5726
+ const entries = await import_node_fs23.promises.readdir(scanPath);
5688
5727
  return entries.filter(
5689
5728
  (e) => (e.startsWith("instrumentation") || e.startsWith("otel-init")) && /\.(ts|js)$/.test(e)
5690
5729
  ).sort();
5691
5730
  }
5692
5731
  function extendLogPath() {
5693
- return process.env.NEAT_EXTEND_LOG ?? import_node_path36.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
5732
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path37.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
5694
5733
  }
5695
5734
  async function appendExtendLog(entry2) {
5696
5735
  const logPath = extendLogPath();
5697
- await import_node_fs22.promises.mkdir(import_node_path36.default.dirname(logPath), { recursive: true });
5698
- await import_node_fs22.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
5736
+ await import_node_fs23.promises.mkdir(import_node_path37.default.dirname(logPath), { recursive: true });
5737
+ await import_node_fs23.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
5699
5738
  }
5700
5739
  function splicedContent(fileContent, snippet2) {
5701
5740
  if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
@@ -5753,7 +5792,7 @@ function lookupInstrumentation(library, installedVersion) {
5753
5792
  }
5754
5793
  async function describeProjectInstrumentation(ctx) {
5755
5794
  const hookFiles = await findHookFiles(ctx.scanPath);
5756
- const envNeat = await fileExists2(import_node_path36.default.join(ctx.scanPath, ".env.neat"));
5795
+ const envNeat = await fileExists2(import_node_path37.default.join(ctx.scanPath, ".env.neat"));
5757
5796
  const registryInstrPackages = new Set(
5758
5797
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
5759
5798
  );
@@ -5775,31 +5814,31 @@ async function applyExtension(ctx, args, options) {
5775
5814
  );
5776
5815
  }
5777
5816
  for (const file of hookFiles) {
5778
- const content = await import_node_fs22.promises.readFile(import_node_path36.default.join(ctx.scanPath, file), "utf8");
5817
+ const content = await import_node_fs23.promises.readFile(import_node_path37.default.join(ctx.scanPath, file), "utf8");
5779
5818
  if (content.includes(args.registration_snippet)) {
5780
5819
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
5781
5820
  }
5782
5821
  }
5783
5822
  const primaryFile = hookFiles[0];
5784
- const primaryPath = import_node_path36.default.join(ctx.scanPath, primaryFile);
5823
+ const primaryPath = import_node_path37.default.join(ctx.scanPath, primaryFile);
5785
5824
  const filesTouched = [];
5786
5825
  const depsAdded = [];
5787
- const pkgPath = import_node_path36.default.join(ctx.scanPath, "package.json");
5826
+ const pkgPath = import_node_path37.default.join(ctx.scanPath, "package.json");
5788
5827
  const pkg = await readPackageJson(ctx.scanPath);
5789
5828
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
5790
5829
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
5791
- await import_node_fs22.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
5830
+ await import_node_fs23.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
5792
5831
  filesTouched.push("package.json");
5793
5832
  depsAdded.push(`${args.instrumentation_package}@${args.version}`);
5794
5833
  }
5795
- const hookContent = await import_node_fs22.promises.readFile(primaryPath, "utf8");
5834
+ const hookContent = await import_node_fs23.promises.readFile(primaryPath, "utf8");
5796
5835
  const patched = splicedContent(hookContent, args.registration_snippet);
5797
5836
  if (!patched) {
5798
5837
  throw new Error(
5799
5838
  `Could not find instrumentation insertion point in ${primaryFile}. Expected __INSTRUMENTATION_BLOCK__, instrumentations.push(, or new NodeSDK(.`
5800
5839
  );
5801
5840
  }
5802
- await import_node_fs22.promises.writeFile(primaryPath, patched, "utf8");
5841
+ await import_node_fs23.promises.writeFile(primaryPath, patched, "utf8");
5803
5842
  filesTouched.push(primaryFile);
5804
5843
  const cmd = await detectPackageManager(ctx.scanPath);
5805
5844
  const installer = options?.runInstall ?? runPackageManagerInstall;
@@ -5830,7 +5869,7 @@ async function dryRunExtension(ctx, args) {
5830
5869
  };
5831
5870
  }
5832
5871
  for (const file of hookFiles) {
5833
- const content = await import_node_fs22.promises.readFile(import_node_path36.default.join(ctx.scanPath, file), "utf8");
5872
+ const content = await import_node_fs23.promises.readFile(import_node_path37.default.join(ctx.scanPath, file), "utf8");
5834
5873
  if (content.includes(args.registration_snippet)) {
5835
5874
  return {
5836
5875
  library: args.library,
@@ -5852,7 +5891,7 @@ async function dryRunExtension(ctx, args) {
5852
5891
  depsToAdd.push(`${args.instrumentation_package}@${args.version}`);
5853
5892
  filesTouched.push("package.json");
5854
5893
  }
5855
- const hookContent = await import_node_fs22.promises.readFile(import_node_path36.default.join(ctx.scanPath, primaryFile), "utf8");
5894
+ const hookContent = await import_node_fs23.promises.readFile(import_node_path37.default.join(ctx.scanPath, primaryFile), "utf8");
5856
5895
  const patched = splicedContent(hookContent, args.registration_snippet);
5857
5896
  if (patched) {
5858
5897
  filesTouched.push(primaryFile);
@@ -5867,28 +5906,28 @@ async function rollbackExtension(ctx, args) {
5867
5906
  if (!await fileExists2(logPath)) {
5868
5907
  return { undone: false, message: "no apply found for library" };
5869
5908
  }
5870
- const raw = await import_node_fs22.promises.readFile(logPath, "utf8");
5909
+ const raw = await import_node_fs23.promises.readFile(logPath, "utf8");
5871
5910
  const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
5872
5911
  const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
5873
5912
  if (!match) {
5874
5913
  return { undone: false, message: "no apply found for library" };
5875
5914
  }
5876
- const pkgPath = import_node_path36.default.join(ctx.scanPath, "package.json");
5915
+ const pkgPath = import_node_path37.default.join(ctx.scanPath, "package.json");
5877
5916
  if (await fileExists2(pkgPath)) {
5878
5917
  const pkg = await readPackageJson(ctx.scanPath);
5879
5918
  if (pkg.dependencies?.[match.instrumentation_package]) {
5880
5919
  const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
5881
5920
  pkg.dependencies = rest;
5882
- await import_node_fs22.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
5921
+ await import_node_fs23.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
5883
5922
  }
5884
5923
  }
5885
5924
  const hookFiles = await findHookFiles(ctx.scanPath);
5886
5925
  for (const file of hookFiles) {
5887
- const filePath = import_node_path36.default.join(ctx.scanPath, file);
5888
- const content = await import_node_fs22.promises.readFile(filePath, "utf8");
5926
+ const filePath = import_node_path37.default.join(ctx.scanPath, file);
5927
+ const content = await import_node_fs23.promises.readFile(filePath, "utf8");
5889
5928
  if (content.includes(match.registration_snippet)) {
5890
5929
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
5891
- await import_node_fs22.promises.writeFile(filePath, filtered, "utf8");
5930
+ await import_node_fs23.promises.writeFile(filePath, filtered, "utf8");
5892
5931
  break;
5893
5932
  }
5894
5933
  }
@@ -5900,7 +5939,7 @@ async function rollbackExtension(ctx, args) {
5900
5939
 
5901
5940
  // src/diff.ts
5902
5941
  init_cjs_shims();
5903
- var import_node_fs23 = require("fs");
5942
+ var import_node_fs24 = require("fs");
5904
5943
  async function loadSnapshotForDiff(target) {
5905
5944
  if (/^https?:\/\//i.test(target)) {
5906
5945
  const res = await fetch(target);
@@ -5909,7 +5948,7 @@ async function loadSnapshotForDiff(target) {
5909
5948
  }
5910
5949
  return await res.json();
5911
5950
  }
5912
- const raw = await import_node_fs23.promises.readFile(target, "utf8");
5951
+ const raw = await import_node_fs24.promises.readFile(target, "utf8");
5913
5952
  return JSON.parse(raw);
5914
5953
  }
5915
5954
  function indexEntries(entries) {
@@ -5977,23 +6016,23 @@ function canonicalJson(value) {
5977
6016
 
5978
6017
  // src/projects.ts
5979
6018
  init_cjs_shims();
5980
- var import_node_path37 = __toESM(require("path"), 1);
6019
+ var import_node_path38 = __toESM(require("path"), 1);
5981
6020
  function pathsForProject(project, baseDir) {
5982
6021
  if (project === DEFAULT_PROJECT) {
5983
6022
  return {
5984
- snapshotPath: import_node_path37.default.join(baseDir, "graph.json"),
5985
- errorsPath: import_node_path37.default.join(baseDir, "errors.ndjson"),
5986
- staleEventsPath: import_node_path37.default.join(baseDir, "stale-events.ndjson"),
5987
- embeddingsCachePath: import_node_path37.default.join(baseDir, "embeddings.json"),
5988
- policyViolationsPath: import_node_path37.default.join(baseDir, "policy-violations.ndjson")
6023
+ snapshotPath: import_node_path38.default.join(baseDir, "graph.json"),
6024
+ errorsPath: import_node_path38.default.join(baseDir, "errors.ndjson"),
6025
+ staleEventsPath: import_node_path38.default.join(baseDir, "stale-events.ndjson"),
6026
+ embeddingsCachePath: import_node_path38.default.join(baseDir, "embeddings.json"),
6027
+ policyViolationsPath: import_node_path38.default.join(baseDir, "policy-violations.ndjson")
5989
6028
  };
5990
6029
  }
5991
6030
  return {
5992
- snapshotPath: import_node_path37.default.join(baseDir, `${project}.json`),
5993
- errorsPath: import_node_path37.default.join(baseDir, `errors.${project}.ndjson`),
5994
- staleEventsPath: import_node_path37.default.join(baseDir, `stale-events.${project}.ndjson`),
5995
- embeddingsCachePath: import_node_path37.default.join(baseDir, `embeddings.${project}.json`),
5996
- policyViolationsPath: import_node_path37.default.join(baseDir, `policy-violations.${project}.ndjson`)
6031
+ snapshotPath: import_node_path38.default.join(baseDir, `${project}.json`),
6032
+ errorsPath: import_node_path38.default.join(baseDir, `errors.${project}.ndjson`),
6033
+ staleEventsPath: import_node_path38.default.join(baseDir, `stale-events.${project}.ndjson`),
6034
+ embeddingsCachePath: import_node_path38.default.join(baseDir, `embeddings.${project}.json`),
6035
+ policyViolationsPath: import_node_path38.default.join(baseDir, `policy-violations.${project}.ndjson`)
5997
6036
  };
5998
6037
  }
5999
6038
  var Projects = class {
@@ -6029,25 +6068,25 @@ var Projects = class {
6029
6068
 
6030
6069
  // src/registry.ts
6031
6070
  init_cjs_shims();
6032
- var import_node_fs24 = require("fs");
6071
+ var import_node_fs25 = require("fs");
6033
6072
  var import_node_os3 = __toESM(require("os"), 1);
6034
- var import_node_path38 = __toESM(require("path"), 1);
6073
+ var import_node_path39 = __toESM(require("path"), 1);
6035
6074
  var import_types24 = require("@neat.is/types");
6036
6075
  var LOCK_TIMEOUT_MS = 5e3;
6037
6076
  var LOCK_RETRY_MS = 50;
6038
6077
  function neatHome() {
6039
6078
  const override = process.env.NEAT_HOME;
6040
- if (override && override.length > 0) return import_node_path38.default.resolve(override);
6041
- return import_node_path38.default.join(import_node_os3.default.homedir(), ".neat");
6079
+ if (override && override.length > 0) return import_node_path39.default.resolve(override);
6080
+ return import_node_path39.default.join(import_node_os3.default.homedir(), ".neat");
6042
6081
  }
6043
6082
  function registryPath() {
6044
- return import_node_path38.default.join(neatHome(), "projects.json");
6083
+ return import_node_path39.default.join(neatHome(), "projects.json");
6045
6084
  }
6046
6085
  function registryLockPath() {
6047
- return import_node_path38.default.join(neatHome(), "projects.json.lock");
6086
+ return import_node_path39.default.join(neatHome(), "projects.json.lock");
6048
6087
  }
6049
6088
  function daemonPidPath() {
6050
- return import_node_path38.default.join(neatHome(), "neatd.pid");
6089
+ return import_node_path39.default.join(neatHome(), "neatd.pid");
6051
6090
  }
6052
6091
  function isPidAliveDefault(pid) {
6053
6092
  try {
@@ -6059,7 +6098,7 @@ function isPidAliveDefault(pid) {
6059
6098
  }
6060
6099
  async function readPidFile(file) {
6061
6100
  try {
6062
- const raw = await import_node_fs24.promises.readFile(file, "utf8");
6101
+ const raw = await import_node_fs25.promises.readFile(file, "utf8");
6063
6102
  const pid = Number.parseInt(raw.trim(), 10);
6064
6103
  return Number.isInteger(pid) && pid > 0 ? pid : void 0;
6065
6104
  } catch {
@@ -6107,32 +6146,32 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
6107
6146
  }
6108
6147
  }
6109
6148
  async function normalizeProjectPath(input) {
6110
- const resolved = import_node_path38.default.resolve(input);
6149
+ const resolved = import_node_path39.default.resolve(input);
6111
6150
  try {
6112
- return await import_node_fs24.promises.realpath(resolved);
6151
+ return await import_node_fs25.promises.realpath(resolved);
6113
6152
  } catch {
6114
6153
  return resolved;
6115
6154
  }
6116
6155
  }
6117
6156
  async function writeAtomically(target, contents) {
6118
- await import_node_fs24.promises.mkdir(import_node_path38.default.dirname(target), { recursive: true });
6157
+ await import_node_fs25.promises.mkdir(import_node_path39.default.dirname(target), { recursive: true });
6119
6158
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
6120
- const fd = await import_node_fs24.promises.open(tmp, "w");
6159
+ const fd = await import_node_fs25.promises.open(tmp, "w");
6121
6160
  try {
6122
6161
  await fd.writeFile(contents, "utf8");
6123
6162
  await fd.sync();
6124
6163
  } finally {
6125
6164
  await fd.close();
6126
6165
  }
6127
- await import_node_fs24.promises.rename(tmp, target);
6166
+ await import_node_fs25.promises.rename(tmp, target);
6128
6167
  }
6129
6168
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
6130
6169
  const deadline = Date.now() + timeoutMs;
6131
- await import_node_fs24.promises.mkdir(import_node_path38.default.dirname(lockPath), { recursive: true });
6170
+ await import_node_fs25.promises.mkdir(import_node_path39.default.dirname(lockPath), { recursive: true });
6132
6171
  let probedHolder = false;
6133
6172
  while (true) {
6134
6173
  try {
6135
- const fd = await import_node_fs24.promises.open(lockPath, "wx");
6174
+ const fd = await import_node_fs25.promises.open(lockPath, "wx");
6136
6175
  try {
6137
6176
  await fd.writeFile(`${process.pid}
6138
6177
  `, "utf8");
@@ -6157,7 +6196,7 @@ async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaul
6157
6196
  }
6158
6197
  }
6159
6198
  async function releaseLock(lockPath) {
6160
- await import_node_fs24.promises.unlink(lockPath).catch(() => {
6199
+ await import_node_fs25.promises.unlink(lockPath).catch(() => {
6161
6200
  });
6162
6201
  }
6163
6202
  async function withLock(fn) {
@@ -6173,7 +6212,7 @@ async function readRegistry() {
6173
6212
  const file = registryPath();
6174
6213
  let raw;
6175
6214
  try {
6176
- raw = await import_node_fs24.promises.readFile(file, "utf8");
6215
+ raw = await import_node_fs25.promises.readFile(file, "utf8");
6177
6216
  } catch (err) {
6178
6217
  if (err.code === "ENOENT") {
6179
6218
  return { version: 1, projects: [] };
@@ -6924,8 +6963,8 @@ init_otel_grpc();
6924
6963
 
6925
6964
  // src/search.ts
6926
6965
  init_cjs_shims();
6927
- var import_node_fs25 = require("fs");
6928
- var import_node_path41 = __toESM(require("path"), 1);
6966
+ var import_node_fs26 = require("fs");
6967
+ var import_node_path42 = __toESM(require("path"), 1);
6929
6968
  var import_node_crypto3 = require("crypto");
6930
6969
  var DEFAULT_LIMIT = 10;
6931
6970
  var NOMIC_DIM = 768;
@@ -7055,7 +7094,7 @@ async function pickEmbedder() {
7055
7094
  }
7056
7095
  async function readCache(cachePath) {
7057
7096
  try {
7058
- const raw = await import_node_fs25.promises.readFile(cachePath, "utf8");
7097
+ const raw = await import_node_fs26.promises.readFile(cachePath, "utf8");
7059
7098
  const parsed = JSON.parse(raw);
7060
7099
  if (parsed.version !== 1) return null;
7061
7100
  return parsed;
@@ -7064,8 +7103,8 @@ async function readCache(cachePath) {
7064
7103
  }
7065
7104
  }
7066
7105
  async function writeCache(cachePath, cache) {
7067
- await import_node_fs25.promises.mkdir(import_node_path41.default.dirname(cachePath), { recursive: true });
7068
- await import_node_fs25.promises.writeFile(cachePath, JSON.stringify(cache));
7106
+ await import_node_fs26.promises.mkdir(import_node_path42.default.dirname(cachePath), { recursive: true });
7107
+ await import_node_fs26.promises.writeFile(cachePath, JSON.stringify(cache));
7069
7108
  }
7070
7109
  var VectorIndex = class {
7071
7110
  constructor(embedder, cachePath) {
@@ -7221,8 +7260,8 @@ var ALL_PHASES = [
7221
7260
  ];
7222
7261
  function classifyChange(relPath) {
7223
7262
  const phases = /* @__PURE__ */ new Set();
7224
- const base = import_node_path42.default.basename(relPath).toLowerCase();
7225
- const segments = relPath.split(import_node_path42.default.sep).map((s) => s.toLowerCase());
7263
+ const base = import_node_path43.default.basename(relPath).toLowerCase();
7264
+ const segments = relPath.split(import_node_path43.default.sep).map((s) => s.toLowerCase());
7226
7265
  if (base === "package.json" || base === "requirements.txt" || base === "pyproject.toml" || base === "setup.py") {
7227
7266
  phases.add("services");
7228
7267
  phases.add("aliases");
@@ -7336,16 +7375,16 @@ function countWatchableDirs(scanPath, limit) {
7336
7375
  if (count >= limit) return;
7337
7376
  let entries;
7338
7377
  try {
7339
- entries = import_node_fs26.default.readdirSync(dir, { withFileTypes: true });
7378
+ entries = import_node_fs27.default.readdirSync(dir, { withFileTypes: true });
7340
7379
  } catch {
7341
7380
  return;
7342
7381
  }
7343
7382
  for (const e of entries) {
7344
7383
  if (count >= limit) return;
7345
7384
  if (!e.isDirectory()) continue;
7346
- if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path42.default.join(dir, e.name) + import_node_path42.default.sep))) continue;
7385
+ if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path43.default.join(dir, e.name) + import_node_path43.default.sep))) continue;
7347
7386
  count++;
7348
- if (depth < 2) visit(import_node_path42.default.join(dir, e.name), depth + 1);
7387
+ if (depth < 2) visit(import_node_path43.default.join(dir, e.name), depth + 1);
7349
7388
  }
7350
7389
  };
7351
7390
  visit(scanPath, 0);
@@ -7363,8 +7402,8 @@ async function startWatch(graph, opts) {
7363
7402
  const projectName = opts.project ?? DEFAULT_PROJECT;
7364
7403
  await loadGraphFromDisk(graph, opts.outPath);
7365
7404
  const detachEventBus = attachGraphToEventBus(graph, { project: projectName });
7366
- const policyFilePath = import_node_path42.default.join(opts.scanPath, "policy.json");
7367
- const policyViolationsPath = import_node_path42.default.join(import_node_path42.default.dirname(opts.outPath), "policy-violations.ndjson");
7405
+ const policyFilePath = import_node_path43.default.join(opts.scanPath, "policy.json");
7406
+ const policyViolationsPath = import_node_path43.default.join(import_node_path43.default.dirname(opts.outPath), "policy-violations.ndjson");
7368
7407
  let policies = [];
7369
7408
  try {
7370
7409
  policies = await loadPolicyFile(policyFilePath);
@@ -7415,7 +7454,7 @@ async function startWatch(graph, opts) {
7415
7454
  assertBindAuthority(host, auth.authToken);
7416
7455
  const port = opts.port ?? 8080;
7417
7456
  const otelPort = opts.otelPort ?? 4318;
7418
- const cachePath = opts.embeddingsCachePath ?? import_node_path42.default.join(import_node_path42.default.dirname(opts.outPath), "embeddings.json");
7457
+ const cachePath = opts.embeddingsCachePath ?? import_node_path43.default.join(import_node_path43.default.dirname(opts.outPath), "embeddings.json");
7419
7458
  let searchIndex;
7420
7459
  try {
7421
7460
  searchIndex = await buildSearchIndex(graph, { cachePath });
@@ -7433,7 +7472,7 @@ async function startWatch(graph, opts) {
7433
7472
  // Paths are derived from the explicit options the watch caller passes
7434
7473
  // — pathsForProject is only used to fill in the embeddings/snapshot
7435
7474
  // fields so the registry shape is complete.
7436
- ...pathsForProject(projectName, import_node_path42.default.dirname(opts.outPath)),
7475
+ ...pathsForProject(projectName, import_node_path43.default.dirname(opts.outPath)),
7437
7476
  snapshotPath: opts.outPath,
7438
7477
  errorsPath: opts.errorsPath,
7439
7478
  staleEventsPath: opts.staleEventsPath
@@ -7520,9 +7559,9 @@ async function startWatch(graph, opts) {
7520
7559
  };
7521
7560
  const onPath = (absPath) => {
7522
7561
  if (shouldIgnore(absPath)) return;
7523
- const rel = import_node_path42.default.relative(opts.scanPath, absPath);
7562
+ const rel = import_node_path43.default.relative(opts.scanPath, absPath);
7524
7563
  if (!rel || rel.startsWith("..")) return;
7525
- pendingPaths.add(rel.split(import_node_path42.default.sep).join("/"));
7564
+ pendingPaths.add(rel.split(import_node_path43.default.sep).join("/"));
7526
7565
  const phases = classifyChange(rel);
7527
7566
  if (phases.size === 0) {
7528
7567
  for (const p of ALL_PHASES) pending.add(p);
@@ -7576,8 +7615,8 @@ async function startWatch(graph, opts) {
7576
7615
 
7577
7616
  // src/deploy/detect.ts
7578
7617
  init_cjs_shims();
7579
- var import_node_fs27 = require("fs");
7580
- var import_node_path43 = __toESM(require("path"), 1);
7618
+ var import_node_fs28 = require("fs");
7619
+ var import_node_path44 = __toESM(require("path"), 1);
7581
7620
  var import_node_child_process2 = require("child_process");
7582
7621
  var import_node_crypto4 = require("crypto");
7583
7622
  function generateToken() {
@@ -7677,21 +7716,21 @@ async function runDeploy(opts = {}) {
7677
7716
  const token = generateToken();
7678
7717
  switch (substrate) {
7679
7718
  case "docker-compose": {
7680
- const artifactPath = import_node_path43.default.join(cwd, "docker-compose.neat.yml");
7719
+ const artifactPath = import_node_path44.default.join(cwd, "docker-compose.neat.yml");
7681
7720
  const contents = emitDockerCompose(cwd);
7682
- await import_node_fs27.promises.writeFile(artifactPath, contents, "utf8");
7721
+ await import_node_fs28.promises.writeFile(artifactPath, contents, "utf8");
7683
7722
  return {
7684
7723
  substrate,
7685
7724
  artifactPath,
7686
7725
  token,
7687
7726
  contents,
7688
- startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path43.default.basename(artifactPath)} up -d`
7727
+ startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path44.default.basename(artifactPath)} up -d`
7689
7728
  };
7690
7729
  }
7691
7730
  case "systemd": {
7692
- const artifactPath = import_node_path43.default.join(cwd, "neat.service");
7731
+ const artifactPath = import_node_path44.default.join(cwd, "neat.service");
7693
7732
  const contents = emitSystemdUnit(cwd);
7694
- await import_node_fs27.promises.writeFile(artifactPath, contents, "utf8");
7733
+ await import_node_fs28.promises.writeFile(artifactPath, contents, "utf8");
7695
7734
  return {
7696
7735
  substrate,
7697
7736
  artifactPath,
@@ -7724,8 +7763,8 @@ init_cjs_shims();
7724
7763
 
7725
7764
  // src/installers/javascript.ts
7726
7765
  init_cjs_shims();
7727
- var import_node_fs28 = require("fs");
7728
- var import_node_path44 = __toESM(require("path"), 1);
7766
+ var import_node_fs29 = require("fs");
7767
+ var import_node_path45 = __toESM(require("path"), 1);
7729
7768
  var import_semver2 = __toESM(require("semver"), 1);
7730
7769
 
7731
7770
  // src/installers/templates.ts
@@ -8272,15 +8311,15 @@ var OTEL_ENV = {
8272
8311
  value: "http://localhost:4318/projects/<project>/v1/traces"
8273
8312
  };
8274
8313
  function serviceNodeName(pkg, serviceDir) {
8275
- return pkg.name ?? import_node_path44.default.basename(serviceDir);
8314
+ return pkg.name ?? import_node_path45.default.basename(serviceDir);
8276
8315
  }
8277
8316
  function projectToken(pkg, serviceDir, project) {
8278
8317
  if (project && project.length > 0) return project;
8279
- return pkg.name ?? import_node_path44.default.basename(serviceDir);
8318
+ return pkg.name ?? import_node_path45.default.basename(serviceDir);
8280
8319
  }
8281
8320
  async function readJsonFile(p) {
8282
8321
  try {
8283
- const raw = await import_node_fs28.promises.readFile(p, "utf8");
8322
+ const raw = await import_node_fs29.promises.readFile(p, "utf8");
8284
8323
  return JSON.parse(raw);
8285
8324
  } catch {
8286
8325
  return null;
@@ -8289,16 +8328,16 @@ async function readJsonFile(p) {
8289
8328
  async function detectRuntimeKind(pkgRoot, pkg) {
8290
8329
  const deps = allDeps(pkg);
8291
8330
  if ("react-native" in deps || "expo" in deps) return "react-native";
8292
- const appJson = await readJsonFile(import_node_path44.default.join(pkgRoot, "app.json"));
8331
+ const appJson = await readJsonFile(import_node_path45.default.join(pkgRoot, "app.json"));
8293
8332
  if (appJson && typeof appJson === "object" && "expo" in appJson) {
8294
8333
  return "react-native";
8295
8334
  }
8296
- if (await exists3(import_node_path44.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path44.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path44.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
8335
+ if (await exists3(import_node_path45.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path45.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path45.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
8297
8336
  return "browser-bundle";
8298
8337
  }
8299
- if (await exists3(import_node_path44.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
8300
- if (await exists3(import_node_path44.default.join(pkgRoot, "bun.lockb"))) return "bun";
8301
- if (await exists3(import_node_path44.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path44.default.join(pkgRoot, "deno.lock"))) {
8338
+ if (await exists3(import_node_path45.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
8339
+ if (await exists3(import_node_path45.default.join(pkgRoot, "bun.lockb"))) return "bun";
8340
+ if (await exists3(import_node_path45.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path45.default.join(pkgRoot, "deno.lock"))) {
8302
8341
  return "deno";
8303
8342
  }
8304
8343
  const engines = pkg.engines ?? {};
@@ -8307,7 +8346,7 @@ async function detectRuntimeKind(pkgRoot, pkg) {
8307
8346
  }
8308
8347
  async function readPackageJson2(serviceDir) {
8309
8348
  try {
8310
- const raw = await import_node_fs28.promises.readFile(import_node_path44.default.join(serviceDir, "package.json"), "utf8");
8349
+ const raw = await import_node_fs29.promises.readFile(import_node_path45.default.join(serviceDir, "package.json"), "utf8");
8311
8350
  return JSON.parse(raw);
8312
8351
  } catch {
8313
8352
  return null;
@@ -8315,7 +8354,7 @@ async function readPackageJson2(serviceDir) {
8315
8354
  }
8316
8355
  async function exists3(p) {
8317
8356
  try {
8318
- await import_node_fs28.promises.stat(p);
8357
+ await import_node_fs29.promises.stat(p);
8319
8358
  return true;
8320
8359
  } catch {
8321
8360
  return false;
@@ -8323,7 +8362,7 @@ async function exists3(p) {
8323
8362
  }
8324
8363
  async function readFileMaybe(p) {
8325
8364
  try {
8326
- return await import_node_fs28.promises.readFile(p, "utf8");
8365
+ return await import_node_fs29.promises.readFile(p, "utf8");
8327
8366
  } catch {
8328
8367
  return null;
8329
8368
  }
@@ -8351,7 +8390,7 @@ function needsVersionUpgrade(installed, expected) {
8351
8390
  var NEXT_CONFIG_CANDIDATES = ["next.config.js", "next.config.ts", "next.config.mjs"];
8352
8391
  async function findNextConfig(serviceDir) {
8353
8392
  for (const name of NEXT_CONFIG_CANDIDATES) {
8354
- const candidate = import_node_path44.default.join(serviceDir, name);
8393
+ const candidate = import_node_path45.default.join(serviceDir, name);
8355
8394
  if (await exists3(candidate)) return candidate;
8356
8395
  }
8357
8396
  return null;
@@ -8378,7 +8417,7 @@ function hasRemixDependency(pkg) {
8378
8417
  }
8379
8418
  async function findRemixEntry(serviceDir) {
8380
8419
  for (const rel of REMIX_ENTRY_CANDIDATES) {
8381
- const candidate = import_node_path44.default.join(serviceDir, rel);
8420
+ const candidate = import_node_path45.default.join(serviceDir, rel);
8382
8421
  if (await exists3(candidate)) return candidate;
8383
8422
  }
8384
8423
  return null;
@@ -8390,14 +8429,14 @@ function hasSvelteKitDependency(pkg) {
8390
8429
  }
8391
8430
  async function findSvelteKitHooks(serviceDir) {
8392
8431
  for (const rel of SVELTEKIT_HOOKS_CANDIDATES) {
8393
- const candidate = import_node_path44.default.join(serviceDir, rel);
8432
+ const candidate = import_node_path45.default.join(serviceDir, rel);
8394
8433
  if (await exists3(candidate)) return candidate;
8395
8434
  }
8396
8435
  return null;
8397
8436
  }
8398
8437
  async function findSvelteKitConfig(serviceDir) {
8399
8438
  for (const rel of SVELTEKIT_CONFIG_CANDIDATES) {
8400
- const candidate = import_node_path44.default.join(serviceDir, rel);
8439
+ const candidate = import_node_path45.default.join(serviceDir, rel);
8401
8440
  if (await exists3(candidate)) return candidate;
8402
8441
  }
8403
8442
  return null;
@@ -8408,7 +8447,7 @@ function hasNuxtDependency(pkg) {
8408
8447
  }
8409
8448
  async function findNuxtConfig(serviceDir) {
8410
8449
  for (const name of NUXT_CONFIG_CANDIDATES) {
8411
- const candidate = import_node_path44.default.join(serviceDir, name);
8450
+ const candidate = import_node_path45.default.join(serviceDir, name);
8412
8451
  if (await exists3(candidate)) return candidate;
8413
8452
  }
8414
8453
  return null;
@@ -8419,7 +8458,7 @@ function hasAstroDependency(pkg) {
8419
8458
  }
8420
8459
  async function findAstroConfig(serviceDir) {
8421
8460
  for (const name of ASTRO_CONFIG_CANDIDATES) {
8422
- const candidate = import_node_path44.default.join(serviceDir, name);
8461
+ const candidate = import_node_path45.default.join(serviceDir, name);
8423
8462
  if (await exists3(candidate)) return candidate;
8424
8463
  }
8425
8464
  return null;
@@ -8433,7 +8472,7 @@ function parseNextMajor(range) {
8433
8472
  return Number.isFinite(n) ? n : null;
8434
8473
  }
8435
8474
  async function isTypeScriptProject(serviceDir) {
8436
- return exists3(import_node_path44.default.join(serviceDir, "tsconfig.json"));
8475
+ return exists3(import_node_path45.default.join(serviceDir, "tsconfig.json"));
8437
8476
  }
8438
8477
  var INDEX_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs", ".cjs"];
8439
8478
  var INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `index${ext}`);
@@ -8479,7 +8518,7 @@ function entryFromScript(script) {
8479
8518
  }
8480
8519
  async function resolveEntry(serviceDir, pkg) {
8481
8520
  if (typeof pkg.main === "string" && pkg.main.length > 0) {
8482
- const candidate = import_node_path44.default.resolve(serviceDir, pkg.main);
8521
+ const candidate = import_node_path45.default.resolve(serviceDir, pkg.main);
8483
8522
  if (await exists3(candidate)) return candidate;
8484
8523
  }
8485
8524
  if (pkg.bin) {
@@ -8493,36 +8532,36 @@ async function resolveEntry(serviceDir, pkg) {
8493
8532
  if (typeof first === "string") binEntry = first;
8494
8533
  }
8495
8534
  if (binEntry) {
8496
- const candidate = import_node_path44.default.resolve(serviceDir, binEntry);
8535
+ const candidate = import_node_path45.default.resolve(serviceDir, binEntry);
8497
8536
  if (await exists3(candidate)) return candidate;
8498
8537
  }
8499
8538
  }
8500
8539
  const startEntry = entryFromScript(pkg.scripts?.start);
8501
8540
  if (startEntry) {
8502
- const candidate = import_node_path44.default.resolve(serviceDir, startEntry);
8541
+ const candidate = import_node_path45.default.resolve(serviceDir, startEntry);
8503
8542
  if (await exists3(candidate)) return candidate;
8504
8543
  }
8505
8544
  const devEntry = entryFromScript(pkg.scripts?.dev);
8506
8545
  if (devEntry) {
8507
- const candidate = import_node_path44.default.resolve(serviceDir, devEntry);
8546
+ const candidate = import_node_path45.default.resolve(serviceDir, devEntry);
8508
8547
  if (await exists3(candidate)) return candidate;
8509
8548
  }
8510
8549
  for (const rel of SRC_INDEX_CANDIDATES) {
8511
- const candidate = import_node_path44.default.join(serviceDir, rel);
8550
+ const candidate = import_node_path45.default.join(serviceDir, rel);
8512
8551
  if (await exists3(candidate)) return candidate;
8513
8552
  }
8514
8553
  for (const rel of SRC_NAMED_CANDIDATES) {
8515
- const candidate = import_node_path44.default.join(serviceDir, rel);
8554
+ const candidate = import_node_path45.default.join(serviceDir, rel);
8516
8555
  if (await exists3(candidate)) return candidate;
8517
8556
  }
8518
8557
  for (const name of INDEX_CANDIDATES) {
8519
- const candidate = import_node_path44.default.join(serviceDir, name);
8558
+ const candidate = import_node_path45.default.join(serviceDir, name);
8520
8559
  if (await exists3(candidate)) return candidate;
8521
8560
  }
8522
8561
  return null;
8523
8562
  }
8524
8563
  function dispatchEntry(entryFile, pkg) {
8525
- const ext = import_node_path44.default.extname(entryFile).toLowerCase();
8564
+ const ext = import_node_path45.default.extname(entryFile).toLowerCase();
8526
8565
  if (ext === ".ts" || ext === ".tsx") return "ts";
8527
8566
  if (ext === ".mjs") return "esm";
8528
8567
  if (ext === ".cjs") return "cjs";
@@ -8539,9 +8578,9 @@ function otelInitContents(flavor) {
8539
8578
  return OTEL_INIT_CJS;
8540
8579
  }
8541
8580
  function injectionLine(flavor, entryFile, otelInitFile) {
8542
- let rel = import_node_path44.default.relative(import_node_path44.default.dirname(entryFile), otelInitFile);
8581
+ let rel = import_node_path45.default.relative(import_node_path45.default.dirname(entryFile), otelInitFile);
8543
8582
  if (!rel.startsWith(".")) rel = `./${rel}`;
8544
- rel = rel.split(import_node_path44.default.sep).join("/");
8583
+ rel = rel.split(import_node_path45.default.sep).join("/");
8545
8584
  if (flavor === "cjs") return `require('${rel}')`;
8546
8585
  if (flavor === "esm") return `import '${rel}'`;
8547
8586
  const tsRel = rel.replace(/\.ts$/, "");
@@ -8554,23 +8593,23 @@ function lineIsOtelInjection(line) {
8554
8593
  }
8555
8594
  async function detectsSrcLayout(serviceDir) {
8556
8595
  const [hasSrcApp, hasSrcPages, hasRootApp, hasRootPages] = await Promise.all([
8557
- exists3(import_node_path44.default.join(serviceDir, "src", "app")),
8558
- exists3(import_node_path44.default.join(serviceDir, "src", "pages")),
8559
- exists3(import_node_path44.default.join(serviceDir, "app")),
8560
- exists3(import_node_path44.default.join(serviceDir, "pages"))
8596
+ exists3(import_node_path45.default.join(serviceDir, "src", "app")),
8597
+ exists3(import_node_path45.default.join(serviceDir, "src", "pages")),
8598
+ exists3(import_node_path45.default.join(serviceDir, "app")),
8599
+ exists3(import_node_path45.default.join(serviceDir, "pages"))
8561
8600
  ]);
8562
8601
  return (hasSrcApp || hasSrcPages) && !hasRootApp && !hasRootPages;
8563
8602
  }
8564
8603
  async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
8565
8604
  const useTs = await isTypeScriptProject(serviceDir);
8566
8605
  const srcLayout = await detectsSrcLayout(serviceDir);
8567
- const baseDir = srcLayout ? import_node_path44.default.join(serviceDir, "src") : serviceDir;
8568
- const instrumentationFile = import_node_path44.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
8569
- const instrumentationNodeFile = import_node_path44.default.join(
8606
+ const baseDir = srcLayout ? import_node_path45.default.join(serviceDir, "src") : serviceDir;
8607
+ const instrumentationFile = import_node_path45.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
8608
+ const instrumentationNodeFile = import_node_path45.default.join(
8570
8609
  baseDir,
8571
8610
  useTs ? "instrumentation.node.ts" : "instrumentation.node.js"
8572
8611
  );
8573
- const envNeatFile = import_node_path44.default.join(baseDir, ".env.neat");
8612
+ const envNeatFile = import_node_path45.default.join(baseDir, ".env.neat");
8574
8613
  const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
8575
8614
  const dependencyEdits = [];
8576
8615
  for (const sdk of SDK_PACKAGES) {
@@ -8627,7 +8666,7 @@ async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project)
8627
8666
  const nextMajor = parseNextMajor(nextRange);
8628
8667
  if (nextMajor !== null && nextMajor < 15) {
8629
8668
  try {
8630
- const raw = await import_node_fs28.promises.readFile(nextConfigPath, "utf8");
8669
+ const raw = await import_node_fs29.promises.readFile(nextConfigPath, "utf8");
8631
8670
  if (!raw.includes("instrumentationHook")) {
8632
8671
  nextConfigEdit = {
8633
8672
  file: nextConfigPath,
@@ -8675,7 +8714,7 @@ function buildDependencyEdits(pkg, manifestPath) {
8675
8714
  return edits;
8676
8715
  }
8677
8716
  async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
8678
- const envNeatFile = import_node_path44.default.join(serviceDir, ".env.neat");
8717
+ const envNeatFile = import_node_path45.default.join(serviceDir, ".env.neat");
8679
8718
  if (!await exists3(envNeatFile)) {
8680
8719
  generatedFiles.push({
8681
8720
  file: envNeatFile,
@@ -8710,7 +8749,7 @@ function fileImportsOtelHook(raw, specifiers) {
8710
8749
  }
8711
8750
  async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
8712
8751
  const useTs = await isTypeScriptProject(serviceDir);
8713
- const otelServerFile = import_node_path44.default.join(
8752
+ const otelServerFile = import_node_path45.default.join(
8714
8753
  serviceDir,
8715
8754
  useTs ? "app/otel.server.ts" : "app/otel.server.js"
8716
8755
  );
@@ -8731,7 +8770,7 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
8731
8770
  await queueEnvNeat(serviceDir, pkg, project, generatedFiles);
8732
8771
  const entrypointEdits = [];
8733
8772
  try {
8734
- const raw = await import_node_fs28.promises.readFile(entryFile, "utf8");
8773
+ const raw = await import_node_fs29.promises.readFile(entryFile, "utf8");
8735
8774
  if (!fileImportsOtelHook(raw, ["./otel.server"])) {
8736
8775
  const lines = raw.split(/\r?\n/);
8737
8776
  const firstReal = lines[0]?.startsWith("#!") ? lines[1] ?? "" : lines[0] ?? "";
@@ -8767,11 +8806,11 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
8767
8806
  }
8768
8807
  async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project) {
8769
8808
  const useTs = await isTypeScriptProject(serviceDir);
8770
- const otelInitFile = import_node_path44.default.join(
8809
+ const otelInitFile = import_node_path45.default.join(
8771
8810
  serviceDir,
8772
8811
  useTs ? "src/otel-init.ts" : "src/otel-init.js"
8773
8812
  );
8774
- const resolvedHooksFile = hooksFile ?? import_node_path44.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
8813
+ const resolvedHooksFile = hooksFile ?? import_node_path45.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
8775
8814
  const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
8776
8815
  const generatedFiles = [];
8777
8816
  const entrypointEdits = [];
@@ -8796,7 +8835,7 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
8796
8835
  });
8797
8836
  } else {
8798
8837
  try {
8799
- const raw = await import_node_fs28.promises.readFile(hooksFile, "utf8");
8838
+ const raw = await import_node_fs29.promises.readFile(hooksFile, "utf8");
8800
8839
  if (!fileImportsOtelHook(raw, ["./otel-init"])) {
8801
8840
  const lines = raw.split(/\r?\n/);
8802
8841
  const firstReal = lines[0]?.startsWith("#!") ? lines[1] ?? "" : lines[0] ?? "";
@@ -8833,11 +8872,11 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
8833
8872
  }
8834
8873
  async function planNuxt(serviceDir, pkg, manifestPath, project) {
8835
8874
  const useTs = await isTypeScriptProject(serviceDir);
8836
- const otelPluginFile = import_node_path44.default.join(
8875
+ const otelPluginFile = import_node_path45.default.join(
8837
8876
  serviceDir,
8838
8877
  useTs ? "server/plugins/otel.ts" : "server/plugins/otel.js"
8839
8878
  );
8840
- const otelInitFile = import_node_path44.default.join(
8879
+ const otelInitFile = import_node_path45.default.join(
8841
8880
  serviceDir,
8842
8881
  useTs ? "server/plugins/otel-init.ts" : "server/plugins/otel-init.js"
8843
8882
  );
@@ -8888,19 +8927,19 @@ async function planNuxt(serviceDir, pkg, manifestPath, project) {
8888
8927
  var ASTRO_MIDDLEWARE_CANDIDATES = ["src/middleware.ts", "src/middleware.js"];
8889
8928
  async function findAstroMiddleware(serviceDir) {
8890
8929
  for (const rel of ASTRO_MIDDLEWARE_CANDIDATES) {
8891
- const candidate = import_node_path44.default.join(serviceDir, rel);
8930
+ const candidate = import_node_path45.default.join(serviceDir, rel);
8892
8931
  if (await exists3(candidate)) return candidate;
8893
8932
  }
8894
8933
  return null;
8895
8934
  }
8896
8935
  async function planAstro(serviceDir, pkg, manifestPath, project) {
8897
8936
  const useTs = await isTypeScriptProject(serviceDir);
8898
- const otelInitFile = import_node_path44.default.join(
8937
+ const otelInitFile = import_node_path45.default.join(
8899
8938
  serviceDir,
8900
8939
  useTs ? "src/otel-init.ts" : "src/otel-init.js"
8901
8940
  );
8902
8941
  const existingMiddleware = await findAstroMiddleware(serviceDir);
8903
- const middlewareFile = existingMiddleware ?? import_node_path44.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
8942
+ const middlewareFile = existingMiddleware ?? import_node_path45.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
8904
8943
  const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
8905
8944
  const generatedFiles = [];
8906
8945
  const entrypointEdits = [];
@@ -8925,7 +8964,7 @@ async function planAstro(serviceDir, pkg, manifestPath, project) {
8925
8964
  });
8926
8965
  } else {
8927
8966
  try {
8928
- const raw = await import_node_fs28.promises.readFile(existingMiddleware, "utf8");
8967
+ const raw = await import_node_fs29.promises.readFile(existingMiddleware, "utf8");
8929
8968
  if (!fileImportsOtelHook(raw, ["./otel-init"])) {
8930
8969
  const lines = raw.split(/\r?\n/);
8931
8970
  const firstReal = lines[0]?.startsWith("#!") ? lines[1] ?? "" : lines[0] ?? "";
@@ -8996,7 +9035,7 @@ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
8996
9035
  }
8997
9036
  async function plan(serviceDir, opts) {
8998
9037
  const pkg = await readPackageJson2(serviceDir);
8999
- const manifestPath = import_node_path44.default.join(serviceDir, "package.json");
9038
+ const manifestPath = import_node_path45.default.join(serviceDir, "package.json");
9000
9039
  const project = opts?.project;
9001
9040
  const empty = {
9002
9041
  language: "javascript",
@@ -9031,8 +9070,8 @@ async function plan(serviceDir, opts) {
9031
9070
  return { ...empty, libOnly: true };
9032
9071
  }
9033
9072
  const flavor = dispatchEntry(entryFile, pkg);
9034
- const otelInitFile = import_node_path44.default.join(import_node_path44.default.dirname(entryFile), otelInitFilename(flavor));
9035
- const envNeatFile = import_node_path44.default.join(serviceDir, ".env.neat");
9073
+ const otelInitFile = import_node_path45.default.join(import_node_path45.default.dirname(entryFile), otelInitFilename(flavor));
9074
+ const envNeatFile = import_node_path45.default.join(serviceDir, ".env.neat");
9036
9075
  const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
9037
9076
  const dependencyEdits = [];
9038
9077
  for (const sdk of SDK_PACKAGES) {
@@ -9056,7 +9095,7 @@ async function plan(serviceDir, opts) {
9056
9095
  }
9057
9096
  const entrypointEdits = [];
9058
9097
  try {
9059
- const raw = await import_node_fs28.promises.readFile(entryFile, "utf8");
9098
+ const raw = await import_node_fs29.promises.readFile(entryFile, "utf8");
9060
9099
  const lines = raw.split(/\r?\n/);
9061
9100
  const firstReal = lines[0]?.startsWith("#!") ? lines[1] ?? "" : lines[0] ?? "";
9062
9101
  if (!lineIsOtelInjection(firstReal)) {
@@ -9101,13 +9140,13 @@ async function plan(serviceDir, opts) {
9101
9140
  };
9102
9141
  }
9103
9142
  function isAllowedWritePath(serviceDir, target) {
9104
- const rel = import_node_path44.default.relative(serviceDir, target);
9143
+ const rel = import_node_path45.default.relative(serviceDir, target);
9105
9144
  if (rel.startsWith("..")) return false;
9106
- const base = import_node_path44.default.basename(target);
9145
+ const base = import_node_path45.default.basename(target);
9107
9146
  if (base === "package.json") return true;
9108
9147
  if (base === ".env.neat") return true;
9109
9148
  if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
9110
- const relPosix = rel.split(import_node_path44.default.sep).join("/");
9149
+ const relPosix = rel.split(import_node_path45.default.sep).join("/");
9111
9150
  if (/^instrumentation(?:\.node)?\.(?:js|cjs|mjs|ts)$/.test(base)) {
9112
9151
  if (relPosix === base) return true;
9113
9152
  if (relPosix === `src/${base}`) return true;
@@ -9124,10 +9163,10 @@ function isAllowedWritePath(serviceDir, target) {
9124
9163
  return false;
9125
9164
  }
9126
9165
  async function writeAtomic(file, contents) {
9127
- await import_node_fs28.promises.mkdir(import_node_path44.default.dirname(file), { recursive: true });
9166
+ await import_node_fs29.promises.mkdir(import_node_path45.default.dirname(file), { recursive: true });
9128
9167
  const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
9129
- await import_node_fs28.promises.writeFile(tmp, contents, "utf8");
9130
- await import_node_fs28.promises.rename(tmp, file);
9168
+ await import_node_fs29.promises.writeFile(tmp, contents, "utf8");
9169
+ await import_node_fs29.promises.rename(tmp, file);
9131
9170
  }
9132
9171
  async function apply(installPlan) {
9133
9172
  const { serviceDir } = installPlan;
@@ -9203,7 +9242,7 @@ async function apply(installPlan) {
9203
9242
  for (const target of allTargets) {
9204
9243
  if (await exists3(target)) {
9205
9244
  try {
9206
- originals.set(target, await import_node_fs28.promises.readFile(target, "utf8"));
9245
+ originals.set(target, await import_node_fs29.promises.readFile(target, "utf8"));
9207
9246
  } catch {
9208
9247
  }
9209
9248
  }
@@ -9286,14 +9325,14 @@ async function rollback(installPlan, originals, createdFiles) {
9286
9325
  const removed = [];
9287
9326
  for (const [file, raw] of originals.entries()) {
9288
9327
  try {
9289
- await import_node_fs28.promises.writeFile(file, raw, "utf8");
9328
+ await import_node_fs29.promises.writeFile(file, raw, "utf8");
9290
9329
  restored.push(file);
9291
9330
  } catch {
9292
9331
  }
9293
9332
  }
9294
9333
  for (const file of createdFiles) {
9295
9334
  try {
9296
- await import_node_fs28.promises.unlink(file);
9335
+ await import_node_fs29.promises.unlink(file);
9297
9336
  removed.push(file);
9298
9337
  } catch {
9299
9338
  }
@@ -9308,8 +9347,8 @@ async function rollback(installPlan, originals, createdFiles) {
9308
9347
  ...removed.map((f) => `removed: ${f}`),
9309
9348
  ""
9310
9349
  ];
9311
- const rollbackPath = import_node_path44.default.join(installPlan.serviceDir, "neat-rollback.patch");
9312
- await import_node_fs28.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
9350
+ const rollbackPath = import_node_path45.default.join(installPlan.serviceDir, "neat-rollback.patch");
9351
+ await import_node_fs29.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
9313
9352
  }
9314
9353
  function injectInstrumentationHook(raw) {
9315
9354
  if (raw.includes("instrumentationHook")) return raw;
@@ -9338,8 +9377,8 @@ var javascriptInstaller = {
9338
9377
 
9339
9378
  // src/installers/python.ts
9340
9379
  init_cjs_shims();
9341
- var import_node_fs29 = require("fs");
9342
- var import_node_path45 = __toESM(require("path"), 1);
9380
+ var import_node_fs30 = require("fs");
9381
+ var import_node_path46 = __toESM(require("path"), 1);
9343
9382
  var SDK_PACKAGES2 = [
9344
9383
  { name: "opentelemetry-distro", version: ">=0.49b0" },
9345
9384
  { name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
@@ -9351,7 +9390,7 @@ var OTEL_ENV2 = {
9351
9390
  };
9352
9391
  async function exists4(p) {
9353
9392
  try {
9354
- await import_node_fs29.promises.stat(p);
9393
+ await import_node_fs30.promises.stat(p);
9355
9394
  return true;
9356
9395
  } catch {
9357
9396
  return false;
@@ -9360,7 +9399,7 @@ async function exists4(p) {
9360
9399
  async function detect2(serviceDir) {
9361
9400
  const markers = ["requirements.txt", "pyproject.toml", "setup.py"];
9362
9401
  for (const m of markers) {
9363
- if (await exists4(import_node_path45.default.join(serviceDir, m))) return true;
9402
+ if (await exists4(import_node_path46.default.join(serviceDir, m))) return true;
9364
9403
  }
9365
9404
  return false;
9366
9405
  }
@@ -9370,9 +9409,9 @@ function reqPackageName(line) {
9370
9409
  return head.replace(/[<>=!~].*$/, "").toLowerCase();
9371
9410
  }
9372
9411
  async function planRequirementsTxtEdits(serviceDir) {
9373
- const file = import_node_path45.default.join(serviceDir, "requirements.txt");
9412
+ const file = import_node_path46.default.join(serviceDir, "requirements.txt");
9374
9413
  if (!await exists4(file)) return null;
9375
- const raw = await import_node_fs29.promises.readFile(file, "utf8");
9414
+ const raw = await import_node_fs30.promises.readFile(file, "utf8");
9376
9415
  const presentNames = new Set(
9377
9416
  raw.split(/\r?\n/).map(reqPackageName).filter((n) => n.length > 0)
9378
9417
  );
@@ -9380,9 +9419,9 @@ async function planRequirementsTxtEdits(serviceDir) {
9380
9419
  return { manifest: file, missing: [...missing] };
9381
9420
  }
9382
9421
  async function planProcfileEdits(serviceDir) {
9383
- const procfile = import_node_path45.default.join(serviceDir, "Procfile");
9422
+ const procfile = import_node_path46.default.join(serviceDir, "Procfile");
9384
9423
  if (!await exists4(procfile)) return [];
9385
- const raw = await import_node_fs29.promises.readFile(procfile, "utf8");
9424
+ const raw = await import_node_fs30.promises.readFile(procfile, "utf8");
9386
9425
  const edits = [];
9387
9426
  for (const line of raw.split(/\r?\n/)) {
9388
9427
  if (line.length === 0) continue;
@@ -9434,8 +9473,8 @@ async function applyRequirementsTxt(manifest, edits, original) {
9434
9473
  const next = `${original}${trailing}${newlines.join("\n")}
9435
9474
  `;
9436
9475
  const tmp = `${manifest}.${process.pid}.${Date.now()}.tmp`;
9437
- await import_node_fs29.promises.writeFile(tmp, next, "utf8");
9438
- await import_node_fs29.promises.rename(tmp, manifest);
9476
+ await import_node_fs30.promises.writeFile(tmp, next, "utf8");
9477
+ await import_node_fs30.promises.rename(tmp, manifest);
9439
9478
  }
9440
9479
  async function applyProcfile(procfile, edits, original) {
9441
9480
  let next = original;
@@ -9444,8 +9483,8 @@ async function applyProcfile(procfile, edits, original) {
9444
9483
  next = next.replace(e.before, e.after);
9445
9484
  }
9446
9485
  const tmp = `${procfile}.${process.pid}.${Date.now()}.tmp`;
9447
- await import_node_fs29.promises.writeFile(tmp, next, "utf8");
9448
- await import_node_fs29.promises.rename(tmp, procfile);
9486
+ await import_node_fs30.promises.writeFile(tmp, next, "utf8");
9487
+ await import_node_fs30.promises.rename(tmp, procfile);
9449
9488
  }
9450
9489
  async function apply2(installPlan) {
9451
9490
  const { serviceDir } = installPlan;
@@ -9458,7 +9497,7 @@ async function apply2(installPlan) {
9458
9497
  const originals = /* @__PURE__ */ new Map();
9459
9498
  for (const file of touched) {
9460
9499
  try {
9461
- originals.set(file, await import_node_fs29.promises.readFile(file, "utf8"));
9500
+ originals.set(file, await import_node_fs30.promises.readFile(file, "utf8"));
9462
9501
  } catch {
9463
9502
  }
9464
9503
  }
@@ -9469,7 +9508,7 @@ async function apply2(installPlan) {
9469
9508
  if (raw === void 0) {
9470
9509
  throw new Error(`python installer: cannot read ${file} during apply`);
9471
9510
  }
9472
- const base = import_node_path45.default.basename(file);
9511
+ const base = import_node_path46.default.basename(file);
9473
9512
  if (base === "requirements.txt") {
9474
9513
  const edits = installPlan.dependencyEdits.filter((e) => e.file === file);
9475
9514
  if (edits.length > 0) {
@@ -9494,7 +9533,7 @@ async function rollback2(installPlan, originals) {
9494
9533
  const restored = [];
9495
9534
  for (const [file, raw] of originals.entries()) {
9496
9535
  try {
9497
- await import_node_fs29.promises.writeFile(file, raw, "utf8");
9536
+ await import_node_fs30.promises.writeFile(file, raw, "utf8");
9498
9537
  restored.push(file);
9499
9538
  } catch {
9500
9539
  }
@@ -9508,8 +9547,8 @@ async function rollback2(installPlan, originals) {
9508
9547
  ...restored.map((f) => `restored: ${f}`),
9509
9548
  ""
9510
9549
  ];
9511
- const rollbackPath = import_node_path45.default.join(installPlan.serviceDir, "neat-rollback.patch");
9512
- await import_node_fs29.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
9550
+ const rollbackPath = import_node_path46.default.join(installPlan.serviceDir, "neat-rollback.patch");
9551
+ await import_node_fs30.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
9513
9552
  }
9514
9553
  var pythonInstaller = {
9515
9554
  name: "python",
@@ -9632,10 +9671,10 @@ function renderPatch(sections) {
9632
9671
 
9633
9672
  // src/orchestrator.ts
9634
9673
  init_cjs_shims();
9635
- var import_node_fs30 = require("fs");
9674
+ var import_node_fs31 = require("fs");
9636
9675
  var import_node_http = __toESM(require("http"), 1);
9637
9676
  var import_node_net = __toESM(require("net"), 1);
9638
- var import_node_path46 = __toESM(require("path"), 1);
9677
+ var import_node_path47 = __toESM(require("path"), 1);
9639
9678
  var import_node_child_process3 = require("child_process");
9640
9679
  var import_node_readline = __toESM(require("readline"), 1);
9641
9680
  async function extractAndPersist(opts) {
@@ -9644,7 +9683,7 @@ async function extractAndPersist(opts) {
9644
9683
  const graphKey = opts.projectExplicit ? opts.project : DEFAULT_PROJECT;
9645
9684
  resetGraph(graphKey);
9646
9685
  const graph = getGraph(graphKey);
9647
- const projectPaths = pathsForProject(graphKey, import_node_path46.default.join(opts.scanPath, "neat-out"));
9686
+ const projectPaths = pathsForProject(graphKey, import_node_path47.default.join(opts.scanPath, "neat-out"));
9648
9687
  const extraction = await extractFromDirectory(graph, opts.scanPath, {
9649
9688
  errorsPath: projectPaths.errorsPath
9650
9689
  });
@@ -9698,7 +9737,7 @@ async function applyInstallersOver(services, project, options = {}) {
9698
9737
  console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
9699
9738
  } else if (outcome.outcome === "react-native") {
9700
9739
  reactNative++;
9701
- const svcName = import_node_path46.default.basename(svc.dir);
9740
+ const svcName = import_node_path47.default.basename(svc.dir);
9702
9741
  console.log(
9703
9742
  `neat: ${svc.dir} detected as React Native / Expo
9704
9743
  The installer doesn't cover this runtime deterministically.
@@ -9709,7 +9748,7 @@ async function applyInstallersOver(services, project, options = {}) {
9709
9748
  );
9710
9749
  } else if (outcome.outcome === "bun") {
9711
9750
  bun++;
9712
- const svcName = import_node_path46.default.basename(svc.dir);
9751
+ const svcName = import_node_path47.default.basename(svc.dir);
9713
9752
  console.log(
9714
9753
  `neat: ${svc.dir} detected as Bun
9715
9754
  The installer doesn't cover this runtime deterministically.
@@ -9720,7 +9759,7 @@ async function applyInstallersOver(services, project, options = {}) {
9720
9759
  );
9721
9760
  } else if (outcome.outcome === "deno") {
9722
9761
  deno++;
9723
- const svcName = import_node_path46.default.basename(svc.dir);
9762
+ const svcName = import_node_path47.default.basename(svc.dir);
9724
9763
  console.log(
9725
9764
  `neat: ${svc.dir} detected as Deno
9726
9765
  The installer doesn't cover this runtime deterministically.
@@ -9731,7 +9770,7 @@ async function applyInstallersOver(services, project, options = {}) {
9731
9770
  );
9732
9771
  } else if (outcome.outcome === "cloudflare-workers") {
9733
9772
  cloudflareWorkers++;
9734
- const svcName = import_node_path46.default.basename(svc.dir);
9773
+ const svcName = import_node_path47.default.basename(svc.dir);
9735
9774
  console.log(
9736
9775
  `neat: ${svc.dir} detected as Cloudflare Workers
9737
9776
  The installer doesn't cover this runtime deterministically.
@@ -9742,7 +9781,7 @@ async function applyInstallersOver(services, project, options = {}) {
9742
9781
  );
9743
9782
  } else if (outcome.outcome === "electron") {
9744
9783
  electron++;
9745
- const svcName = import_node_path46.default.basename(svc.dir);
9784
+ const svcName = import_node_path47.default.basename(svc.dir);
9746
9785
  console.log(
9747
9786
  `neat: ${svc.dir} detected as Electron
9748
9787
  The installer doesn't cover this runtime deterministically.
@@ -9916,10 +9955,10 @@ function formatPortCollisionMessage(port) {
9916
9955
  ];
9917
9956
  }
9918
9957
  function spawnDaemonDetached() {
9919
- const here = import_node_path46.default.dirname(new URL(importMetaUrl).pathname);
9958
+ const here = import_node_path47.default.dirname(new URL(importMetaUrl).pathname);
9920
9959
  const candidates = [
9921
- import_node_path46.default.join(here, "neatd.cjs"),
9922
- import_node_path46.default.join(here, "neatd.js")
9960
+ import_node_path47.default.join(here, "neatd.cjs"),
9961
+ import_node_path47.default.join(here, "neatd.js")
9923
9962
  ];
9924
9963
  let entry2 = null;
9925
9964
  const fsSync = require("fs");
@@ -9977,12 +10016,13 @@ async function runOrchestrator(opts) {
9977
10016
  browser: "skipped"
9978
10017
  }
9979
10018
  };
9980
- const stat = await import_node_fs30.promises.stat(opts.scanPath).catch(() => null);
10019
+ const stat = await import_node_fs31.promises.stat(opts.scanPath).catch(() => null);
9981
10020
  if (!stat || !stat.isDirectory()) {
9982
10021
  console.error(`neat: ${opts.scanPath} is not a directory`);
9983
10022
  result.exitCode = 2;
9984
10023
  return result;
9985
10024
  }
10025
+ printBanner();
9986
10026
  console.log(`neat: ${opts.scanPath}`);
9987
10027
  console.log("");
9988
10028
  const persisted = await extractAndPersist({
@@ -9993,6 +10033,13 @@ async function runOrchestrator(opts) {
9993
10033
  const { graph, services, languages } = persisted;
9994
10034
  result.steps.discovery = { services: services.length, languages };
9995
10035
  console.log(`discovered ${services.length} service(s) across ${languages.length} language(s)`);
10036
+ if (services.length === 0) {
10037
+ console.error(
10038
+ `neat: no services found in ${opts.scanPath} \u2014 run from inside your project root, or \`npx neat.is <path>\``
10039
+ );
10040
+ result.exitCode = 2;
10041
+ return result;
10042
+ }
9996
10043
  let runApply = !opts.noInstrument;
9997
10044
  if (runApply && !opts.yes && process.stdout.isTTY && process.stdin.isTTY) {
9998
10045
  runApply = await promptYesNo("instrument your services and open the dashboard?");
@@ -10116,11 +10163,17 @@ function printSummary(result, graph, dashboardUrl) {
10116
10163
  for (const [t, c] of [...byEdge.entries()].sort()) console.log(` ${t}: ${c}`);
10117
10164
  console.log("");
10118
10165
  console.log(`dashboard: ${dashboardUrl}`);
10166
+ const token = process.env.NEAT_AUTH_TOKEN;
10167
+ if (typeof token === "string" && token.length > 0) {
10168
+ console.log(`auth token: ${token}`);
10169
+ } else {
10170
+ console.log("running locally \u2014 open the dashboard, no token needed");
10171
+ }
10119
10172
  }
10120
10173
 
10121
10174
  // src/cli-verbs.ts
10122
10175
  init_cjs_shims();
10123
- var import_node_path47 = __toESM(require("path"), 1);
10176
+ var import_node_path48 = __toESM(require("path"), 1);
10124
10177
 
10125
10178
  // src/cli-client.ts
10126
10179
  init_cjs_shims();
@@ -10149,10 +10202,10 @@ function createHttpClient(baseUrl, bearerToken) {
10149
10202
  const root = baseUrl.replace(/\/$/, "");
10150
10203
  const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
10151
10204
  return {
10152
- async get(path49) {
10205
+ async get(path50) {
10153
10206
  let res;
10154
10207
  try {
10155
- res = await fetch(`${root}${path49}`, {
10208
+ res = await fetch(`${root}${path50}`, {
10156
10209
  headers: { ...authHeader }
10157
10210
  });
10158
10211
  } catch (err) {
@@ -10164,16 +10217,16 @@ function createHttpClient(baseUrl, bearerToken) {
10164
10217
  const body = await res.text().catch(() => "");
10165
10218
  throw new HttpError(
10166
10219
  res.status,
10167
- `${res.status} ${res.statusText} on GET ${path49}: ${body}`,
10220
+ `${res.status} ${res.statusText} on GET ${path50}: ${body}`,
10168
10221
  body
10169
10222
  );
10170
10223
  }
10171
10224
  return await res.json();
10172
10225
  },
10173
- async post(path49, body) {
10226
+ async post(path50, body) {
10174
10227
  let res;
10175
10228
  try {
10176
- res = await fetch(`${root}${path49}`, {
10229
+ res = await fetch(`${root}${path50}`, {
10177
10230
  method: "POST",
10178
10231
  headers: { "content-type": "application/json", ...authHeader },
10179
10232
  body: JSON.stringify(body)
@@ -10187,7 +10240,7 @@ function createHttpClient(baseUrl, bearerToken) {
10187
10240
  const text = await res.text().catch(() => "");
10188
10241
  throw new HttpError(
10189
10242
  res.status,
10190
- `${res.status} ${res.statusText} on POST ${path49}: ${text}`,
10243
+ `${res.status} ${res.statusText} on POST ${path50}: ${text}`,
10191
10244
  text
10192
10245
  );
10193
10246
  }
@@ -10201,12 +10254,12 @@ function projectPath(project, suffix) {
10201
10254
  }
10202
10255
  async function runRootCause(client, input) {
10203
10256
  const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
10204
- const path49 = projectPath(
10257
+ const path50 = projectPath(
10205
10258
  input.project,
10206
10259
  `/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
10207
10260
  );
10208
10261
  try {
10209
- const result = await client.get(path49);
10262
+ const result = await client.get(path50);
10210
10263
  const arrowPath = result.traversalPath.join(" \u2190 ");
10211
10264
  const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
10212
10265
  const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
@@ -10232,12 +10285,12 @@ async function runRootCause(client, input) {
10232
10285
  }
10233
10286
  async function runBlastRadius(client, input) {
10234
10287
  const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
10235
- const path49 = projectPath(
10288
+ const path50 = projectPath(
10236
10289
  input.project,
10237
10290
  `/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
10238
10291
  );
10239
10292
  try {
10240
- const result = await client.get(path49);
10293
+ const result = await client.get(path50);
10241
10294
  if (result.totalAffected === 0) {
10242
10295
  return {
10243
10296
  summary: `${result.origin} has no downstream dependencies. Nothing else would break if it failed.`
@@ -10271,12 +10324,12 @@ function formatBlastEntry(n) {
10271
10324
  }
10272
10325
  async function runDependencies(client, input) {
10273
10326
  const depth = input.depth ?? 3;
10274
- const path49 = projectPath(
10327
+ const path50 = projectPath(
10275
10328
  input.project,
10276
10329
  `/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
10277
10330
  );
10278
10331
  try {
10279
- const result = await client.get(path49);
10332
+ const result = await client.get(path50);
10280
10333
  if (result.total === 0) {
10281
10334
  return {
10282
10335
  summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
@@ -10357,9 +10410,9 @@ function formatDuration(ms) {
10357
10410
  return `${Math.round(h / 24)}d`;
10358
10411
  }
10359
10412
  async function runIncidents(client, input) {
10360
- const path49 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
10413
+ const path50 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
10361
10414
  try {
10362
- const body = await client.get(path49);
10415
+ const body = await client.get(path50);
10363
10416
  const events = body.events;
10364
10417
  if (events.length === 0) {
10365
10418
  return {
@@ -10649,7 +10702,7 @@ async function resolveProjectEntry(opts) {
10649
10702
  const cwd = opts.cwd ?? process.cwd();
10650
10703
  const resolvedCwd = await normalizeProjectPath(cwd);
10651
10704
  for (const entry2 of entries) {
10652
- if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path47.default.sep}`)) {
10705
+ if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path48.default.sep}`)) {
10653
10706
  return entry2;
10654
10707
  }
10655
10708
  }
@@ -10800,8 +10853,24 @@ async function runSync(opts) {
10800
10853
 
10801
10854
  // src/cli.ts
10802
10855
  var import_types27 = require("@neat.is/types");
10856
+ function isNpxInvocation() {
10857
+ if (process.env.npm_command === "exec") return true;
10858
+ const execpath = process.env.npm_execpath ?? "";
10859
+ if (execpath.includes("npx")) return true;
10860
+ const entry2 = process.argv[1] ?? "";
10861
+ if (entry2.includes("/_npx/") || entry2.includes("\\_npx\\")) return true;
10862
+ return false;
10863
+ }
10864
+ function commandPrefix() {
10865
+ return isNpxInvocation() ? "npx neat.is" : "neat";
10866
+ }
10803
10867
  function usage() {
10804
- console.log("usage: neat <command> [args] [--project <name>]");
10868
+ const neat = commandPrefix();
10869
+ console.log("Installed via npx? Prefix commands with `npx neat.is`, or install once: `npm i -g neat.is`.");
10870
+ console.log("");
10871
+ console.log(`usage: ${neat} <command> [args] [--project <name>]`);
10872
+ console.log("");
10873
+ console.log(`Run \`${neat}\` with no command from inside your project to go zero-to-graph in one step.`);
10805
10874
  console.log("");
10806
10875
  console.log("lifecycle commands:");
10807
10876
  console.log(" init <path> One-time install: discover, extract, register, plan SDK install.");
@@ -10841,30 +10910,30 @@ function usage() {
10841
10910
  console.log("");
10842
10911
  console.log("query commands (mirror the MCP tools, ADR-050):");
10843
10912
  console.log(" root-cause <node-id> Walk inbound edges to find what broke first.");
10844
- console.log(" example: neat root-cause service:<name>");
10913
+ console.log(` example: ${neat} root-cause service:<name>`);
10845
10914
  console.log(" blast-radius <node-id> BFS outbound \u2014 what would break if this dies.");
10846
- console.log(" example: neat blast-radius database:<host>");
10915
+ console.log(` example: ${neat} blast-radius database:<host>`);
10847
10916
  console.log(" dependencies <node-id> Transitive outbound dependencies.");
10848
10917
  console.log(" Flags: --depth N (default 3, max 10)");
10849
- console.log(" example: neat dependencies service:<name> --depth 2");
10918
+ console.log(` example: ${neat} dependencies service:<name> --depth 2`);
10850
10919
  console.log(" observed-dependencies <node-id> OBSERVED-only outbound edges (runtime traffic).");
10851
- console.log(" example: neat observed-dependencies service:<name>");
10920
+ console.log(` example: ${neat} observed-dependencies service:<name>`);
10852
10921
  console.log(" incidents [<node-id>] Recent error events; per-node when an id is given.");
10853
10922
  console.log(" Flags: --limit N (default 20)");
10854
- console.log(" example: neat incidents service:<name> --limit 5");
10923
+ console.log(` example: ${neat} incidents service:<name> --limit 5`);
10855
10924
  console.log(" search <query> Semantic (or substring) match on node names/ids.");
10856
- console.log(' example: neat search "checkout"');
10925
+ console.log(` example: ${neat} search "checkout"`);
10857
10926
  console.log(" diff --against <snapshot> Compare the live graph to a saved snapshot.");
10858
- console.log(" example: neat diff --against ./snapshots/baseline.json");
10927
+ console.log(` example: ${neat} diff --against ./snapshots/baseline.json`);
10859
10928
  console.log(" stale-edges Recent OBSERVED \u2192 STALE transitions.");
10860
10929
  console.log(" Flags: --limit N, --edge-type CALLS|CONNECTS_TO|...");
10861
- console.log(" example: neat stale-edges --edge-type CALLS");
10930
+ console.log(` example: ${neat} stale-edges --edge-type CALLS`);
10862
10931
  console.log(" policies Current policy violations.");
10863
10932
  console.log(" Flags: --node <id>, --hypothetical-action <json>");
10864
- console.log(" example: neat policies --node service:<name> --json");
10933
+ console.log(` example: ${neat} policies --node service:<name> --json`);
10865
10934
  console.log(" divergences Where code (EXTRACTED) and production (OBSERVED) disagree.");
10866
10935
  console.log(" Flags: --type <list>, --min-confidence <0..1>, --node <id>");
10867
- console.log(" example: neat divergences --min-confidence 0.7");
10936
+ console.log(` example: ${neat} divergences --min-confidence 0.7`);
10868
10937
  console.log("");
10869
10938
  console.log("flags:");
10870
10939
  console.log(' --project <name> Name the project this command targets. Default: "default".');
@@ -11008,40 +11077,10 @@ function assignFlag(out, field, value) {
11008
11077
  ;
11009
11078
  out[field] = value;
11010
11079
  }
11011
- function readPackageVersion() {
11012
- const here = typeof __dirname !== "undefined" ? __dirname : import_node_path48.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
11013
- const candidates = [
11014
- import_node_path48.default.resolve(here, "../package.json"),
11015
- import_node_path48.default.resolve(here, "../../package.json")
11016
- ];
11017
- for (const candidate of candidates) {
11018
- try {
11019
- const raw = (0, import_node_fs31.readFileSync)(candidate, "utf8");
11020
- const parsed = JSON.parse(raw);
11021
- if (parsed.name === "@neat.is/core" && typeof parsed.version === "string") {
11022
- return parsed.version;
11023
- }
11024
- } catch {
11025
- }
11026
- }
11027
- return "unknown";
11028
- }
11029
11080
  function printVersion() {
11030
11081
  process.stdout.write(`${readPackageVersion()}
11031
11082
  `);
11032
11083
  }
11033
- function printBanner() {
11034
- console.log("\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");
11035
- console.log("\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D");
11036
- console.log("\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 ");
11037
- console.log("\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2551 ");
11038
- console.log("\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 ");
11039
- console.log("\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D ");
11040
- console.log("");
11041
- console.log(" Network Expressive Architecting Tool");
11042
- console.log(` neat.is \xB7 v${readPackageVersion()} \xB7 Apache 2.0`);
11043
- console.log("");
11044
- }
11045
11084
  function printDiscoveryReport(opts, services) {
11046
11085
  const languages = [...new Set(services.map((s) => s.node.language))].sort();
11047
11086
  const mode = opts.dryRun ? "dry-run" : opts.apply ? "apply" : "patch-only";
@@ -11080,7 +11119,7 @@ async function buildPatchSections(services, project) {
11080
11119
  }
11081
11120
  async function runInit(opts) {
11082
11121
  const written = [];
11083
- const stat = await import_node_fs31.promises.stat(opts.scanPath).catch(() => null);
11122
+ const stat = await import_node_fs32.promises.stat(opts.scanPath).catch(() => null);
11084
11123
  if (!stat || !stat.isDirectory()) {
11085
11124
  console.error(`neat init: ${opts.scanPath} is not a directory`);
11086
11125
  return { exitCode: 2, writtenFiles: written };
@@ -11089,13 +11128,13 @@ async function runInit(opts) {
11089
11128
  printDiscoveryReport(opts, services);
11090
11129
  const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
11091
11130
  const patch = renderPatch(sections);
11092
- const patchPath = import_node_path48.default.join(opts.scanPath, "neat.patch");
11131
+ const patchPath = import_node_path49.default.join(opts.scanPath, "neat.patch");
11093
11132
  if (opts.dryRun) {
11094
- await import_node_fs31.promises.writeFile(patchPath, patch, "utf8");
11133
+ await import_node_fs32.promises.writeFile(patchPath, patch, "utf8");
11095
11134
  written.push(patchPath);
11096
11135
  console.log(`dry-run: patch written to ${patchPath}`);
11097
- const gitignorePath = import_node_path48.default.join(opts.scanPath, ".gitignore");
11098
- const gitignoreExists = await import_node_fs31.promises.stat(gitignorePath).then(() => true).catch(() => false);
11136
+ const gitignorePath = import_node_path49.default.join(opts.scanPath, ".gitignore");
11137
+ const gitignoreExists = await import_node_fs32.promises.stat(gitignorePath).then(() => true).catch(() => false);
11099
11138
  const verb = gitignoreExists ? "append" : "create";
11100
11139
  console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
11101
11140
  console.log("rerun without --dry-run to register and snapshot.");
@@ -11106,9 +11145,9 @@ async function runInit(opts) {
11106
11145
  const graph = getGraph(graphKey);
11107
11146
  const projectPaths = pathsForProject(
11108
11147
  graphKey,
11109
- import_node_path48.default.join(opts.scanPath, "neat-out")
11148
+ import_node_path49.default.join(opts.scanPath, "neat-out")
11110
11149
  );
11111
- const errorsPath = import_node_path48.default.join(import_node_path48.default.dirname(opts.outPath), import_node_path48.default.basename(projectPaths.errorsPath));
11150
+ const errorsPath = import_node_path49.default.join(import_node_path49.default.dirname(opts.outPath), import_node_path49.default.basename(projectPaths.errorsPath));
11112
11151
  const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
11113
11152
  await saveGraphToDisk(graph, opts.outPath);
11114
11153
  written.push(opts.outPath);
@@ -11187,7 +11226,7 @@ async function runInit(opts) {
11187
11226
  console.log("Run `npm install` (or your language equivalent) to refresh lockfiles.");
11188
11227
  }
11189
11228
  } else {
11190
- await import_node_fs31.promises.writeFile(patchPath, patch, "utf8");
11229
+ await import_node_fs32.promises.writeFile(patchPath, patch, "utf8");
11191
11230
  written.push(patchPath);
11192
11231
  }
11193
11232
  }
@@ -11227,9 +11266,9 @@ var CLAUDE_SKILL_CONFIG = {
11227
11266
  };
11228
11267
  function claudeConfigPath() {
11229
11268
  const override = process.env.NEAT_CLAUDE_CONFIG;
11230
- if (override && override.length > 0) return import_node_path48.default.resolve(override);
11269
+ if (override && override.length > 0) return import_node_path49.default.resolve(override);
11231
11270
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
11232
- return import_node_path48.default.join(home, ".claude.json");
11271
+ return import_node_path49.default.join(home, ".claude.json");
11233
11272
  }
11234
11273
  async function runSkill(opts) {
11235
11274
  const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
@@ -11241,7 +11280,7 @@ async function runSkill(opts) {
11241
11280
  const target = claudeConfigPath();
11242
11281
  let existing = {};
11243
11282
  try {
11244
- existing = JSON.parse(await import_node_fs31.promises.readFile(target, "utf8"));
11283
+ existing = JSON.parse(await import_node_fs32.promises.readFile(target, "utf8"));
11245
11284
  } catch (err) {
11246
11285
  if (err.code !== "ENOENT") {
11247
11286
  console.error(`neat skill: failed to read ${target} \u2014 ${err.message}`);
@@ -11253,8 +11292,8 @@ async function runSkill(opts) {
11253
11292
  ...existing,
11254
11293
  mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
11255
11294
  };
11256
- await import_node_fs31.promises.mkdir(import_node_path48.default.dirname(target), { recursive: true });
11257
- await import_node_fs31.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
11295
+ await import_node_fs32.promises.mkdir(import_node_path49.default.dirname(target), { recursive: true });
11296
+ await import_node_fs32.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
11258
11297
  console.log(`neat skill: wrote mcpServers.neat to ${target}`);
11259
11298
  console.log("restart Claude Code to pick up the new MCP server.");
11260
11299
  return { exitCode: 0 };
@@ -11269,16 +11308,24 @@ async function runSkill(opts) {
11269
11308
  return { exitCode: 0 };
11270
11309
  }
11271
11310
  async function main() {
11272
- const [, , cmd, ...rest] = process.argv;
11273
- if (!cmd || cmd === "-h" || cmd === "--help") {
11311
+ const argv = process.argv.slice(2);
11312
+ const cmd0 = argv[0];
11313
+ if (cmd0 === "-h" || cmd0 === "--help") {
11274
11314
  usage();
11275
11315
  process.exit(0);
11276
11316
  }
11277
- if (cmd === "--version" || cmd === "-v" || cmd === "version") {
11317
+ if (cmd0 === "--version" || cmd0 === "-v" || cmd0 === "version") {
11278
11318
  printVersion();
11279
11319
  process.exit(0);
11280
11320
  }
11281
- const parsed = parseArgs(rest);
11321
+ const argvParsed = parseArgs(argv);
11322
+ if (argvParsed.positional.length === 0) {
11323
+ const orchestratorCode2 = await tryOrchestrator(process.cwd(), argvParsed);
11324
+ if (orchestratorCode2 !== null && orchestratorCode2 !== 0) process.exit(orchestratorCode2);
11325
+ return;
11326
+ }
11327
+ const cmd = argvParsed.positional[0];
11328
+ const parsed = { ...argvParsed, positional: argvParsed.positional.slice(1) };
11282
11329
  const { positional, apply: apply3, dryRun, noInstall } = parsed;
11283
11330
  const project = parsed.project ?? DEFAULT_PROJECT;
11284
11331
  if (cmd === "init") {
@@ -11292,12 +11339,12 @@ async function main() {
11292
11339
  console.error("neat init: --apply and --dry-run are mutually exclusive");
11293
11340
  process.exit(2);
11294
11341
  }
11295
- const scanPath = import_node_path48.default.resolve(target);
11342
+ const scanPath = import_node_path49.default.resolve(target);
11296
11343
  const projectExplicit = parsed.project !== null;
11297
- const projectName = projectExplicit ? project : import_node_path48.default.basename(scanPath);
11344
+ const projectName = projectExplicit ? project : import_node_path49.default.basename(scanPath);
11298
11345
  const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
11299
- const fallback = pathsForProject(projectKey, import_node_path48.default.join(scanPath, "neat-out")).snapshotPath;
11300
- const outPath = import_node_path48.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
11346
+ const fallback = pathsForProject(projectKey, import_node_path49.default.join(scanPath, "neat-out")).snapshotPath;
11347
+ const outPath = import_node_path49.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
11301
11348
  const result = await runInit({
11302
11349
  scanPath,
11303
11350
  outPath,
@@ -11318,21 +11365,21 @@ async function main() {
11318
11365
  usage();
11319
11366
  process.exit(2);
11320
11367
  }
11321
- const scanPath = import_node_path48.default.resolve(target);
11322
- const stat = await import_node_fs31.promises.stat(scanPath).catch(() => null);
11368
+ const scanPath = import_node_path49.default.resolve(target);
11369
+ const stat = await import_node_fs32.promises.stat(scanPath).catch(() => null);
11323
11370
  if (!stat || !stat.isDirectory()) {
11324
11371
  console.error(`neat watch: ${scanPath} is not a directory`);
11325
11372
  process.exit(2);
11326
11373
  }
11327
- const projectPaths = pathsForProject(project, import_node_path48.default.join(scanPath, "neat-out"));
11328
- const outPath = import_node_path48.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
11329
- const errorsPath = import_node_path48.default.resolve(
11330
- process.env.NEAT_ERRORS_PATH ?? import_node_path48.default.join(import_node_path48.default.dirname(outPath), import_node_path48.default.basename(projectPaths.errorsPath))
11374
+ const projectPaths = pathsForProject(project, import_node_path49.default.join(scanPath, "neat-out"));
11375
+ const outPath = import_node_path49.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
11376
+ const errorsPath = import_node_path49.default.resolve(
11377
+ process.env.NEAT_ERRORS_PATH ?? import_node_path49.default.join(import_node_path49.default.dirname(outPath), import_node_path49.default.basename(projectPaths.errorsPath))
11331
11378
  );
11332
- const staleEventsPath = import_node_path48.default.resolve(
11333
- process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path48.default.join(import_node_path48.default.dirname(outPath), import_node_path48.default.basename(projectPaths.staleEventsPath))
11379
+ const staleEventsPath = import_node_path49.default.resolve(
11380
+ process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path49.default.join(import_node_path49.default.dirname(outPath), import_node_path49.default.basename(projectPaths.staleEventsPath))
11334
11381
  );
11335
- const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path48.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
11382
+ const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path49.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
11336
11383
  const handle = await startWatch(getGraph(project), {
11337
11384
  scanPath,
11338
11385
  outPath,
@@ -11478,11 +11525,11 @@ async function main() {
11478
11525
  process.exit(1);
11479
11526
  }
11480
11527
  async function tryOrchestrator(cmd, parsed) {
11481
- const scanPath = import_node_path48.default.resolve(cmd);
11482
- const stat = await import_node_fs31.promises.stat(scanPath).catch(() => null);
11528
+ const scanPath = import_node_path49.default.resolve(cmd);
11529
+ const stat = await import_node_fs32.promises.stat(scanPath).catch(() => null);
11483
11530
  if (!stat || !stat.isDirectory()) return null;
11484
11531
  const projectExplicit = parsed.project !== null;
11485
- const projectName = projectExplicit ? parsed.project : import_node_path48.default.basename(scanPath);
11532
+ const projectName = projectExplicit ? parsed.project : import_node_path49.default.basename(scanPath);
11486
11533
  const result = await runOrchestrator({
11487
11534
  scanPath,
11488
11535
  project: projectName,
@@ -11683,11 +11730,15 @@ if (/[\\/]cli\.(?:cjs|js)$/.test(entry) || entry.endsWith("/cli") || entry.endsW
11683
11730
  0 && (module.exports = {
11684
11731
  CLAUDE_SKILL_CONFIG,
11685
11732
  QUERY_VERBS,
11733
+ commandPrefix,
11734
+ isNpxInvocation,
11735
+ main,
11686
11736
  parseArgs,
11687
11737
  printBanner,
11688
11738
  readPackageVersion,
11689
11739
  runInit,
11690
11740
  runQueryVerb,
11691
- runSkill
11741
+ runSkill,
11742
+ usage
11692
11743
  });
11693
11744
  //# sourceMappingURL=cli.cjs.map