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

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
@@ -61,8 +61,8 @@ function mountBearerAuth(app, opts) {
61
61
  ]);
62
62
  const publicRead = opts.publicRead === true;
63
63
  app.addHook("preHandler", (req, reply, done) => {
64
- const path99 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
- if (exactUnauthPaths.has(path99) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path99)) {
64
+ const path100 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
+ if (exactUnauthPaths.has(path100) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path100)) {
66
66
  done();
67
67
  return;
68
68
  }
@@ -415,8 +415,8 @@ function websocketChannelPathOf(attrs) {
415
415
  const v = attrs[key];
416
416
  if (typeof v === "string" && v.length > 0) {
417
417
  const q = v.indexOf("?");
418
- const path99 = q === -1 ? v : v.slice(0, q);
419
- if (path99.length > 0) return path99;
418
+ const path100 = q === -1 ? v : v.slice(0, q);
419
+ if (path100.length > 0) return path100;
420
420
  }
421
421
  }
422
422
  return void 0;
@@ -789,6 +789,7 @@ __export(cli_exports, {
789
789
  QUERY_VERBS: () => QUERY_VERBS,
790
790
  UnknownProfileError: () => UnknownProfileError,
791
791
  commandPrefix: () => commandPrefix,
792
+ isLoopbackEndpoint: () => isLoopbackEndpoint,
792
793
  isNpxInvocation: () => isNpxInvocation,
793
794
  main: () => main,
794
795
  parseArgs: () => parseArgs,
@@ -805,9 +806,9 @@ __export(cli_exports, {
805
806
  });
806
807
  module.exports = __toCommonJS(cli_exports);
807
808
  init_cjs_shims();
808
- var import_node_path99 = __toESM(require("path"), 1);
809
+ var import_node_path100 = __toESM(require("path"), 1);
809
810
  var import_node_os11 = __toESM(require("os"), 1);
810
- var import_node_fs60 = require("fs");
811
+ var import_node_fs61 = require("fs");
811
812
 
812
813
  // src/banner.ts
813
814
  init_cjs_shims();
@@ -1490,19 +1491,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1490
1491
  function longestIncomingWalk(graph, start, maxDepth) {
1491
1492
  let best = { path: [start], edges: [] };
1492
1493
  const visited = /* @__PURE__ */ new Set([start]);
1493
- function step(node, path99, edges) {
1494
- if (path99.length > best.path.length) {
1495
- best = { path: [...path99], edges: [...edges] };
1494
+ function step(node, path100, edges) {
1495
+ if (path100.length > best.path.length) {
1496
+ best = { path: [...path100], edges: [...edges] };
1496
1497
  }
1497
- if (path99.length - 1 >= maxDepth) return;
1498
+ if (path100.length - 1 >= maxDepth) return;
1498
1499
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1499
1500
  for (const [srcId, edge] of incoming) {
1500
1501
  if (visited.has(srcId)) continue;
1501
1502
  visited.add(srcId);
1502
- path99.push(srcId);
1503
+ path100.push(srcId);
1503
1504
  edges.push(edge);
1504
- step(srcId, path99, edges);
1505
- path99.pop();
1505
+ step(srcId, path100, edges);
1506
+ path100.pop();
1506
1507
  edges.pop();
1507
1508
  visited.delete(srcId);
1508
1509
  }
@@ -1717,20 +1718,20 @@ function dominantFailingCall(graph, serviceId17, visited) {
1717
1718
  return best;
1718
1719
  }
1719
1720
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1720
- const path99 = [originServiceId];
1721
+ const path100 = [originServiceId];
1721
1722
  const edges = [];
1722
1723
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1723
1724
  let current = originServiceId;
1724
1725
  for (let depth = 0; depth < maxDepth; depth++) {
1725
1726
  const hop = dominantFailingCall(graph, current, visited);
1726
1727
  if (!hop) break;
1727
- path99.push(hop.nextService);
1728
+ path100.push(hop.nextService);
1728
1729
  edges.push(hop.edge);
1729
1730
  visited.add(hop.nextService);
1730
1731
  current = hop.nextService;
1731
1732
  }
1732
1733
  if (edges.length === 0) return null;
1733
- return { path: path99, edges, culprit: current };
1734
+ return { path: path100, edges, culprit: current };
1734
1735
  }
1735
1736
  function isStaleCallEdge(e) {
1736
1737
  return e.type === import_types2.EdgeType.CALLS && e.provenance === import_types2.Provenance.STALE;
@@ -1766,26 +1767,26 @@ function dominantStaleCall(graph, serviceId17, visited) {
1766
1767
  return best;
1767
1768
  }
1768
1769
  function followStaleCallChain(graph, originServiceId, maxDepth) {
1769
- const path99 = [originServiceId];
1770
+ const path100 = [originServiceId];
1770
1771
  const edges = [];
1771
1772
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1772
1773
  let current = originServiceId;
1773
1774
  for (let depth = 0; depth < maxDepth; depth++) {
1774
1775
  const hop = dominantStaleCall(graph, current, visited);
1775
1776
  if (!hop) break;
1776
- path99.push(hop.nextService);
1777
+ path100.push(hop.nextService);
1777
1778
  edges.push(hop.edge);
1778
1779
  visited.add(hop.nextService);
1779
1780
  current = hop.nextService;
1780
1781
  }
1781
1782
  if (edges.length === 0) return null;
1782
- return { path: path99, edges, culprit: current };
1783
+ return { path: path100, edges, culprit: current };
1783
1784
  }
1784
1785
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1785
1786
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1786
1787
  if (!chain) return null;
1787
1788
  const culprit = chain.culprit;
1788
- const path99 = [...chain.path];
1789
+ const path100 = [...chain.path];
1789
1790
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1790
1791
  const baseConfidence = confidenceFromMix(chain.edges);
1791
1792
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1793,14 +1794,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1793
1794
  if (loc) {
1794
1795
  let rootCauseNode = culprit;
1795
1796
  if (loc.fileNode) {
1796
- path99.push(loc.fileNode);
1797
+ path100.push(loc.fileNode);
1797
1798
  edgeProvenances.push(import_types2.Provenance.OBSERVED);
1798
1799
  rootCauseNode = loc.fileNode;
1799
1800
  }
1800
1801
  return import_types2.RootCauseResultSchema.parse({
1801
1802
  rootCauseNode,
1802
1803
  rootCauseReason: loc.rootCauseReason,
1803
- traversalPath: path99,
1804
+ traversalPath: path100,
1804
1805
  edgeProvenances,
1805
1806
  confidence,
1806
1807
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1812,7 +1813,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1812
1813
  return import_types2.RootCauseResultSchema.parse({
1813
1814
  rootCauseNode: culprit,
1814
1815
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1815
- traversalPath: path99,
1816
+ traversalPath: path100,
1816
1817
  edgeProvenances,
1817
1818
  confidence,
1818
1819
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -2465,10 +2466,10 @@ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
2465
2466
  traversalPath = staleChain.path;
2466
2467
  edgeProvenances = staleChain.edges.map((e) => e.provenance);
2467
2468
  } else if (top.node !== seedNode) {
2468
- const path99 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
2469
- if (path99) {
2470
- traversalPath = path99.nodes;
2471
- edgeProvenances = path99.edges.map((e) => e.provenance);
2469
+ const path100 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
2470
+ if (path100) {
2471
+ traversalPath = path100.nodes;
2472
+ edgeProvenances = path100.edges.map((e) => e.provenance);
2472
2473
  } else {
2473
2474
  traversalPath = [errorNodeId, top.node];
2474
2475
  edgeProvenances = [top.provenance ?? import_types2.Provenance.OBSERVED];
@@ -3919,8 +3920,8 @@ function chiRoutesFromSource(source, parser) {
3919
3920
  chiWalk(tree.rootNode, "", out);
3920
3921
  return out;
3921
3922
  }
3922
- function stripChiRegex(path99) {
3923
- return path99.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3923
+ function stripChiRegex(path100) {
3924
+ return path100.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3924
3925
  }
3925
3926
  function chiWalk(node, prefix, out) {
3926
3927
  for (let i = 0; i < node.namedChildCount; i++) {
@@ -4592,9 +4593,9 @@ function rubyRocketRoute(args) {
4592
4593
  if (!pair || pair.type !== "pair") continue;
4593
4594
  const k = pair.childForFieldName("key");
4594
4595
  if (k?.type !== "string") continue;
4595
- const path99 = rubyLiteral(k);
4596
- if (path99 === null) continue;
4597
- return { path: path99, target: rubyLiteral(pair.childForFieldName("value")) };
4596
+ const path100 = rubyLiteral(k);
4597
+ if (path100 === null) continue;
4598
+ return { path: path100, target: rubyLiteral(pair.childForFieldName("value")) };
4598
4599
  }
4599
4600
  return null;
4600
4601
  }
@@ -20453,10 +20454,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
20453
20454
  // src/connectors/supabase/map.ts
20454
20455
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
20455
20456
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
20456
- function targetFromRestPath(path99) {
20457
- const rpcMatch = REST_RPC_PATH_RE.exec(path99);
20457
+ function targetFromRestPath(path100) {
20458
+ const rpcMatch = REST_RPC_PATH_RE.exec(path100);
20458
20459
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
20459
- const tableMatch = REST_TABLE_PATH_RE.exec(path99);
20460
+ const tableMatch = REST_TABLE_PATH_RE.exec(path100);
20460
20461
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
20461
20462
  return null;
20462
20463
  }
@@ -21060,9 +21061,9 @@ function parseFirebaseTargetName(targetName) {
21060
21061
  const secondSep = rest.indexOf(FIELD_SEP);
21061
21062
  if (secondSep === -1) return null;
21062
21063
  const method = rest.slice(0, secondSep);
21063
- const path99 = rest.slice(secondSep + 1);
21064
- if (!resourceName || !method || !path99) return null;
21065
- return { resourceName, method, path: path99 };
21064
+ const path100 = rest.slice(secondSep + 1);
21065
+ if (!resourceName || !method || !path100) return null;
21066
+ return { resourceName, method, path: path100 };
21066
21067
  }
21067
21068
  function resourceNameFor(type, labels) {
21068
21069
  if (!labels) return null;
@@ -21100,14 +21101,14 @@ function mapLogEntryToSignal(entry2) {
21100
21101
  if (!req) return null;
21101
21102
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
21102
21103
  const method = req.requestMethod.toUpperCase();
21103
- const path99 = pathFromRequestUrl(req.requestUrl);
21104
- if (path99 === null) return null;
21104
+ const path100 = pathFromRequestUrl(req.requestUrl);
21105
+ if (path100 === null) return null;
21105
21106
  const timestamp = entry2.timestamp;
21106
21107
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
21107
21108
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
21108
21109
  return {
21109
21110
  targetKind: resourceType,
21110
- targetName: packFirebaseTargetName({ resourceName, method, path: path99 }),
21111
+ targetName: packFirebaseTargetName({ resourceName, method, path: path100 }),
21111
21112
  callCount: 1,
21112
21113
  errorCount: isError ? 1 : 0,
21113
21114
  lastObservedIso: timestamp
@@ -21132,9 +21133,9 @@ var import_types73 = require("@neat.is/types");
21132
21133
  function normalizeName(name) {
21133
21134
  return name.toLowerCase().replace(/[^a-z0-9]/g, "");
21134
21135
  }
21135
- function inferServiceName(graph, resourceName, method, path99) {
21136
+ function inferServiceName(graph, resourceName, method, path100) {
21136
21137
  const wantedMethod = method.toUpperCase();
21137
- const wantedPath = normalizePathTemplate(path99);
21138
+ const wantedPath = normalizePathTemplate(path100);
21138
21139
  const services = [];
21139
21140
  const owners = /* @__PURE__ */ new Set();
21140
21141
  graph.forEachNode((_id, attrs) => {
@@ -21351,7 +21352,7 @@ function mapEventToSignal(event) {
21351
21352
  if (Number.isNaN(observedAt.getTime())) return null;
21352
21353
  const statusCode = metadata?.statusCode;
21353
21354
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
21354
- const path99 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
21355
+ const path100 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
21355
21356
  return {
21356
21357
  targetKind: CLOUDFLARE_TARGET_KIND,
21357
21358
  targetName: scriptName,
@@ -21359,7 +21360,7 @@ function mapEventToSignal(event) {
21359
21360
  errorCount: isError ? 1 : 0,
21360
21361
  lastObservedIso: observedAt.toISOString(),
21361
21362
  method,
21362
- ...path99 ? { path: path99 } : {},
21363
+ ...path100 ? { path: path100 } : {},
21363
21364
  ...typeof statusCode === "number" ? { statusCode } : {},
21364
21365
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
21365
21366
  };
@@ -21405,8 +21406,8 @@ function findTaggedWorkerFileNode(graph, workerName) {
21405
21406
  });
21406
21407
  return found;
21407
21408
  }
21408
- function findMatchingRouteNode(graph, serviceName, method, path99) {
21409
- const normalizedPath = normalizePathTemplate(path99);
21409
+ function findMatchingRouteNode(graph, serviceName, method, path100) {
21410
+ const normalizedPath = normalizePathTemplate(path100);
21410
21411
  let found = null;
21411
21412
  graph.forEachNode((id, attrs) => {
21412
21413
  if (found) return;
@@ -21423,10 +21424,10 @@ function createCloudflareResolveTarget(config, graph) {
21423
21424
  return (signal) => {
21424
21425
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
21425
21426
  const scriptName = signal.targetName;
21426
- const { method, path: path99 } = signal;
21427
+ const { method, path: path100 } = signal;
21427
21428
  const resolveRouteGrain = (serviceName, wholeFileId) => {
21428
- if (!method || !path99) return wholeFileId;
21429
- return findMatchingRouteNode(graph, serviceName, method, path99) ?? wholeFileId;
21429
+ if (!method || !path100) return wholeFileId;
21430
+ return findMatchingRouteNode(graph, serviceName, method, path100) ?? wholeFileId;
21430
21431
  };
21431
21432
  const mapping = config.workers?.[scriptName];
21432
21433
  if (mapping) {
@@ -21778,9 +21779,9 @@ function parseCloudRunTargetName(targetName) {
21778
21779
  const secondSep = rest.indexOf(FIELD_SEP2);
21779
21780
  if (secondSep === -1) return null;
21780
21781
  const method = rest.slice(0, secondSep);
21781
- const path99 = rest.slice(secondSep + 1);
21782
- if (!serviceName || !method || !path99) return null;
21783
- return { serviceName, method, path: path99 };
21782
+ const path100 = rest.slice(secondSep + 1);
21783
+ if (!serviceName || !method || !path100) return null;
21784
+ return { serviceName, method, path: path100 };
21784
21785
  }
21785
21786
 
21786
21787
  // src/connectors/cloud-run/map.ts
@@ -21809,14 +21810,14 @@ function mapLogEntryToSignal2(entry2) {
21809
21810
  if (!req) return null;
21810
21811
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
21811
21812
  const method = req.requestMethod.toUpperCase();
21812
- const path99 = pathFromRequestUrl2(req.requestUrl);
21813
- if (path99 === null) return null;
21813
+ const path100 = pathFromRequestUrl2(req.requestUrl);
21814
+ if (path100 === null) return null;
21814
21815
  const timestamp = entry2.timestamp;
21815
21816
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
21816
21817
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
21817
21818
  return {
21818
21819
  targetKind: CLOUD_RUN_TARGET_KIND,
21819
- targetName: packCloudRunTargetName({ serviceName, method, path: path99 }),
21820
+ targetName: packCloudRunTargetName({ serviceName, method, path: path100 }),
21820
21821
  callCount: 1,
21821
21822
  errorCount: isError ? 1 : 0,
21822
21823
  lastObservedIso: timestamp
@@ -21855,14 +21856,14 @@ function createCloudRunResolveTarget(graph, config) {
21855
21856
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
21856
21857
  const identity = parseCloudRunTargetName(signal.targetName);
21857
21858
  if (!identity) return null;
21858
- const { serviceName: gcpServiceName, method, path: path99 } = identity;
21859
- const mappedService = config.serviceMap?.[gcpServiceName] ?? (config.inferServices ? inferServiceName(graph, gcpServiceName, method, path99) ?? void 0 : void 0);
21859
+ const { serviceName: gcpServiceName, method, path: path100 } = identity;
21860
+ const mappedService = config.serviceMap?.[gcpServiceName] ?? (config.inferServices ? inferServiceName(graph, gcpServiceName, method, path100) ?? void 0 : void 0);
21860
21861
  if (mappedService) {
21861
21862
  const routeNodeId = findMatchingRouteNode2(
21862
21863
  graph,
21863
21864
  mappedService,
21864
21865
  method,
21865
- normalizePathTemplate(path99)
21866
+ normalizePathTemplate(path100)
21866
21867
  );
21867
21868
  if (routeNodeId) {
21868
21869
  return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types84.EdgeType.CALLS };
@@ -21998,9 +21999,9 @@ function parseGcpLbTargetName(targetName) {
21998
21999
  const secondSep = rest.indexOf(FIELD_SEP3);
21999
22000
  if (secondSep === -1) return null;
22000
22001
  const method = rest.slice(0, secondSep);
22001
- const path99 = rest.slice(secondSep + 1);
22002
- if (!backendServiceName || !method || !path99) return null;
22003
- return { backendServiceName, method, path: path99 };
22002
+ const path100 = rest.slice(secondSep + 1);
22003
+ if (!backendServiceName || !method || !path100) return null;
22004
+ return { backendServiceName, method, path: path100 };
22004
22005
  }
22005
22006
 
22006
22007
  // src/connectors/gcp-lb/map.ts
@@ -22029,14 +22030,14 @@ function mapLogEntryToSignal3(entry2) {
22029
22030
  if (!req) return null;
22030
22031
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
22031
22032
  const method = req.requestMethod.toUpperCase();
22032
- const path99 = pathFromRequestUrl3(req.requestUrl);
22033
- if (path99 === null) return null;
22033
+ const path100 = pathFromRequestUrl3(req.requestUrl);
22034
+ if (path100 === null) return null;
22034
22035
  const timestamp = entry2.timestamp;
22035
22036
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
22036
22037
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD5;
22037
22038
  return {
22038
22039
  targetKind: GCP_LB_TARGET_KIND,
22039
- targetName: packGcpLbTargetName({ backendServiceName, method, path: path99 }),
22040
+ targetName: packGcpLbTargetName({ backendServiceName, method, path: path100 }),
22040
22041
  callCount: 1,
22041
22042
  errorCount: isError ? 1 : 0,
22042
22043
  lastObservedIso: timestamp
@@ -22075,14 +22076,14 @@ function createGcpLbResolveTarget(graph, config) {
22075
22076
  if (signal.targetKind !== GCP_LB_TARGET_KIND) return null;
22076
22077
  const identity = parseGcpLbTargetName(signal.targetName);
22077
22078
  if (!identity) return null;
22078
- const { backendServiceName, method, path: path99 } = identity;
22079
- const mappedService = config.backendServiceMap?.[backendServiceName] ?? (config.inferServices ? inferServiceName(graph, backendServiceName, method, path99) ?? void 0 : void 0);
22079
+ const { backendServiceName, method, path: path100 } = identity;
22080
+ const mappedService = config.backendServiceMap?.[backendServiceName] ?? (config.inferServices ? inferServiceName(graph, backendServiceName, method, path100) ?? void 0 : void 0);
22080
22081
  if (mappedService) {
22081
22082
  const routeNodeId = findMatchingRouteNode3(
22082
22083
  graph,
22083
22084
  mappedService,
22084
22085
  method,
22085
- normalizePathTemplate(path99)
22086
+ normalizePathTemplate(path100)
22086
22087
  );
22087
22088
  if (routeNodeId) {
22088
22089
  return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types88.EdgeType.CALLS };
@@ -24474,9 +24475,9 @@ function makeK8sFetchImpl(transport) {
24474
24475
  req.end();
24475
24476
  }));
24476
24477
  }
24477
- async function listResource(transport, namespace, path99, opts = {}) {
24478
+ async function listResource(transport, namespace, path100, opts = {}) {
24478
24479
  const base = opts.apiUrl ?? transport.server;
24479
- const url = `${base.replace(/\/$/, "")}${path99}`;
24480
+ const url = `${base.replace(/\/$/, "")}${path100}`;
24480
24481
  const fetchImpl = opts.fetchImpl ?? makeK8sFetchImpl(transport);
24481
24482
  const res = await junctionFetch(
24482
24483
  url,
@@ -24486,7 +24487,7 @@ async function listResource(transport, namespace, path99, opts = {}) {
24486
24487
  { provider: "kubernetes", accountKey: `${safeHost(transport.server)}/${namespace}`, fetchImpl }
24487
24488
  );
24488
24489
  if (!res.ok) {
24489
- throw new Error(`kubernetes ${path99} failed: ${res.status} ${res.statusText}`);
24490
+ throw new Error(`kubernetes ${path100} failed: ${res.status} ${res.statusText}`);
24490
24491
  }
24491
24492
  const json = await res.json();
24492
24493
  return Array.isArray(json.items) ? json.items : [];
@@ -29377,7 +29378,8 @@ async function runOrchestrator(opts) {
29377
29378
  } catch (err) {
29378
29379
  if (!(err instanceof ProjectNameCollisionError)) throw err;
29379
29380
  console.error(`neat: ${err.message}`);
29380
- console.error("pass --project <other-name> to register under a different name.");
29381
+ console.error(`neat: run \`neat <path> --project <other-name>\` to register it under a different name,`);
29382
+ console.error(`neat: or \`neat uninstall ${opts.project}\` if the old registration is stale.`);
29381
29383
  result.exitCode = 1;
29382
29384
  return result;
29383
29385
  }
@@ -29956,12 +29958,12 @@ async function runConnectorCommand(rawArgs, deps = {}) {
29956
29958
 
29957
29959
  // src/hosted-connect-cli.ts
29958
29960
  init_cjs_shims();
29959
- var import_node_child_process5 = require("child_process");
29961
+ var import_node_child_process6 = require("child_process");
29960
29962
 
29961
29963
  // src/login-sso.ts
29962
29964
  init_cjs_shims();
29963
29965
  var import_node_http2 = require("http");
29964
- var import_node_child_process4 = require("child_process");
29966
+ var import_node_child_process5 = require("child_process");
29965
29967
  var import_node_crypto7 = require("crypto");
29966
29968
 
29967
29969
  // src/profiles.ts
@@ -30174,6 +30176,198 @@ async function clearActiveProfile(home = neatHome3()) {
30174
30176
  });
30175
30177
  }
30176
30178
 
30179
+ // src/daemon-probe.ts
30180
+ init_cjs_shims();
30181
+ var PROBE_ATTEMPT_TIMEOUT_MS = 3e4;
30182
+ var PROBE_TOTAL_BUDGET_MS = 12e4;
30183
+ var PROBE_RETRY_PAUSE_MS = 2e3;
30184
+ function isTimeoutError(err) {
30185
+ const name = err?.name;
30186
+ return name === "TimeoutError" || name === "AbortError";
30187
+ }
30188
+ async function probeOnce(fetchImpl, root, token, timeoutMs) {
30189
+ let res;
30190
+ try {
30191
+ res = await fetchImpl(`${root}/health`, {
30192
+ headers: { authorization: `Bearer ${token}` },
30193
+ signal: AbortSignal.timeout(timeoutMs)
30194
+ });
30195
+ } catch (err) {
30196
+ if (isTimeoutError(err)) return { kind: "timeout" };
30197
+ return { kind: "unreachable", detail: err.message };
30198
+ }
30199
+ if (res.status === 401 || res.status === 403) return { kind: "unauthorized", status: res.status };
30200
+ if (!res.ok) return { kind: "not-neat", status: res.status };
30201
+ const contentType = res.headers.get("content-type") ?? "";
30202
+ if (!contentType.includes("json")) return { kind: "not-neat", status: res.status };
30203
+ return { kind: "ok" };
30204
+ }
30205
+ async function probeDaemon(fetchImpl, endpoint2, token, hooks) {
30206
+ const root = endpoint2.replace(/\/$/, "");
30207
+ const deadline = hooks.now() + PROBE_TOTAL_BUDGET_MS;
30208
+ let warned = false;
30209
+ for (; ; ) {
30210
+ const result = await probeOnce(fetchImpl, root, token, PROBE_ATTEMPT_TIMEOUT_MS);
30211
+ if (result.kind !== "timeout") return result;
30212
+ if (!warned) {
30213
+ hooks.onWaiting();
30214
+ warned = true;
30215
+ }
30216
+ if (hooks.now() >= deadline) {
30217
+ return { kind: "unreachable", detail: `no response after ${Math.round(PROBE_TOTAL_BUDGET_MS / 1e3)}s` };
30218
+ }
30219
+ await hooks.sleep(PROBE_RETRY_PAUSE_MS);
30220
+ }
30221
+ }
30222
+
30223
+ // src/login-connect.ts
30224
+ init_cjs_shims();
30225
+ var import_node_child_process4 = require("child_process");
30226
+ var import_node_util = require("util");
30227
+ var import_node_fs54 = require("fs");
30228
+ var import_node_path92 = __toESM(require("path"), 1);
30229
+ var execFileAsync = (0, import_node_util.promisify)(import_node_child_process4.execFile);
30230
+ async function hasLocalSnapshot(cwd) {
30231
+ try {
30232
+ await import_node_fs54.promises.access(import_node_path92.default.join(cwd, "neat-out", "graph.json"));
30233
+ return true;
30234
+ } catch {
30235
+ return false;
30236
+ }
30237
+ }
30238
+ var POLL_INTERVAL_MS = 3e3;
30239
+ var POLL_BUDGET_MS = 10 * 6e4;
30240
+ var CP_TIMEOUT_MS = 1e4;
30241
+ async function detectGitHubRepo(cwd) {
30242
+ const read = async (args) => {
30243
+ try {
30244
+ const { stdout } = await execFileAsync("git", args, { cwd, timeout: 5e3 });
30245
+ return stdout.trim();
30246
+ } catch {
30247
+ return null;
30248
+ }
30249
+ };
30250
+ let url = await read(["remote", "get-url", "origin"]);
30251
+ if (!url) {
30252
+ const names = await read(["remote"]);
30253
+ const first = names?.split(/\r?\n/).find((l) => l.trim().length > 0)?.trim();
30254
+ if (!first) return null;
30255
+ url = await read(["remote", "get-url", first]);
30256
+ }
30257
+ if (!url) return null;
30258
+ return parseGitHubRemote(url);
30259
+ }
30260
+ function parseGitHubRemote(url) {
30261
+ const trimmed = url.trim().replace(/\.git$/, "");
30262
+ const m = trimmed.match(/^https?:\/\/(?:[^@/]+@)?github\.com\/([^/]+)\/([^/]+)$/) ?? trimmed.match(/^git@github\.com:([^/]+)\/([^/]+)$/) ?? trimmed.match(/^ssh:\/\/git@github\.com\/([^/]+)\/([^/]+)$/);
30263
+ if (!m) return null;
30264
+ const owner = m[1];
30265
+ const name = m[2];
30266
+ if (!owner || !name) return null;
30267
+ return { owner, name };
30268
+ }
30269
+ async function cpGetJson(fetchImpl, cpUrl, path100, accessToken) {
30270
+ try {
30271
+ const res = await fetchImpl(`${cpUrl}${path100}`, {
30272
+ headers: { authorization: `Bearer ${accessToken}`, accept: "application/json" },
30273
+ signal: AbortSignal.timeout(CP_TIMEOUT_MS)
30274
+ });
30275
+ if (!res.ok) return { ok: false };
30276
+ return { ok: true, body: await res.json() };
30277
+ } catch {
30278
+ return { ok: false };
30279
+ }
30280
+ }
30281
+ async function listBoundRepos(fetchImpl, cpUrl, accessToken, projectId) {
30282
+ const res = await cpGetJson(
30283
+ fetchImpl,
30284
+ cpUrl,
30285
+ `/me/projects/${encodeURIComponent(projectId)}/repos`,
30286
+ accessToken
30287
+ );
30288
+ if (!res.ok || !Array.isArray(res.body)) return null;
30289
+ return res.body;
30290
+ }
30291
+ async function waitForFirstProject(fetchImpl, cpUrl, accessToken, deps) {
30292
+ const pollMs = deps.pollMs ?? POLL_INTERVAL_MS;
30293
+ const deadline = deps.now() + (deps.budgetMs ?? POLL_BUDGET_MS);
30294
+ for (; ; ) {
30295
+ if (deps.signal?.aborted) return { kind: "aborted" };
30296
+ const me = await cpGetJson(fetchImpl, cpUrl, "/me", accessToken);
30297
+ if (me.ok) {
30298
+ const running = (me.body.projects ?? []).find((p) => p.status === "running");
30299
+ if (running) return { kind: "project", project: running };
30300
+ }
30301
+ if (deps.now() >= deadline) return { kind: "timeout" };
30302
+ await deps.sleep(pollMs);
30303
+ if (deps.signal?.aborted) return { kind: "aborted" };
30304
+ }
30305
+ }
30306
+ function bindUrl(webUrl, projectId) {
30307
+ return `${webUrl}/config/repos?project=${encodeURIComponent(projectId)}`;
30308
+ }
30309
+ function onboardingUrl(webUrl) {
30310
+ return `${webUrl}/onboarding`;
30311
+ }
30312
+ async function runPostLoginConnect(input) {
30313
+ const d = input.deps;
30314
+ const out = d.out ?? ((line) => console.log(line));
30315
+ const fetchImpl = d.fetchImpl ?? fetch;
30316
+ const cwd = d.cwd ?? process.cwd();
30317
+ const detect6 = d.detectRepo ?? detectGitHubRepo;
30318
+ const readLine = d.readLine;
30319
+ const repo = await detect6(cwd);
30320
+ out("");
30321
+ if (!repo) {
30322
+ out("This directory has no GitHub remote, so it can't be bound to the project.");
30323
+ out(` Push its graph instead: neat sync --to ${input.project.name}`);
30324
+ out(` Manage projects and repos: ${input.webUrl}`);
30325
+ return;
30326
+ }
30327
+ const slug = `${repo.owner}/${repo.name}`;
30328
+ const bound = await listBoundRepos(fetchImpl, input.cpUrl, input.accessToken, input.project.id);
30329
+ const match = bound?.find((r) => r.owner === repo.owner && r.name === repo.name);
30330
+ if (match) {
30331
+ const state = match.syncStatus ? ` (${match.syncStatus})` : "";
30332
+ out(`${slug} is bound to ${input.project.name}${state} \u2014 hosted NEAT extracts it for you.`);
30333
+ out(` See the graph: ${input.webUrl}`);
30334
+ return;
30335
+ }
30336
+ out(`${slug} isn't part of ${input.project.name} yet \u2014 logging in connected your account, not this repo.`);
30337
+ out("");
30338
+ const installed = input.me?.github?.installed;
30339
+ const bindLine = installed === false ? "1) Install the GitHub App and bind this repo \u2014 hosted NEAT keeps it in sync" : installed === true ? "1) Bind this repo \u2014 hosted NEAT clones and extracts it, and keeps it in sync" : "1) Bind this repo through the console \u2014 hosted NEAT keeps it in sync";
30340
+ out(bindLine);
30341
+ out(`2) Push the graph on this machine now \u2014 a snapshot, not a subscription`);
30342
+ out("3) Neither, for now");
30343
+ const answer = readLine ? (await readLine("Choose 1, 2 or 3 (Enter skips): "))?.trim() : void 0;
30344
+ if (answer === "1") {
30345
+ const url = bindUrl(input.webUrl, input.project.id);
30346
+ const opened = (d.openBrowser ?? (() => false))(url);
30347
+ out("");
30348
+ out(opened ? `Opened ${url}` : `Open this to bind the repo:
30349
+ ${url}`);
30350
+ return;
30351
+ }
30352
+ if (answer === "2") {
30353
+ const hasGraph = await (d.hasLocalGraph ?? hasLocalSnapshot)(cwd);
30354
+ out("");
30355
+ if (!hasGraph) {
30356
+ out("There is no local graph to push yet. Build one, then push it:");
30357
+ out(" neat");
30358
+ out(` neat sync --to ${input.project.name}`);
30359
+ return;
30360
+ }
30361
+ out("Push the local graph with:");
30362
+ out(` neat sync --to ${input.project.name}`);
30363
+ return;
30364
+ }
30365
+ out("");
30366
+ out("No problem \u2014 when you want this repo in the graph:");
30367
+ out(` Bind it: ${bindUrl(input.webUrl, input.project.id)}`);
30368
+ out(` Or push it: neat sync --to ${input.project.name}`);
30369
+ }
30370
+
30177
30371
  // src/login-sso.ts
30178
30372
  var DEFAULT_CP_URL = "https://api.neat.is";
30179
30373
  var DEFAULT_WEB_URL = "https://app.neat.is";
@@ -30199,6 +30393,7 @@ function pickProject(projects, want) {
30199
30393
  return {
30200
30394
  error: {
30201
30395
  code: 1,
30396
+ kind: "no-project",
30202
30397
  message: `no running project to connect to \u2014 create + provision one in the console first${listed}`
30203
30398
  }
30204
30399
  };
@@ -30249,14 +30444,14 @@ async function exchangeCredential(cpUrl, accessToken, opts, deps) {
30249
30444
  if (!cred.endpoint || !cred.authToken) {
30250
30445
  return { error: { code: 1, message: "the credential response was missing endpoint/authToken" } };
30251
30446
  }
30252
- return { project, cred };
30447
+ return { project, cred, me };
30253
30448
  }
30254
30449
  function defaultOpenBrowser(url) {
30255
30450
  const platform = process.platform;
30256
30451
  const cmd = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
30257
30452
  const args = platform === "win32" ? ["/c", "start", "", url] : [url];
30258
30453
  try {
30259
- const child = (0, import_node_child_process4.spawn)(cmd, args, { detached: true, stdio: "ignore" });
30454
+ const child = (0, import_node_child_process5.spawn)(cmd, args, { detached: true, stdio: "ignore" });
30260
30455
  child.on("error", () => {
30261
30456
  });
30262
30457
  child.unref();
@@ -30331,12 +30526,50 @@ async function runSsoLogin(opts, deps) {
30331
30526
  }
30332
30527
  accessToken = lb.token;
30333
30528
  }
30334
- const ex = await exchangeCredential(opts.cpUrl, accessToken, { project: opts.project }, deps);
30529
+ const fetchImpl = deps.fetchImpl ?? fetch;
30530
+ const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
30531
+ const now = deps.now ?? Date.now;
30532
+ let ex = await exchangeCredential(opts.cpUrl, accessToken, { project: opts.project }, deps);
30533
+ if ("error" in ex && ex.error.kind === "no-project" && !opts.json) {
30534
+ const url = onboardingUrl(opts.webUrl);
30535
+ out("");
30536
+ out("You don't have a project yet \u2014 let's make one.");
30537
+ const opened = (deps.openBrowser ?? defaultOpenBrowser)(url);
30538
+ out(opened ? `Opened ${url}` : `Open this to create your first project:
30539
+ ${url}`);
30540
+ out("Waiting for it to come up \u2014 Ctrl-C to stop and finish later.");
30541
+ const waited = await waitForFirstProject(fetchImpl, opts.cpUrl, accessToken, {
30542
+ sleep: sleep2,
30543
+ now,
30544
+ ...deps.signal ? { signal: deps.signal } : {}
30545
+ });
30546
+ if (waited.kind === "aborted") {
30547
+ out("Stopped. Run `neat login` again once your project is up.");
30548
+ return 130;
30549
+ }
30550
+ if (waited.kind === "timeout") {
30551
+ err("neat login: no project came up while waiting \u2014 run `neat login` again once it does");
30552
+ return 1;
30553
+ }
30554
+ out(`${waited.project.name} is up.`);
30555
+ ex = await exchangeCredential(opts.cpUrl, accessToken, { project: opts.project }, deps);
30556
+ }
30335
30557
  if ("error" in ex) {
30336
30558
  err(`neat login: ${ex.error.message}`);
30337
30559
  return ex.error.code;
30338
30560
  }
30339
- const { project, cred } = ex;
30561
+ const { project, cred, me } = ex;
30562
+ const probe = await probeDaemon(fetchImpl, cred.endpoint, cred.authToken, {
30563
+ sleep: sleep2,
30564
+ now,
30565
+ onWaiting: () => err("Waking the hosted daemon \u2014 a cold instance can take up to a minute\u2026")
30566
+ });
30567
+ if (probe.kind !== "ok") {
30568
+ const why = probe.kind === "unreachable" ? probe.detail : probe.kind === "unauthorized" ? `it rejected the credential (HTTP ${probe.status})` : `it answered HTTP ${probe.status} and doesn't look like NEAT`;
30569
+ err(`neat login: "${project.name}" is provisioned, but its daemon isn't answering \u2014 ${why}.`);
30570
+ err(`Nothing was changed. Check the project at ${opts.webUrl} and run \`neat login\` again.`);
30571
+ return probe.kind === "unreachable" ? 3 : 1;
30572
+ }
30340
30573
  await upsertProfile(
30341
30574
  { name: opts.name, endpoint: cred.endpoint, authToken: cred.authToken },
30342
30575
  { makeActive: true, ...deps.home ? { home: deps.home } : {} }
@@ -30364,10 +30597,20 @@ async function runSsoLogin(opts, deps) {
30364
30597
  out(` OTEL_EXPORTER_OTLP_ENDPOINT=${cred.ingestEndpoint}`);
30365
30598
  out(` OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer ${cred.otelToken}`);
30366
30599
  }
30367
- out("");
30368
- out("This directory isn't part of that graph yet \u2014 logging in connected your account,");
30369
- out("not this repo, and nothing here has been uploaded.");
30370
- out(` Manage projects and repos: ${opts.webUrl}`);
30600
+ await runPostLoginConnect({
30601
+ cpUrl: opts.cpUrl,
30602
+ webUrl: opts.webUrl,
30603
+ accessToken,
30604
+ project: { id: project.id, name: project.name },
30605
+ me,
30606
+ deps: {
30607
+ fetchImpl,
30608
+ out,
30609
+ ...deps.readLine ? { readLine: deps.readLine } : {},
30610
+ ...deps.openBrowser ? { openBrowser: deps.openBrowser } : { openBrowser: defaultOpenBrowser },
30611
+ ...deps.connect ?? {}
30612
+ }
30613
+ });
30371
30614
  out("");
30372
30615
  out("Run `neat logout` to switch back to your local daemon.");
30373
30616
  }
@@ -30380,7 +30623,7 @@ function defaultOpenBrowser2(url) {
30380
30623
  const cmd = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
30381
30624
  const args = platform === "win32" ? ["/c", "start", "", url] : [url];
30382
30625
  try {
30383
- const child = (0, import_node_child_process5.spawn)(cmd, args, { detached: true, stdio: "ignore" });
30626
+ const child = (0, import_node_child_process6.spawn)(cmd, args, { detached: true, stdio: "ignore" });
30384
30627
  child.on("error", () => {
30385
30628
  });
30386
30629
  child.unref();
@@ -30475,8 +30718,8 @@ async function runConnectCommand(rawArgs, deps = {}) {
30475
30718
 
30476
30719
  // src/doctor-cli.ts
30477
30720
  init_cjs_shims();
30478
- var import_node_fs54 = require("fs");
30479
- var import_node_path92 = __toESM(require("path"), 1);
30721
+ var import_node_fs55 = require("fs");
30722
+ var import_node_path93 = __toESM(require("path"), 1);
30480
30723
 
30481
30724
  // src/cli-client.ts
30482
30725
  init_cjs_shims();
@@ -30505,10 +30748,10 @@ function createHttpClient(baseUrl, bearerToken, fetchImpl = fetch) {
30505
30748
  const root = baseUrl.replace(/\/$/, "");
30506
30749
  const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
30507
30750
  return {
30508
- async get(path99) {
30751
+ async get(path100) {
30509
30752
  let res;
30510
30753
  try {
30511
- res = await fetchImpl(`${root}${path99}`, {
30754
+ res = await fetchImpl(`${root}${path100}`, {
30512
30755
  headers: { ...authHeader }
30513
30756
  });
30514
30757
  } catch (err) {
@@ -30520,16 +30763,16 @@ function createHttpClient(baseUrl, bearerToken, fetchImpl = fetch) {
30520
30763
  const body = await res.text().catch(() => "");
30521
30764
  throw new HttpError(
30522
30765
  res.status,
30523
- `${res.status} ${res.statusText} on GET ${path99}: ${body}`,
30766
+ `${res.status} ${res.statusText} on GET ${path100}: ${body}`,
30524
30767
  body
30525
30768
  );
30526
30769
  }
30527
30770
  return await res.json();
30528
30771
  },
30529
- async post(path99, body) {
30772
+ async post(path100, body) {
30530
30773
  let res;
30531
30774
  try {
30532
- res = await fetchImpl(`${root}${path99}`, {
30775
+ res = await fetchImpl(`${root}${path100}`, {
30533
30776
  method: "POST",
30534
30777
  headers: { "content-type": "application/json", ...authHeader },
30535
30778
  body: JSON.stringify(body)
@@ -30543,7 +30786,7 @@ function createHttpClient(baseUrl, bearerToken, fetchImpl = fetch) {
30543
30786
  const text = await res.text().catch(() => "");
30544
30787
  throw new HttpError(
30545
30788
  res.status,
30546
- `${res.status} ${res.statusText} on POST ${path99}: ${text}`,
30789
+ `${res.status} ${res.statusText} on POST ${path100}: ${text}`,
30547
30790
  text
30548
30791
  );
30549
30792
  }
@@ -30557,12 +30800,12 @@ function projectPath(project, suffix) {
30557
30800
  }
30558
30801
  async function runRootCause(client, input) {
30559
30802
  const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
30560
- const path99 = projectPath(
30803
+ const path100 = projectPath(
30561
30804
  input.project,
30562
30805
  `/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
30563
30806
  );
30564
30807
  try {
30565
- const result = await client.get(path99);
30808
+ const result = await client.get(path100);
30566
30809
  const arrowPath = result.traversalPath.join(" \u2190 ");
30567
30810
  const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
30568
30811
  const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
@@ -30588,12 +30831,12 @@ async function runRootCause(client, input) {
30588
30831
  }
30589
30832
  async function runBlastRadius(client, input) {
30590
30833
  const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
30591
- const path99 = projectPath(
30834
+ const path100 = projectPath(
30592
30835
  input.project,
30593
30836
  `/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
30594
30837
  );
30595
30838
  try {
30596
- const result = await client.get(path99);
30839
+ const result = await client.get(path100);
30597
30840
  if (result.totalAffected === 0) {
30598
30841
  return {
30599
30842
  summary: `${result.origin} has no dependents. Nothing else would break if it failed.`
@@ -30627,12 +30870,12 @@ function formatBlastEntry(n) {
30627
30870
  }
30628
30871
  async function runDependencies(client, input) {
30629
30872
  const depth = input.depth ?? 3;
30630
- const path99 = projectPath(
30873
+ const path100 = projectPath(
30631
30874
  input.project,
30632
30875
  `/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
30633
30876
  );
30634
30877
  try {
30635
- const result = await client.get(path99);
30878
+ const result = await client.get(path100);
30636
30879
  if (result.total === 0) {
30637
30880
  return {
30638
30881
  summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
@@ -30724,9 +30967,9 @@ function formatDuration(ms) {
30724
30967
  return `${Math.round(h / 24)}d`;
30725
30968
  }
30726
30969
  async function runIncidents(client, input) {
30727
- const path99 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
30970
+ const path100 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
30728
30971
  try {
30729
- const body = await client.get(path99);
30972
+ const body = await client.get(path100);
30730
30973
  const events = body.events;
30731
30974
  if (events.length === 0) {
30732
30975
  return {
@@ -31090,7 +31333,7 @@ function checkNode(nodeVersion) {
31090
31333
  }
31091
31334
  async function neatOutExists(cwd) {
31092
31335
  try {
31093
- const st = await import_node_fs54.promises.stat(import_node_path92.default.join(cwd, "neat-out"));
31336
+ const st = await import_node_fs55.promises.stat(import_node_path93.default.join(cwd, "neat-out"));
31094
31337
  return st.isDirectory();
31095
31338
  } catch {
31096
31339
  return false;
@@ -31210,9 +31453,6 @@ async function runDoctorCommand(argv, deps = {}) {
31210
31453
  // src/login-cli.ts
31211
31454
  init_cjs_shims();
31212
31455
  var import_promises4 = __toESM(require("readline/promises"), 1);
31213
- var PROBE_ATTEMPT_TIMEOUT_MS = 3e4;
31214
- var PROBE_TOTAL_BUDGET_MS = 12e4;
31215
- var PROBE_RETRY_PAUSE_MS = 2e3;
31216
31456
  var DEFAULT_PROFILE_NAME = "hosted";
31217
31457
  function readFlagValue(argv, i) {
31218
31458
  const arg = argv[i];
@@ -31277,44 +31517,6 @@ function printLoginHelp(out) {
31277
31517
  out(' the profile (default "hosted"); the paste token can also come from NEAT_LOGIN_TOKEN.');
31278
31518
  out(" Exit 0 on success, 1 rejected/needs-action, 2 misuse, 3 unreachable.");
31279
31519
  }
31280
- function isTimeoutError(err) {
31281
- const name = err?.name;
31282
- return name === "TimeoutError" || name === "AbortError";
31283
- }
31284
- async function probeOnce(fetchImpl, root, token, timeoutMs) {
31285
- let res;
31286
- try {
31287
- res = await fetchImpl(`${root}/health`, {
31288
- headers: { authorization: `Bearer ${token}` },
31289
- signal: AbortSignal.timeout(timeoutMs)
31290
- });
31291
- } catch (err) {
31292
- if (isTimeoutError(err)) return { kind: "timeout" };
31293
- return { kind: "unreachable", detail: err.message };
31294
- }
31295
- if (res.status === 401 || res.status === 403) return { kind: "unauthorized", status: res.status };
31296
- if (!res.ok) return { kind: "not-neat", status: res.status };
31297
- const contentType = res.headers.get("content-type") ?? "";
31298
- if (!contentType.includes("json")) return { kind: "not-neat", status: res.status };
31299
- return { kind: "ok" };
31300
- }
31301
- async function probeDaemon(fetchImpl, endpoint2, token, hooks) {
31302
- const root = endpoint2.replace(/\/$/, "");
31303
- const deadline = hooks.now() + PROBE_TOTAL_BUDGET_MS;
31304
- let warned = false;
31305
- for (; ; ) {
31306
- const result = await probeOnce(fetchImpl, root, token, PROBE_ATTEMPT_TIMEOUT_MS);
31307
- if (result.kind !== "timeout") return result;
31308
- if (!warned) {
31309
- hooks.onWaiting();
31310
- warned = true;
31311
- }
31312
- if (hooks.now() >= deadline) {
31313
- return { kind: "unreachable", detail: `no response after ${Math.round(PROBE_TOTAL_BUDGET_MS / 1e3)}s` };
31314
- }
31315
- await hooks.sleep(PROBE_RETRY_PAUSE_MS);
31316
- }
31317
- }
31318
31520
  async function runLoginCommand(argv, deps = {}) {
31319
31521
  const out = deps.out ?? ((line) => console.log(line));
31320
31522
  const err = deps.err ?? ((line) => console.error(line));
@@ -31347,8 +31549,13 @@ async function runLoginCommand(argv, deps = {}) {
31347
31549
  fetchImpl,
31348
31550
  out,
31349
31551
  err,
31552
+ readLine,
31553
+ sleep: sleep2,
31554
+ now,
31350
31555
  ...deps.openBrowser ? { openBrowser: deps.openBrowser } : {},
31351
- ...deps.home ? { home: deps.home } : {}
31556
+ ...deps.home ? { home: deps.home } : {},
31557
+ ...deps.signal ? { signal: deps.signal } : {},
31558
+ ...deps.connect ? { connect: deps.connect } : {}
31352
31559
  }
31353
31560
  );
31354
31561
  }
@@ -31505,8 +31712,8 @@ async function defaultReadSecret(prompt) {
31505
31712
 
31506
31713
  // src/welcome.ts
31507
31714
  init_cjs_shims();
31508
- var import_node_fs55 = require("fs");
31509
- var import_node_path93 = __toESM(require("path"), 1);
31715
+ var import_node_fs56 = require("fs");
31716
+ var import_node_path94 = __toESM(require("path"), 1);
31510
31717
  var import_promises5 = __toESM(require("readline/promises"), 1);
31511
31718
  var WORDMARK = [
31512
31719
  "\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",
@@ -31565,15 +31772,17 @@ async function runFrontDoor(deps, out) {
31565
31772
  const login = deps.login ?? (() => Promise.resolve(0));
31566
31773
  const orchestrator = deps.orchestrator ?? (() => Promise.resolve(0));
31567
31774
  const cwd = deps.cwd ?? process.cwd();
31775
+ const flagGiven = deps.instrumentFlagGiven ?? false;
31776
+ const readRegistry2 = deps.readRegistry ?? (() => readRegistry());
31568
31777
  printHeader(out);
31569
31778
  out("Welcome to NEAT. Let's get you a graph of this system.");
31570
31779
  out("");
31571
31780
  const keyReader = deps.readKey !== void 0 ? { read: deps.readKey, done: () => {
31572
31781
  } } : createKeyReader();
31573
31782
  const picked = keyReader !== null ? await selectOption(out, keyReader, deps.moveCursorUp ?? defaultMoveCursorUp) : await selectByNumber(out, readLine);
31574
- if (picked === void 0) return runSelfHosted(out, readLine, orchestrator, cwd);
31783
+ if (picked === void 0) return runSelfHosted(out, readLine, orchestrator, cwd, readRegistry2, flagGiven);
31575
31784
  if (picked === 0) return login(["--browser"]);
31576
- return runSelfHosted(out, readLine, orchestrator, cwd);
31785
+ return runSelfHosted(out, readLine, orchestrator, cwd, readRegistry2, flagGiven);
31577
31786
  }
31578
31787
  var MENU_OPTIONS = [
31579
31788
  "Log me into Hosted Neat",
@@ -31676,7 +31885,7 @@ function decodeKey(buf) {
31676
31885
  function defaultMoveCursorUp(rows) {
31677
31886
  if (rows > 0) process.stdout.write(`\x1B[${rows}A`);
31678
31887
  }
31679
- async function runSelfHosted(out, readLine, orchestrator, cwd) {
31888
+ async function runSelfHosted(out, readLine, orchestrator, cwd, readRegistry2, instrumentFlagGiven) {
31680
31889
  const wantsPrompt = (await readLine("Print a copy-paste setup prompt for your coding agent? [Y/n]: "))?.trim().toLowerCase();
31681
31890
  if (wantsPrompt !== "n" && wantsPrompt !== "no") {
31682
31891
  out("");
@@ -31687,9 +31896,69 @@ async function runSelfHosted(out, readLine, orchestrator, cwd) {
31687
31896
  out("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
31688
31897
  out("");
31689
31898
  }
31899
+ const project = await resolveProjectName(out, readLine, cwd, readRegistry2);
31900
+ const declined = instrumentFlagGiven ? void 0 : await askToInstrument(out, readLine);
31690
31901
  out("Building your local graph now\u2026");
31691
31902
  out("");
31692
- return orchestrator(cwd);
31903
+ const overrides = {
31904
+ ...project !== void 0 ? { project } : {},
31905
+ ...declined === void 0 ? {} : declined ? { noInstrument: true } : { yes: true }
31906
+ };
31907
+ return orchestrator(cwd, Object.keys(overrides).length > 0 ? overrides : void 0);
31908
+ }
31909
+ async function askToInstrument(out, readLine) {
31910
+ out("Instrument the services for OpenTelemetry now?");
31911
+ out("This edits package.json / requirements.txt / go.mod and runs the package manager.");
31912
+ const answer = (await readLine("[Y/n]: "))?.trim().toLowerCase();
31913
+ const declined = answer === "n" || answer === "no";
31914
+ if (declined) {
31915
+ out("");
31916
+ out("Skipping instrumentation \u2014 the graph will hold the declared side only.");
31917
+ out("Run `neat init . --apply` when you want the runtime half.");
31918
+ }
31919
+ out("");
31920
+ return declined;
31921
+ }
31922
+ async function resolveProjectName(out, readLine, cwd, readRegistry2) {
31923
+ const here = import_node_path94.default.resolve(cwd);
31924
+ const base = import_node_path94.default.basename(here);
31925
+ let taken;
31926
+ try {
31927
+ const { projects } = await readRegistry2();
31928
+ taken = new Map(projects.map((p) => [p.name, p.path]));
31929
+ } catch {
31930
+ return void 0;
31931
+ }
31932
+ const clash = taken.get(base);
31933
+ if (clash === void 0 || import_node_path94.default.resolve(clash) === here) return void 0;
31934
+ out("");
31935
+ out(`A project named \`${base}\` is already registered (${clash}).`);
31936
+ const suggested = firstFreeName(base, taken);
31937
+ for (; ; ) {
31938
+ const answer = (await readLine(`Name this one: [${suggested}] `))?.trim();
31939
+ if (answer === void 0) return suggested;
31940
+ const chosen = answer.length === 0 ? suggested : answer;
31941
+ if (!isUsableProjectName(chosen)) {
31942
+ out(`"${chosen}" won't work as a project name \u2014 letters, digits, dot, dash and underscore.`);
31943
+ continue;
31944
+ }
31945
+ const other = taken.get(chosen);
31946
+ if (other !== void 0 && import_node_path94.default.resolve(other) !== here) {
31947
+ out(`\`${chosen}\` is registered too (${other}).`);
31948
+ continue;
31949
+ }
31950
+ out("");
31951
+ return chosen;
31952
+ }
31953
+ }
31954
+ function firstFreeName(base, taken) {
31955
+ for (let n = 2; ; n++) {
31956
+ const candidate = `${base}-${n}`;
31957
+ if (!taken.has(candidate)) return candidate;
31958
+ }
31959
+ }
31960
+ function isUsableProjectName(name) {
31961
+ return name.length > 0 && name !== "." && name !== ".." && /^[A-Za-z0-9._-]+$/.test(name);
31693
31962
  }
31694
31963
  async function shouldShowWelcome(deps = {}) {
31695
31964
  const stdinTTY = deps.stdinIsTTY ?? Boolean(process.stdin.isTTY);
@@ -31699,9 +31968,9 @@ async function shouldShowWelcome(deps = {}) {
31699
31968
  const readRegistry2 = deps.readRegistry ?? (() => readRegistry());
31700
31969
  const hasSnapshot = deps.hasSnapshot ?? defaultHasSnapshot;
31701
31970
  try {
31702
- const here = import_node_path93.default.resolve(cwd);
31971
+ const here = import_node_path94.default.resolve(cwd);
31703
31972
  const { projects } = await readRegistry2();
31704
- if (projects.some((p) => import_node_path93.default.resolve(p.path) === here)) return false;
31973
+ if (projects.some((p) => import_node_path94.default.resolve(p.path) === here)) return false;
31705
31974
  if (await hasSnapshot(here)) return false;
31706
31975
  return true;
31707
31976
  } catch {
@@ -31710,7 +31979,7 @@ async function shouldShowWelcome(deps = {}) {
31710
31979
  }
31711
31980
  async function defaultHasSnapshot(cwd) {
31712
31981
  try {
31713
- const entries = await import_node_fs55.promises.readdir(import_node_path93.default.join(cwd, "neat-out"));
31982
+ const entries = await import_node_fs56.promises.readdir(import_node_path94.default.join(cwd, "neat-out"));
31714
31983
  return entries.some((e) => e.endsWith(".json"));
31715
31984
  } catch {
31716
31985
  return false;
@@ -31728,27 +31997,27 @@ async function defaultReadLine2(prompt) {
31728
31997
 
31729
31998
  // src/hooks-cli.ts
31730
31999
  init_cjs_shims();
31731
- var import_node_path94 = __toESM(require("path"), 1);
32000
+ var import_node_path95 = __toESM(require("path"), 1);
31732
32001
  var import_node_os8 = __toESM(require("os"), 1);
31733
- var import_node_fs56 = require("fs");
32002
+ var import_node_fs57 = require("fs");
31734
32003
  var import_node_url5 = require("url");
31735
32004
  var HOOK_FILENAME = "neat-search-nudge.mjs";
31736
32005
  var GUIDE_FILENAME = "GRAPH_FIRST.md";
31737
32006
  var GUIDE_INSTALL_NAME = "neat-graph-first.md";
31738
32007
  var HOOK_MATCHER = "Grep|Glob|Bash";
31739
32008
  function moduleDir() {
31740
- return typeof __dirname !== "undefined" ? __dirname : import_node_path94.default.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl));
32009
+ return typeof __dirname !== "undefined" ? __dirname : import_node_path95.default.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl));
31741
32010
  }
31742
32011
  async function readSkillAsset(rel) {
31743
32012
  const here = moduleDir();
31744
32013
  const candidates = [
31745
- import_node_path94.default.resolve(here, "../../claude-skill", rel),
31746
- import_node_path94.default.resolve(here, "../../../claude-skill", rel),
31747
- import_node_path94.default.resolve(here, "../claude-skill", rel)
32014
+ import_node_path95.default.resolve(here, "../../claude-skill", rel),
32015
+ import_node_path95.default.resolve(here, "../../../claude-skill", rel),
32016
+ import_node_path95.default.resolve(here, "../claude-skill", rel)
31748
32017
  ];
31749
32018
  for (const candidate of candidates) {
31750
32019
  try {
31751
- return await import_node_fs56.promises.readFile(candidate, "utf8");
32020
+ return await import_node_fs57.promises.readFile(candidate, "utf8");
31752
32021
  } catch {
31753
32022
  }
31754
32023
  }
@@ -31758,20 +32027,20 @@ async function readSkillAsset(rel) {
31758
32027
  }
31759
32028
  function neatHome4() {
31760
32029
  const override = process.env.NEAT_HOME;
31761
- if (override && override.length > 0) return import_node_path94.default.resolve(override);
31762
- return import_node_path94.default.join(import_node_os8.default.homedir(), ".neat");
32030
+ if (override && override.length > 0) return import_node_path95.default.resolve(override);
32031
+ return import_node_path95.default.join(import_node_os8.default.homedir(), ".neat");
31763
32032
  }
31764
32033
  function claudeSettingsPath() {
31765
32034
  const override = process.env.NEAT_CLAUDE_SETTINGS;
31766
- if (override && override.length > 0) return import_node_path94.default.resolve(override);
32035
+ if (override && override.length > 0) return import_node_path95.default.resolve(override);
31767
32036
  const home = process.env.HOME ?? process.env.USERPROFILE ?? import_node_os8.default.homedir();
31768
- return import_node_path94.default.join(home, ".claude", "settings.json");
32037
+ return import_node_path95.default.join(home, ".claude", "settings.json");
31769
32038
  }
31770
32039
  function installedHookPath() {
31771
- return import_node_path94.default.join(neatHome4(), "hooks", HOOK_FILENAME);
32040
+ return import_node_path95.default.join(neatHome4(), "hooks", HOOK_FILENAME);
31772
32041
  }
31773
32042
  function gateFlagPath() {
31774
- return import_node_path94.default.join(neatHome4(), "hooks", "gate-enabled");
32043
+ return import_node_path95.default.join(neatHome4(), "hooks", "gate-enabled");
31775
32044
  }
31776
32045
  function isNeatSearchEntry(entry2) {
31777
32046
  return (entry2.hooks ?? []).some(
@@ -31804,14 +32073,14 @@ async function runHooks(opts) {
31804
32073
  const hookScript = await readSkillAsset(`hooks/${HOOK_FILENAME}`);
31805
32074
  const guide = await readSkillAsset(GUIDE_FILENAME);
31806
32075
  const scriptPath = installedHookPath();
31807
- await import_node_fs56.promises.mkdir(import_node_path94.default.dirname(scriptPath), { recursive: true });
31808
- await import_node_fs56.promises.writeFile(scriptPath, hookScript, { mode: 493 });
31809
- const guidePath = import_node_path94.default.join(neatHome4(), GUIDE_INSTALL_NAME);
31810
- await import_node_fs56.promises.writeFile(guidePath, guide, "utf8");
32076
+ await import_node_fs57.promises.mkdir(import_node_path95.default.dirname(scriptPath), { recursive: true });
32077
+ await import_node_fs57.promises.writeFile(scriptPath, hookScript, { mode: 493 });
32078
+ const guidePath = import_node_path95.default.join(neatHome4(), GUIDE_INSTALL_NAME);
32079
+ await import_node_fs57.promises.writeFile(guidePath, guide, "utf8");
31811
32080
  const settingsFile = claudeSettingsPath();
31812
32081
  let settings = {};
31813
32082
  try {
31814
- settings = JSON.parse(await import_node_fs56.promises.readFile(settingsFile, "utf8"));
32083
+ settings = JSON.parse(await import_node_fs57.promises.readFile(settingsFile, "utf8"));
31815
32084
  } catch (err) {
31816
32085
  if (err.code !== "ENOENT") {
31817
32086
  console.error(
@@ -31833,14 +32102,14 @@ async function runHooks(opts) {
31833
32102
  ...settings,
31834
32103
  hooks: { ...hooks, PreToolUse: preToolUse }
31835
32104
  };
31836
- await import_node_fs56.promises.mkdir(import_node_path94.default.dirname(settingsFile), { recursive: true });
31837
- await import_node_fs56.promises.writeFile(settingsFile, JSON.stringify(merged, null, 2) + "\n", "utf8");
32105
+ await import_node_fs57.promises.mkdir(import_node_path95.default.dirname(settingsFile), { recursive: true });
32106
+ await import_node_fs57.promises.writeFile(settingsFile, JSON.stringify(merged, null, 2) + "\n", "utf8");
31838
32107
  const flag = gateFlagPath();
31839
32108
  if (opts.gate) {
31840
- await import_node_fs56.promises.mkdir(import_node_path94.default.dirname(flag), { recursive: true });
31841
- await import_node_fs56.promises.writeFile(flag, "1\n", "utf8");
32109
+ await import_node_fs57.promises.mkdir(import_node_path95.default.dirname(flag), { recursive: true });
32110
+ await import_node_fs57.promises.writeFile(flag, "1\n", "utf8");
31842
32111
  } else {
31843
- await import_node_fs56.promises.rm(flag, { force: true });
32112
+ await import_node_fs57.promises.rm(flag, { force: true });
31844
32113
  }
31845
32114
  const mode = opts.gate ? "GATE (deny search until you ask the graph)" : "nudge (search still runs)";
31846
32115
  console.log(`neat hooks: installed the search hook in ${opts.gate ? "gate" : "nudge"} mode`);
@@ -31930,8 +32199,8 @@ async function runHooksCommand(args) {
31930
32199
 
31931
32200
  // src/claude-cli.ts
31932
32201
  init_cjs_shims();
31933
- var import_node_path95 = __toESM(require("path"), 1);
31934
- var import_node_fs57 = require("fs");
32202
+ var import_node_path96 = __toESM(require("path"), 1);
32203
+ var import_node_fs58 = require("fs");
31935
32204
  var NEAT_SECTION_HEADING = "## neat";
31936
32205
  var NEAT_DIRECTIVE_BODY = `This project has NEAT wired in: a live, fused semantic graph of the system \u2014
31937
32206
  code and runtime behaviour (OpenTelemetry) in one model, every fact tagged with
@@ -31965,8 +32234,8 @@ ${NEAT_DIRECTIVE_BODY}
31965
32234
  }
31966
32235
  function claudeMdPath() {
31967
32236
  const override = process.env.NEAT_CLAUDE_MD;
31968
- if (override && override.length > 0) return import_node_path95.default.resolve(override);
31969
- return import_node_path95.default.join(process.cwd(), "CLAUDE.md");
32237
+ if (override && override.length > 0) return import_node_path96.default.resolve(override);
32238
+ return import_node_path96.default.join(process.cwd(), "CLAUDE.md");
31970
32239
  }
31971
32240
  function splitAroundSection(raw) {
31972
32241
  const lines = raw.split("\n");
@@ -31994,7 +32263,7 @@ function compose(before, after) {
31994
32263
  }
31995
32264
  async function readIfExists2(file) {
31996
32265
  try {
31997
- return await import_node_fs57.promises.readFile(file, "utf8");
32266
+ return await import_node_fs58.promises.readFile(file, "utf8");
31998
32267
  } catch (err) {
31999
32268
  if (err.code === "ENOENT") return null;
32000
32269
  throw err;
@@ -32005,8 +32274,8 @@ async function runInstall() {
32005
32274
  const raw = await readIfExists2(file) ?? "";
32006
32275
  const { before, after, found } = splitAroundSection(raw);
32007
32276
  const next = compose(before, after);
32008
- await import_node_fs57.promises.mkdir(import_node_path95.default.dirname(file), { recursive: true });
32009
- await import_node_fs57.promises.writeFile(file, next, "utf8");
32277
+ await import_node_fs58.promises.mkdir(import_node_path96.default.dirname(file), { recursive: true });
32278
+ await import_node_fs58.promises.writeFile(file, next, "utf8");
32010
32279
  const verb = raw.length === 0 ? "created" : found ? "refreshed" : "added";
32011
32280
  console.log(`neat claude: ${verb} the \`${NEAT_SECTION_HEADING}\` section in ${file}`);
32012
32281
  console.log("Your agent will now reach for `neat ask` before Read/Grep/Bash. Restart the");
@@ -32027,7 +32296,7 @@ async function runUninstall() {
32027
32296
  }
32028
32297
  const remaining = [before, after].filter((s) => s.length > 0).join("\n\n");
32029
32298
  const next = remaining.length > 0 ? remaining.replace(/\n*$/, "") + "\n" : "";
32030
- await import_node_fs57.promises.writeFile(file, next, "utf8");
32299
+ await import_node_fs58.promises.writeFile(file, next, "utf8");
32031
32300
  console.log(`neat claude: removed the \`${NEAT_SECTION_HEADING}\` section from ${file}.`);
32032
32301
  return { exitCode: 0 };
32033
32302
  }
@@ -32070,10 +32339,10 @@ async function runClaudeCommand(args) {
32070
32339
 
32071
32340
  // src/codex-cli.ts
32072
32341
  init_cjs_shims();
32073
- var import_node_path96 = __toESM(require("path"), 1);
32342
+ var import_node_path97 = __toESM(require("path"), 1);
32074
32343
  var import_node_os9 = __toESM(require("os"), 1);
32075
- var import_node_fs58 = require("fs");
32076
- var import_node_util = require("util");
32344
+ var import_node_fs59 = require("fs");
32345
+ var import_node_util2 = require("util");
32077
32346
  var import_smol_toml6 = require("smol-toml");
32078
32347
  var CODEX_MCP_SERVER = {
32079
32348
  command: "npx",
@@ -32090,14 +32359,14 @@ var NEAT_GRAPH_FIRST_START = "<!-- neat:graph-first -->";
32090
32359
  var NEAT_GRAPH_FIRST_END = "<!-- /neat:graph-first -->";
32091
32360
  function codexConfigPath() {
32092
32361
  const override = process.env.NEAT_CODEX_CONFIG;
32093
- if (override && override.length > 0) return import_node_path96.default.resolve(override);
32362
+ if (override && override.length > 0) return import_node_path97.default.resolve(override);
32094
32363
  const home = process.env.HOME ?? process.env.USERPROFILE ?? import_node_os9.default.homedir();
32095
- return import_node_path96.default.join(home, ".codex", "config.toml");
32364
+ return import_node_path97.default.join(home, ".codex", "config.toml");
32096
32365
  }
32097
32366
  function agentsFilePath() {
32098
32367
  const override = process.env.NEAT_CODEX_AGENTS;
32099
- if (override && override.length > 0) return import_node_path96.default.resolve(override);
32100
- return import_node_path96.default.join(process.cwd(), "AGENTS.md");
32368
+ if (override && override.length > 0) return import_node_path97.default.resolve(override);
32369
+ return import_node_path97.default.join(process.cwd(), "AGENTS.md");
32101
32370
  }
32102
32371
  function isTableHeader(line) {
32103
32372
  return /^\s*\[\[?[^\]]+\]\]?\s*$/.test(line);
@@ -32174,17 +32443,17 @@ function verifyPreserved(raw, spliced, original) {
32174
32443
  }
32175
32444
  const origServers = original.mcp_servers ?? {};
32176
32445
  const nextServers = next.mcp_servers ?? {};
32177
- if (!(0, import_node_util.isDeepStrictEqual)(nextServers.neat, CODEX_MCP_SERVER)) return false;
32446
+ if (!(0, import_node_util2.isDeepStrictEqual)(nextServers.neat, CODEX_MCP_SERVER)) return false;
32178
32447
  for (const name of Object.keys(origServers)) {
32179
32448
  if (name === "neat") continue;
32180
- if (!(0, import_node_util.isDeepStrictEqual)(nextServers[name], origServers[name])) return false;
32449
+ if (!(0, import_node_util2.isDeepStrictEqual)(nextServers[name], origServers[name])) return false;
32181
32450
  }
32182
32451
  for (const name of Object.keys(nextServers)) {
32183
32452
  if (name !== "neat" && !(name in origServers)) return false;
32184
32453
  }
32185
32454
  for (const key of Object.keys(original)) {
32186
32455
  if (key === "mcp_servers") continue;
32187
- if (!(0, import_node_util.isDeepStrictEqual)(next[key], original[key])) return false;
32456
+ if (!(0, import_node_util2.isDeepStrictEqual)(next[key], original[key])) return false;
32188
32457
  }
32189
32458
  for (const key of Object.keys(next)) {
32190
32459
  if (key !== "mcp_servers" && !(key in original)) return false;
@@ -32231,7 +32500,7 @@ async function runCodex(opts) {
32231
32500
  const agentsPath = agentsFilePath();
32232
32501
  let configRaw = "";
32233
32502
  try {
32234
- configRaw = await import_node_fs58.promises.readFile(configPath, "utf8");
32503
+ configRaw = await import_node_fs59.promises.readFile(configPath, "utf8");
32235
32504
  } catch (err) {
32236
32505
  if (err.code !== "ENOENT") {
32237
32506
  console.error(`neat codex: failed to read ${configPath} \u2014 ${err.message}`);
@@ -32240,7 +32509,7 @@ async function runCodex(opts) {
32240
32509
  }
32241
32510
  let agentsRaw = "";
32242
32511
  try {
32243
- agentsRaw = await import_node_fs58.promises.readFile(agentsPath, "utf8");
32512
+ agentsRaw = await import_node_fs59.promises.readFile(agentsPath, "utf8");
32244
32513
  } catch (err) {
32245
32514
  if (err.code !== "ENOENT") {
32246
32515
  console.error(`neat codex: failed to read ${agentsPath} \u2014 ${err.message}`);
@@ -32280,15 +32549,15 @@ async function runCodex(opts) {
32280
32549
  return { exitCode: 0 };
32281
32550
  }
32282
32551
  if (config.changed) {
32283
- await import_node_fs58.promises.mkdir(import_node_path96.default.dirname(configPath), { recursive: true });
32284
- await import_node_fs58.promises.writeFile(configPath, config.text, "utf8");
32552
+ await import_node_fs59.promises.mkdir(import_node_path97.default.dirname(configPath), { recursive: true });
32553
+ await import_node_fs59.promises.writeFile(configPath, config.text, "utf8");
32285
32554
  console.log(`neat codex: wrote [mcp_servers.neat] to ${configPath}`);
32286
32555
  } else {
32287
32556
  console.log(`neat codex: ${configPath} already has NEAT's MCP server`);
32288
32557
  }
32289
32558
  if (agents.changed) {
32290
- await import_node_fs58.promises.mkdir(import_node_path96.default.dirname(agentsPath), { recursive: true });
32291
- await import_node_fs58.promises.writeFile(agentsPath, agents.text, "utf8");
32559
+ await import_node_fs59.promises.mkdir(import_node_path97.default.dirname(agentsPath), { recursive: true });
32560
+ await import_node_fs59.promises.writeFile(agentsPath, agents.text, "utf8");
32292
32561
  console.log(`neat codex: wrote the graph-first block to ${agentsPath}`);
32293
32562
  } else {
32294
32563
  console.log(`neat codex: ${agentsPath} already has the graph-first block`);
@@ -32345,10 +32614,10 @@ async function runCodexCommand(args) {
32345
32614
 
32346
32615
  // src/editors-cli.ts
32347
32616
  init_cjs_shims();
32348
- var import_node_path97 = __toESM(require("path"), 1);
32617
+ var import_node_path98 = __toESM(require("path"), 1);
32349
32618
  var import_node_os10 = __toESM(require("os"), 1);
32350
- var import_node_fs59 = require("fs");
32351
- var import_node_util2 = require("util");
32619
+ var import_node_fs60 = require("fs");
32620
+ var import_node_util3 = require("util");
32352
32621
  var jsonc = __toESM(require("jsonc-parser"), 1);
32353
32622
  var NEAT_MCP_SERVER = {
32354
32623
  command: "npx",
@@ -32371,17 +32640,17 @@ function homeDir() {
32371
32640
  }
32372
32641
  function xdgConfigDir() {
32373
32642
  const xdg = process.env.XDG_CONFIG_HOME;
32374
- return xdg && xdg.length > 0 ? import_node_path97.default.resolve(xdg) : import_node_path97.default.join(homeDir(), ".config");
32643
+ return xdg && xdg.length > 0 ? import_node_path98.default.resolve(xdg) : import_node_path98.default.join(homeDir(), ".config");
32375
32644
  }
32376
32645
  function envOverride(name) {
32377
32646
  const v = process.env[name];
32378
- return v && v.length > 0 ? import_node_path97.default.resolve(v) : void 0;
32647
+ return v && v.length > 0 ? import_node_path98.default.resolve(v) : void 0;
32379
32648
  }
32380
32649
  var CURSOR_CLIENT = {
32381
32650
  id: "cursor",
32382
32651
  label: "Cursor",
32383
32652
  docsUrl: "https://docs.cursor.com/context/mcp",
32384
- mcpConfigPath: () => envOverride("NEAT_CURSOR_CONFIG") ?? import_node_path97.default.join(homeDir(), ".cursor", "mcp.json"),
32653
+ mcpConfigPath: () => envOverride("NEAT_CURSOR_CONFIG") ?? import_node_path98.default.join(homeDir(), ".cursor", "mcp.json"),
32385
32654
  mcpContainerKey: "mcpServers",
32386
32655
  format: "json",
32387
32656
  // Cursor still reads a single `.cursorrules` at the project root (the modern
@@ -32393,7 +32662,7 @@ var DEVIN_CLIENT = {
32393
32662
  id: "devin",
32394
32663
  label: "Devin Desktop (Cascade)",
32395
32664
  docsUrl: "https://docs.devin.ai/desktop/cascade/mcp",
32396
- mcpConfigPath: () => envOverride("NEAT_DEVIN_CONFIG") ?? import_node_path97.default.join(homeDir(), ".codeium", "windsurf", "mcp_config.json"),
32665
+ mcpConfigPath: () => envOverride("NEAT_DEVIN_CONFIG") ?? import_node_path98.default.join(homeDir(), ".codeium", "windsurf", "mcp_config.json"),
32397
32666
  mcpContainerKey: "mcpServers",
32398
32667
  format: "json",
32399
32668
  rulesFileName: ".windsurfrules"
@@ -32402,7 +32671,7 @@ var GEMINI_CLIENT = {
32402
32671
  id: "gemini",
32403
32672
  label: "Gemini CLI",
32404
32673
  docsUrl: "https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md",
32405
- mcpConfigPath: () => envOverride("NEAT_GEMINI_CONFIG") ?? import_node_path97.default.join(homeDir(), ".gemini", "settings.json"),
32674
+ mcpConfigPath: () => envOverride("NEAT_GEMINI_CONFIG") ?? import_node_path98.default.join(homeDir(), ".gemini", "settings.json"),
32406
32675
  mcpContainerKey: "mcpServers",
32407
32676
  format: "json",
32408
32677
  rulesFileName: "GEMINI.md"
@@ -32411,7 +32680,7 @@ var QWEN_CLIENT = {
32411
32680
  id: "qwen",
32412
32681
  label: "Qwen Code",
32413
32682
  docsUrl: "https://qwenlm.github.io/qwen-code-docs/en/users/features/mcp/",
32414
- mcpConfigPath: () => envOverride("NEAT_QWEN_CONFIG") ?? import_node_path97.default.join(homeDir(), ".qwen", "settings.json"),
32683
+ mcpConfigPath: () => envOverride("NEAT_QWEN_CONFIG") ?? import_node_path98.default.join(homeDir(), ".qwen", "settings.json"),
32415
32684
  mcpContainerKey: "mcpServers",
32416
32685
  format: "json",
32417
32686
  rulesFileName: "QWEN.md"
@@ -32420,7 +32689,7 @@ var AMAZONQ_CLIENT = {
32420
32689
  id: "amazonq",
32421
32690
  label: "Amazon Q Developer CLI",
32422
32691
  docsUrl: "https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line-mcp-configuration.html",
32423
- mcpConfigPath: () => envOverride("NEAT_AMAZONQ_CONFIG") ?? import_node_path97.default.join(homeDir(), ".aws", "amazonq", "mcp.json"),
32692
+ mcpConfigPath: () => envOverride("NEAT_AMAZONQ_CONFIG") ?? import_node_path98.default.join(homeDir(), ".aws", "amazonq", "mcp.json"),
32424
32693
  mcpContainerKey: "mcpServers",
32425
32694
  format: "json"
32426
32695
  };
@@ -32428,7 +32697,7 @@ var ROOCODE_CLIENT = {
32428
32697
  id: "roocode",
32429
32698
  label: "Roo Code",
32430
32699
  docsUrl: "https://roocodeinc.github.io/Roo-Code/features/mcp/using-mcp-in-roo",
32431
- mcpConfigPath: () => envOverride("NEAT_ROOCODE_CONFIG") ?? import_node_path97.default.join(process.cwd(), ".roo", "mcp.json"),
32700
+ mcpConfigPath: () => envOverride("NEAT_ROOCODE_CONFIG") ?? import_node_path98.default.join(process.cwd(), ".roo", "mcp.json"),
32432
32701
  mcpContainerKey: "mcpServers",
32433
32702
  format: "json"
32434
32703
  };
@@ -32441,9 +32710,9 @@ var ZED_CLIENT = {
32441
32710
  if (override) return override;
32442
32711
  if (process.platform === "win32") {
32443
32712
  const appData = process.env.APPDATA;
32444
- if (appData && appData.length > 0) return import_node_path97.default.join(appData, "Zed", "settings.json");
32713
+ if (appData && appData.length > 0) return import_node_path98.default.join(appData, "Zed", "settings.json");
32445
32714
  }
32446
- return import_node_path97.default.join(homeDir(), ".config", "zed", "settings.json");
32715
+ return import_node_path98.default.join(homeDir(), ".config", "zed", "settings.json");
32447
32716
  },
32448
32717
  mcpContainerKey: "context_servers",
32449
32718
  format: "jsonc",
@@ -32453,7 +32722,7 @@ var OPENCODE_CLIENT = {
32453
32722
  id: "opencode",
32454
32723
  label: "OpenCode",
32455
32724
  docsUrl: "https://opencode.ai/docs/mcp-servers/",
32456
- mcpConfigPath: () => envOverride("NEAT_OPENCODE_CONFIG") ?? import_node_path97.default.join(xdgConfigDir(), "opencode", "opencode.json"),
32725
+ mcpConfigPath: () => envOverride("NEAT_OPENCODE_CONFIG") ?? import_node_path98.default.join(xdgConfigDir(), "opencode", "opencode.json"),
32457
32726
  mcpContainerKey: "mcp",
32458
32727
  format: "json",
32459
32728
  serverEntry: NEAT_OPENCODE_SERVER,
@@ -32463,7 +32732,7 @@ var CRUSH_CLIENT = {
32463
32732
  id: "crush",
32464
32733
  label: "Crush",
32465
32734
  docsUrl: "https://charmbracelet-crush.mintlify.app/configuration/mcp",
32466
- mcpConfigPath: () => envOverride("NEAT_CRUSH_CONFIG") ?? import_node_path97.default.join(xdgConfigDir(), "crush", "crush.json"),
32735
+ mcpConfigPath: () => envOverride("NEAT_CRUSH_CONFIG") ?? import_node_path98.default.join(xdgConfigDir(), "crush", "crush.json"),
32467
32736
  mcpContainerKey: "mcp",
32468
32737
  format: "json",
32469
32738
  serverEntry: NEAT_CRUSH_SERVER,
@@ -32482,7 +32751,7 @@ var CLIENTS = {
32482
32751
  };
32483
32752
  function mergeJsonMcp(existing, containerKey, serverEntry) {
32484
32753
  const servers = existing[containerKey] ?? {};
32485
- const already = (0, import_node_util2.isDeepStrictEqual)(servers.neat, serverEntry);
32754
+ const already = (0, import_node_util3.isDeepStrictEqual)(servers.neat, serverEntry);
32486
32755
  const merged = {
32487
32756
  ...existing,
32488
32757
  [containerKey]: { ...servers, neat: serverEntry }
@@ -32493,7 +32762,7 @@ function mergeJsoncMcp(raw, containerKey, serverEntry) {
32493
32762
  const base = raw.trim().length > 0 ? raw : "{}";
32494
32763
  const parsed = jsonc.parse(base) ?? {};
32495
32764
  const servers = parsed[containerKey] ?? {};
32496
- if ((0, import_node_util2.isDeepStrictEqual)(servers.neat, serverEntry)) {
32765
+ if ((0, import_node_util3.isDeepStrictEqual)(servers.neat, serverEntry)) {
32497
32766
  return { text: raw, changed: false };
32498
32767
  }
32499
32768
  const edits = jsonc.modify(base, [containerKey, "neat"], serverEntry, {
@@ -32526,7 +32795,7 @@ async function planMcp(client, mcpPath) {
32526
32795
  const serverEntry = client.serverEntry ?? NEAT_MCP_SERVER;
32527
32796
  let raw = "";
32528
32797
  try {
32529
- raw = await import_node_fs59.promises.readFile(mcpPath, "utf8");
32798
+ raw = await import_node_fs60.promises.readFile(mcpPath, "utf8");
32530
32799
  } catch (err) {
32531
32800
  const e = err;
32532
32801
  if (e.code === "ENOENT") {
@@ -32568,7 +32837,7 @@ async function runEditorInstall(client, opts) {
32568
32837
  const mcpPath = client.mcpConfigPath();
32569
32838
  const serverEntry = client.serverEntry ?? NEAT_MCP_SERVER;
32570
32839
  const hasRules = typeof client.rulesFileName === "string";
32571
- const rulesPath = hasRules ? import_node_path97.default.join(opts.projectDir, client.rulesFileName) : "";
32840
+ const rulesPath = hasRules ? import_node_path98.default.join(opts.projectDir, client.rulesFileName) : "";
32572
32841
  const mcp = await planMcp(client, mcpPath);
32573
32842
  if (mcp === null) return { exitCode: 1 };
32574
32843
  let existingRules = "";
@@ -32577,7 +32846,7 @@ async function runEditorInstall(client, opts) {
32577
32846
  let block = "";
32578
32847
  if (hasRules) {
32579
32848
  try {
32580
- existingRules = await import_node_fs59.promises.readFile(rulesPath, "utf8");
32849
+ existingRules = await import_node_fs60.promises.readFile(rulesPath, "utf8");
32581
32850
  } catch (err) {
32582
32851
  if (err.code !== "ENOENT") {
32583
32852
  console.error(`neat ${client.id}: failed to read ${rulesPath} \u2014 ${err.message}`);
@@ -32611,11 +32880,11 @@ async function runEditorInstall(client, opts) {
32611
32880
  );
32612
32881
  return { exitCode: 0 };
32613
32882
  }
32614
- await import_node_fs59.promises.mkdir(import_node_path97.default.dirname(mcpPath), { recursive: true });
32615
- await import_node_fs59.promises.writeFile(mcpPath, mcp.text, "utf8");
32883
+ await import_node_fs60.promises.mkdir(import_node_path98.default.dirname(mcpPath), { recursive: true });
32884
+ await import_node_fs60.promises.writeFile(mcpPath, mcp.text, "utf8");
32616
32885
  if (hasRules) {
32617
- await import_node_fs59.promises.mkdir(import_node_path97.default.dirname(rulesPath), { recursive: true });
32618
- await import_node_fs59.promises.writeFile(rulesPath, newRules, "utf8");
32886
+ await import_node_fs60.promises.mkdir(import_node_path98.default.dirname(rulesPath), { recursive: true });
32887
+ await import_node_fs60.promises.writeFile(rulesPath, newRules, "utf8");
32619
32888
  }
32620
32889
  console.log(`neat ${client.id}: wired NEAT into ${client.label}`);
32621
32890
  console.log(` MCP server: ${mcpPath} (${client.mcpContainerKey}.neat \u2192 npx -y @neat.is/mcp)`);
@@ -33117,7 +33386,7 @@ function sleep(ms, signal) {
33117
33386
 
33118
33387
  // src/cli-verbs.ts
33119
33388
  init_cjs_shims();
33120
- var import_node_path98 = __toESM(require("path"), 1);
33389
+ var import_node_path99 = __toESM(require("path"), 1);
33121
33390
  async function resolveProjectEntry(opts) {
33122
33391
  const entries = await listProjects();
33123
33392
  if (opts.project) {
@@ -33127,7 +33396,7 @@ async function resolveProjectEntry(opts) {
33127
33396
  const cwd = opts.cwd ?? process.cwd();
33128
33397
  const resolvedCwd = await normalizeProjectPath(cwd);
33129
33398
  for (const entry2 of entries) {
33130
- if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path98.default.sep}`)) {
33399
+ if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path99.default.sep}`)) {
33131
33400
  return entry2;
33132
33401
  }
33133
33402
  }
@@ -33302,6 +33571,7 @@ async function runSync(opts) {
33302
33571
 
33303
33572
  // src/cli.ts
33304
33573
  var import_types112 = require("@neat.is/types");
33574
+ init_auth();
33305
33575
  function usage5() {
33306
33576
  const neat = commandPrefix();
33307
33577
  console.log("Installed via npx? Prefix commands with `npx neat.is`, or install once: `npm i -g neat.is`.");
@@ -33704,7 +33974,7 @@ async function buildPatchSections(services, project) {
33704
33974
  }
33705
33975
  async function runInit(opts) {
33706
33976
  const written = [];
33707
- const stat = await import_node_fs60.promises.stat(opts.scanPath).catch(() => null);
33977
+ const stat = await import_node_fs61.promises.stat(opts.scanPath).catch(() => null);
33708
33978
  if (!stat || !stat.isDirectory()) {
33709
33979
  console.error(`neat init: ${opts.scanPath} is not a directory`);
33710
33980
  return { exitCode: 2, writtenFiles: written };
@@ -33713,13 +33983,13 @@ async function runInit(opts) {
33713
33983
  printDiscoveryReport(opts, services);
33714
33984
  const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
33715
33985
  const patch = renderPatch(sections);
33716
- const patchPath = import_node_path99.default.join(opts.scanPath, "neat.patch");
33986
+ const patchPath = import_node_path100.default.join(opts.scanPath, "neat.patch");
33717
33987
  if (opts.dryRun) {
33718
- await import_node_fs60.promises.writeFile(patchPath, patch, "utf8");
33988
+ await import_node_fs61.promises.writeFile(patchPath, patch, "utf8");
33719
33989
  written.push(patchPath);
33720
33990
  console.log(`dry-run: patch written to ${patchPath}`);
33721
- const gitignorePath = import_node_path99.default.join(opts.scanPath, ".gitignore");
33722
- const gitignoreExists = await import_node_fs60.promises.stat(gitignorePath).then(() => true).catch(() => false);
33991
+ const gitignorePath = import_node_path100.default.join(opts.scanPath, ".gitignore");
33992
+ const gitignoreExists = await import_node_fs61.promises.stat(gitignorePath).then(() => true).catch(() => false);
33723
33993
  const verb = gitignoreExists ? "append" : "create";
33724
33994
  console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
33725
33995
  console.log("rerun without --dry-run to register and snapshot.");
@@ -33730,9 +34000,9 @@ async function runInit(opts) {
33730
34000
  const graph = getGraph(graphKey);
33731
34001
  const projectPaths = pathsForProject(
33732
34002
  graphKey,
33733
- import_node_path99.default.join(opts.scanPath, "neat-out")
34003
+ import_node_path100.default.join(opts.scanPath, "neat-out")
33734
34004
  );
33735
- const errorsPath = import_node_path99.default.join(import_node_path99.default.dirname(opts.outPath), import_node_path99.default.basename(projectPaths.errorsPath));
34005
+ const errorsPath = import_node_path100.default.join(import_node_path100.default.dirname(opts.outPath), import_node_path100.default.basename(projectPaths.errorsPath));
33736
34006
  const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
33737
34007
  await saveGraphToDisk(graph, opts.outPath);
33738
34008
  written.push(opts.outPath);
@@ -33811,7 +34081,7 @@ async function runInit(opts) {
33811
34081
  console.log("Run `npm install` (or your language equivalent) to refresh lockfiles.");
33812
34082
  }
33813
34083
  } else {
33814
- await import_node_fs60.promises.writeFile(patchPath, patch, "utf8");
34084
+ await import_node_fs61.promises.writeFile(patchPath, patch, "utf8");
33815
34085
  written.push(patchPath);
33816
34086
  }
33817
34087
  }
@@ -33852,9 +34122,9 @@ var CLAUDE_SKILL_CONFIG = {
33852
34122
  };
33853
34123
  function claudeConfigPath() {
33854
34124
  const override = process.env.NEAT_CLAUDE_CONFIG;
33855
- if (override && override.length > 0) return import_node_path99.default.resolve(override);
34125
+ if (override && override.length > 0) return import_node_path100.default.resolve(override);
33856
34126
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
33857
- return import_node_path99.default.join(home, ".claude.json");
34127
+ return import_node_path100.default.join(home, ".claude.json");
33858
34128
  }
33859
34129
  async function runSkill(opts) {
33860
34130
  const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
@@ -33866,7 +34136,7 @@ async function runSkill(opts) {
33866
34136
  const target = claudeConfigPath();
33867
34137
  let existing = {};
33868
34138
  try {
33869
- existing = JSON.parse(await import_node_fs60.promises.readFile(target, "utf8"));
34139
+ existing = JSON.parse(await import_node_fs61.promises.readFile(target, "utf8"));
33870
34140
  } catch (err) {
33871
34141
  if (err.code !== "ENOENT") {
33872
34142
  console.error(`neat skill: failed to read ${target} \u2014 ${err.message}`);
@@ -33878,8 +34148,8 @@ async function runSkill(opts) {
33878
34148
  ...existing,
33879
34149
  mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
33880
34150
  };
33881
- await import_node_fs60.promises.mkdir(import_node_path99.default.dirname(target), { recursive: true });
33882
- await import_node_fs60.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
34151
+ await import_node_fs61.promises.mkdir(import_node_path100.default.dirname(target), { recursive: true });
34152
+ await import_node_fs61.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
33883
34153
  console.log(`neat skill: wrote mcpServers.neat to ${target}`);
33884
34154
  console.log("restart Claude Code to pick up the new MCP server.");
33885
34155
  console.log("");
@@ -34000,12 +34270,12 @@ async function main() {
34000
34270
  console.error("neat init: --apply and --dry-run are mutually exclusive");
34001
34271
  process.exit(2);
34002
34272
  }
34003
- const scanPath = import_node_path99.default.resolve(target);
34273
+ const scanPath = import_node_path100.default.resolve(target);
34004
34274
  const projectExplicit = parsed.project !== null;
34005
- const projectName = projectExplicit ? project : import_node_path99.default.basename(scanPath);
34275
+ const projectName = projectExplicit ? project : import_node_path100.default.basename(scanPath);
34006
34276
  const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
34007
- const fallback = pathsForProject(projectKey, import_node_path99.default.join(scanPath, "neat-out")).snapshotPath;
34008
- const outPath = import_node_path99.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
34277
+ const fallback = pathsForProject(projectKey, import_node_path100.default.join(scanPath, "neat-out")).snapshotPath;
34278
+ const outPath = import_node_path100.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
34009
34279
  const result = await runInit({
34010
34280
  scanPath,
34011
34281
  outPath,
@@ -34026,21 +34296,21 @@ async function main() {
34026
34296
  usage5();
34027
34297
  process.exit(2);
34028
34298
  }
34029
- const scanPath = import_node_path99.default.resolve(target);
34030
- const stat = await import_node_fs60.promises.stat(scanPath).catch(() => null);
34299
+ const scanPath = import_node_path100.default.resolve(target);
34300
+ const stat = await import_node_fs61.promises.stat(scanPath).catch(() => null);
34031
34301
  if (!stat || !stat.isDirectory()) {
34032
34302
  console.error(`neat watch: ${scanPath} is not a directory`);
34033
34303
  process.exit(2);
34034
34304
  }
34035
- const projectPaths = pathsForProject(project, import_node_path99.default.join(scanPath, "neat-out"));
34036
- const outPath = import_node_path99.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
34037
- const errorsPath = import_node_path99.default.resolve(
34038
- process.env.NEAT_ERRORS_PATH ?? import_node_path99.default.join(import_node_path99.default.dirname(outPath), import_node_path99.default.basename(projectPaths.errorsPath))
34305
+ const projectPaths = pathsForProject(project, import_node_path100.default.join(scanPath, "neat-out"));
34306
+ const outPath = import_node_path100.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
34307
+ const errorsPath = import_node_path100.default.resolve(
34308
+ process.env.NEAT_ERRORS_PATH ?? import_node_path100.default.join(import_node_path100.default.dirname(outPath), import_node_path100.default.basename(projectPaths.errorsPath))
34039
34309
  );
34040
- const staleEventsPath = import_node_path99.default.resolve(
34041
- process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path99.default.join(import_node_path99.default.dirname(outPath), import_node_path99.default.basename(projectPaths.staleEventsPath))
34310
+ const staleEventsPath = import_node_path100.default.resolve(
34311
+ process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path100.default.join(import_node_path100.default.dirname(outPath), import_node_path100.default.basename(projectPaths.staleEventsPath))
34042
34312
  );
34043
- const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path99.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
34313
+ const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path100.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
34044
34314
  const handle = await startWatch(getGraph(project), {
34045
34315
  scanPath,
34046
34316
  outPath,
@@ -34049,7 +34319,7 @@ async function main() {
34049
34319
  project,
34050
34320
  // Resolve NEAT_HOME so a `neat watch` picks up connectors added to
34051
34321
  // ~/.neat/connectors.json (#871). Same resolution the rest of the CLI uses.
34052
- neatHome: process.env.NEAT_HOME ? import_node_path99.default.resolve(process.env.NEAT_HOME) : import_node_path99.default.join(import_node_os11.default.homedir(), ".neat"),
34322
+ neatHome: process.env.NEAT_HOME ? import_node_path100.default.resolve(process.env.NEAT_HOME) : import_node_path100.default.join(import_node_os11.default.homedir(), ".neat"),
34053
34323
  ...embeddingsCachePath ? { embeddingsCachePath } : {},
34054
34324
  host: process.env.HOST ?? "0.0.0.0",
34055
34325
  port: Number(process.env.PORT ?? 8080),
@@ -34231,11 +34501,11 @@ async function main() {
34231
34501
  process.exit(1);
34232
34502
  }
34233
34503
  async function tryOrchestrator(cmd, parsed) {
34234
- const scanPath = import_node_path99.default.resolve(cmd);
34235
- const stat = await import_node_fs60.promises.stat(scanPath).catch(() => null);
34504
+ const scanPath = import_node_path100.default.resolve(cmd);
34505
+ const stat = await import_node_fs61.promises.stat(scanPath).catch(() => null);
34236
34506
  if (!stat || !stat.isDirectory()) return null;
34237
34507
  const projectExplicit = parsed.project !== null;
34238
- const projectName = projectExplicit ? parsed.project : import_node_path99.default.basename(scanPath);
34508
+ const projectName = projectExplicit ? parsed.project : import_node_path100.default.basename(scanPath);
34239
34509
  const result = await runOrchestrator({
34240
34510
  scanPath,
34241
34511
  project: projectName,
@@ -34250,7 +34520,18 @@ async function tryOrchestrator(cmd, parsed) {
34250
34520
  async function runWelcomeFlow(parsed) {
34251
34521
  return runWelcome({
34252
34522
  login: (loginArgv) => runLoginCommand(loginArgv),
34253
- orchestrator: async (cwd) => await tryOrchestrator(cwd, parsed) ?? 0
34523
+ // What the person chose at the door reaches the orchestrator as the flags they
34524
+ // stand for: the name as `--project`, the instrument answer as `--no-instrument`
34525
+ // or as `yes` (which keeps the orchestrator's own prompt quiet, since the door
34526
+ // already asked).
34527
+ orchestrator: async (cwd, opts) => await tryOrchestrator(cwd, {
34528
+ ...parsed,
34529
+ ...opts?.project !== void 0 ? { project: opts.project } : {},
34530
+ ...opts?.noInstrument !== void 0 ? { noInstrument: opts.noInstrument } : {},
34531
+ ...opts?.yes !== void 0 ? { yes: opts.yes } : {}
34532
+ }) ?? 0,
34533
+ // An explicit flag is an answer; don't ask again.
34534
+ instrumentFlagGiven: parsed.noInstrument || parsed.dryRun
34254
34535
  });
34255
34536
  }
34256
34537
  var QUERY_VERBS = /* @__PURE__ */ new Set([
@@ -34283,6 +34564,13 @@ var ProjectResolutionError = class extends Error {
34283
34564
  }
34284
34565
  exitCode;
34285
34566
  };
34567
+ function isLoopbackEndpoint(endpoint2) {
34568
+ try {
34569
+ return isLoopbackHost2(new URL(endpoint2).hostname.replace(/^\[|\]$/g, ""));
34570
+ } catch {
34571
+ return false;
34572
+ }
34573
+ }
34286
34574
  async function resolveProjectForVerb(client, parsed) {
34287
34575
  const explicit = resolveProjectFlag(parsed);
34288
34576
  if (explicit) return explicit;
@@ -34513,6 +34801,9 @@ async function runQueryVerb(cmd, parsed) {
34513
34801
  console.error(`neat ${cmd}: ${detail.trim()}`);
34514
34802
  } else if (err instanceof TransportError) {
34515
34803
  console.error(`neat ${cmd}: ${err.message}. Is the daemon running? (endpoint=${target.endpoint})`);
34804
+ if (isLoopbackEndpoint(target.endpoint)) {
34805
+ console.error(`neat ${cmd}: start one with \`neat watch .\`, or \`neat\` to extract and start in one step.`);
34806
+ }
34516
34807
  } else {
34517
34808
  console.error(`neat ${cmd}: ${err.message}`);
34518
34809
  }
@@ -34569,6 +34860,7 @@ if (/[\\/](?:cli\.(?:cjs|js)|cli|neat|neat\.is)$/.test(entry)) {
34569
34860
  QUERY_VERBS,
34570
34861
  UnknownProfileError,
34571
34862
  commandPrefix,
34863
+ isLoopbackEndpoint,
34572
34864
  isNpxInvocation,
34573
34865
  main,
34574
34866
  parseArgs,