@neat.is/core 0.3.3 → 0.3.4

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
@@ -101,8 +101,8 @@ function reshapeGrpcRequest(req) {
101
101
  };
102
102
  }
103
103
  function resolveProtoRoot() {
104
- const here = import_node_path33.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
105
- return import_node_path33.default.resolve(here, "..", "proto");
104
+ const here = import_node_path34.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
105
+ return import_node_path34.default.resolve(here, "..", "proto");
106
106
  }
107
107
  function loadTraceService() {
108
108
  const protoRoot = resolveProtoRoot();
@@ -157,13 +157,13 @@ async function startOtelGrpcReceiver(opts) {
157
157
  })
158
158
  };
159
159
  }
160
- var import_node_url, import_node_path33, grpc, protoLoader;
160
+ var import_node_url, import_node_path34, grpc, protoLoader;
161
161
  var init_otel_grpc = __esm({
162
162
  "src/otel-grpc.ts"() {
163
163
  "use strict";
164
164
  init_cjs_shims();
165
165
  import_node_url = require("url");
166
- import_node_path33 = __toESM(require("path"), 1);
166
+ import_node_path34 = __toESM(require("path"), 1);
167
167
  grpc = __toESM(require("@grpc/grpc-js"), 1);
168
168
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
169
169
  init_otel();
@@ -260,10 +260,10 @@ function parseOtlpRequest(body) {
260
260
  }
261
261
  function loadProtobufDecoder() {
262
262
  if (exportTraceServiceRequestType) return exportTraceServiceRequestType;
263
- const here = import_node_path34.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
264
- const protoRoot = import_node_path34.default.resolve(here, "..", "proto");
263
+ const here = import_node_path35.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
264
+ const protoRoot = import_node_path35.default.resolve(here, "..", "proto");
265
265
  const root = new import_protobufjs.default.Root();
266
- root.resolvePath = (_origin, target) => import_node_path34.default.resolve(protoRoot, target);
266
+ root.resolvePath = (_origin, target) => import_node_path35.default.resolve(protoRoot, target);
267
267
  root.loadSync(
268
268
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
269
269
  { keepCase: true }
@@ -355,12 +355,12 @@ async function buildOtelReceiver(opts) {
355
355
  };
356
356
  return decorated;
357
357
  }
358
- var import_node_path34, import_node_url2, import_fastify2, import_protobufjs, exportTraceServiceRequestType;
358
+ var import_node_path35, import_node_url2, import_fastify2, import_protobufjs, exportTraceServiceRequestType;
359
359
  var init_otel = __esm({
360
360
  "src/otel.ts"() {
361
361
  "use strict";
362
362
  init_cjs_shims();
363
- import_node_path34 = __toESM(require("path"), 1);
363
+ import_node_path35 = __toESM(require("path"), 1);
364
364
  import_node_url2 = require("url");
365
365
  import_fastify2 = __toESM(require("fastify"), 1);
366
366
  import_protobufjs = __toESM(require("protobufjs"), 1);
@@ -380,7 +380,7 @@ __export(cli_exports, {
380
380
  });
381
381
  module.exports = __toCommonJS(cli_exports);
382
382
  init_cjs_shims();
383
- var import_node_path39 = __toESM(require("path"), 1);
383
+ var import_node_path40 = __toESM(require("path"), 1);
384
384
  var import_node_fs25 = require("fs");
385
385
 
386
386
  // src/graph.ts
@@ -887,19 +887,19 @@ function confidenceFromMix(edges, now = Date.now()) {
887
887
  function longestIncomingWalk(graph, start, maxDepth) {
888
888
  let best = { path: [start], edges: [] };
889
889
  const visited = /* @__PURE__ */ new Set([start]);
890
- function step(node, path40, edges) {
891
- if (path40.length > best.path.length) {
892
- best = { path: [...path40], edges: [...edges] };
890
+ function step(node, path41, edges) {
891
+ if (path41.length > best.path.length) {
892
+ best = { path: [...path41], edges: [...edges] };
893
893
  }
894
- if (path40.length - 1 >= maxDepth) return;
894
+ if (path41.length - 1 >= maxDepth) return;
895
895
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
896
896
  for (const [srcId, edge] of incoming) {
897
897
  if (visited.has(srcId)) continue;
898
898
  visited.add(srcId);
899
- path40.push(srcId);
899
+ path41.push(srcId);
900
900
  edges.push(edge);
901
- step(srcId, path40, edges);
902
- path40.pop();
901
+ step(srcId, path41, edges);
902
+ path41.pop();
903
903
  edges.pop();
904
904
  visited.delete(srcId);
905
905
  }
@@ -1588,35 +1588,37 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false) {
1588
1588
  const existing = graph.getEdgeAttributes(id);
1589
1589
  const newSpanCount = (existing.signal?.spanCount ?? existing.callCount ?? 0) + 1;
1590
1590
  const newErrorCount = (existing.signal?.errorCount ?? 0) + (isError ? 1 : 0);
1591
+ const newSignal = {
1592
+ spanCount: newSpanCount,
1593
+ errorCount: newErrorCount,
1594
+ lastObservedAgeMs: 0
1595
+ };
1591
1596
  const updated = {
1592
1597
  ...existing,
1593
1598
  provenance: import_types3.Provenance.OBSERVED,
1594
1599
  lastObserved: ts,
1595
1600
  callCount: newSpanCount,
1596
- signal: {
1597
- spanCount: newSpanCount,
1598
- errorCount: newErrorCount,
1599
- lastObservedAgeMs: 0
1600
- },
1601
- confidence: 1
1601
+ signal: newSignal,
1602
+ confidence: (0, import_types3.confidenceForObservedSignal)(newSignal)
1602
1603
  };
1603
1604
  graph.replaceEdgeAttributes(id, updated);
1604
1605
  return { edge: updated, created: false };
1605
1606
  }
1607
+ const signal = {
1608
+ spanCount: 1,
1609
+ errorCount: isError ? 1 : 0,
1610
+ lastObservedAgeMs: 0
1611
+ };
1606
1612
  const edge = {
1607
1613
  id,
1608
1614
  source,
1609
1615
  target,
1610
1616
  type,
1611
1617
  provenance: import_types3.Provenance.OBSERVED,
1612
- confidence: 1,
1618
+ confidence: (0, import_types3.confidenceForObservedSignal)(signal),
1613
1619
  lastObserved: ts,
1614
1620
  callCount: 1,
1615
- signal: {
1616
- spanCount: 1,
1617
- errorCount: isError ? 1 : 0,
1618
- lastObservedAgeMs: 0
1619
- }
1621
+ signal
1620
1622
  };
1621
1623
  graph.addEdgeWithKey(id, source, target, edge);
1622
1624
  return { edge, created: true };
@@ -2251,6 +2253,27 @@ function formatExtractionBanner(count) {
2251
2253
  if (count === 1) return `[neat] 1 file skipped due to parse errors`;
2252
2254
  return `[neat] ${count} files skipped due to parse errors`;
2253
2255
  }
2256
+ var droppedSink = [];
2257
+ function noteExtractedDropped(edge) {
2258
+ droppedSink.push(edge);
2259
+ }
2260
+ function drainDroppedExtracted() {
2261
+ return droppedSink.splice(0, droppedSink.length);
2262
+ }
2263
+ function isRejectedLogEnabled() {
2264
+ const raw = process.env.NEAT_EXTRACTED_REJECTED_LOG;
2265
+ return raw === "1" || raw === "true";
2266
+ }
2267
+ async function writeRejectedExtracted(drops, rejectedPath) {
2268
+ if (drops.length === 0) return;
2269
+ await import_node_fs7.promises.mkdir(import_node_path7.default.dirname(rejectedPath), { recursive: true });
2270
+ const lines = drops.map((d) => JSON.stringify({ ...d, ts: (/* @__PURE__ */ new Date()).toISOString() })).join("\n") + "\n";
2271
+ await import_node_fs7.promises.appendFile(rejectedPath, lines, "utf8");
2272
+ }
2273
+ function formatPrecisionFloorBanner(count) {
2274
+ if (count === 1) return `[neat] 1 extracted edge dropped below precision floor`;
2275
+ return `[neat] ${count} extracted edges dropped below precision floor`;
2276
+ }
2254
2277
 
2255
2278
  // src/extract/services.ts
2256
2279
  var DEFAULT_SCAN_DEPTH = 5;
@@ -3189,6 +3212,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
3189
3212
  target: dbNode.id,
3190
3213
  type: import_types7.EdgeType.CONNECTS_TO,
3191
3214
  provenance: import_types7.Provenance.EXTRACTED,
3215
+ confidence: (0, import_types7.confidenceForExtracted)("structural"),
3192
3216
  // ADR-032 / #140 — every EXTRACTED edge carries evidence.file.
3193
3217
  // Ghost-edge cleanup keys retirement on this; the conditional
3194
3218
  // sourceFile spread that used to live here was a v0.1.x leftover.
@@ -3263,6 +3287,7 @@ async function addConfigNodes(graph, services, scanPath) {
3263
3287
  target: node.id,
3264
3288
  type: import_types8.EdgeType.CONFIGURED_BY,
3265
3289
  provenance: import_types8.Provenance.EXTRACTED,
3290
+ confidence: (0, import_types8.confidenceForExtracted)("structural"),
3266
3291
  evidence: { file: relPath.split(import_node_path18.default.sep).join("/") }
3267
3292
  };
3268
3293
  if (!graph.hasEdge(edge.id)) {
@@ -3417,17 +3442,32 @@ async function addHttpCallEdges(graph, services) {
3417
3442
  for (const [targetId, evidenceFile] of seenTargets) {
3418
3443
  const fileContent = files.find((f) => f.path === evidenceFile.file)?.content ?? "";
3419
3444
  const line = lineOf(fileContent, `//${evidenceFile.host}`);
3445
+ const confidence = (0, import_types9.confidenceForExtracted)("hostname-shape-match");
3446
+ const ev = {
3447
+ file: import_node_path20.default.relative(service.dir, evidenceFile.file),
3448
+ line,
3449
+ snippet: snippet(fileContent, line)
3450
+ };
3451
+ const edgeId = (0, import_types4.extractedEdgeId)(service.node.id, targetId, import_types9.EdgeType.CALLS);
3452
+ if (!(0, import_types9.passesExtractedFloor)(confidence)) {
3453
+ noteExtractedDropped({
3454
+ source: service.node.id,
3455
+ target: targetId,
3456
+ type: import_types9.EdgeType.CALLS,
3457
+ confidence,
3458
+ confidenceKind: "hostname-shape-match",
3459
+ evidence: ev
3460
+ });
3461
+ continue;
3462
+ }
3420
3463
  const edge = {
3421
- id: (0, import_types4.extractedEdgeId)(service.node.id, targetId, import_types9.EdgeType.CALLS),
3464
+ id: edgeId,
3422
3465
  source: service.node.id,
3423
3466
  target: targetId,
3424
3467
  type: import_types9.EdgeType.CALLS,
3425
3468
  provenance: import_types9.Provenance.EXTRACTED,
3426
- evidence: {
3427
- file: import_node_path20.default.relative(service.dir, evidenceFile.file),
3428
- line,
3429
- snippet: snippet(fileContent, line)
3430
- }
3469
+ confidence,
3470
+ evidence: ev
3431
3471
  };
3432
3472
  if (!graph.hasEdge(edge.id)) {
3433
3473
  graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
@@ -3466,6 +3506,10 @@ function kafkaEndpointsFromFile(file, serviceDir) {
3466
3506
  name: topic,
3467
3507
  kind: "kafka-topic",
3468
3508
  edgeType,
3509
+ // `producer.send({topic: 'x'})` / `consumer.subscribe({topic: 'x'})` —
3510
+ // framework-aware (kafkajs / node-rdkafka shape). Verified-call-site
3511
+ // tier (ADR-066).
3512
+ confidenceKind: "verified-call-site",
3469
3513
  evidence: {
3470
3514
  file: import_node_path21.default.relative(serviceDir, file.path),
3471
3515
  line,
@@ -3498,6 +3542,10 @@ function redisEndpointsFromFile(file, serviceDir) {
3498
3542
  name: host,
3499
3543
  kind: "redis",
3500
3544
  edgeType: "CALLS",
3545
+ // `redis://host` URL literal — the scheme is structural support, but no
3546
+ // call expression is verified to wire it through. URL-with-structural-
3547
+ // support tier (ADR-066).
3548
+ confidenceKind: "url-with-structural-support",
3501
3549
  evidence: {
3502
3550
  file: import_node_path22.default.relative(serviceDir, file.path),
3503
3551
  line,
@@ -3539,6 +3587,10 @@ function awsEndpointsFromFile(file, serviceDir) {
3539
3587
  name,
3540
3588
  kind,
3541
3589
  edgeType: "CALLS",
3590
+ // SDK marker (S3Client, GetCommand, etc.) plus a Bucket/TableName
3591
+ // literal — framework-aware recognizer, verified-call-site tier
3592
+ // (ADR-066).
3593
+ confidenceKind: "verified-call-site",
3542
3594
  evidence: {
3543
3595
  file: import_node_path23.default.relative(serviceDir, file.path),
3544
3596
  line,
@@ -3618,6 +3670,10 @@ function grpcEndpointsFromFile(file, serviceDir) {
3618
3670
  name,
3619
3671
  kind,
3620
3672
  edgeType: "CALLS",
3673
+ // `new <Name>Client(...)` with @aws-sdk/* or @grpc/grpc-js import
3674
+ // context — import-aware classification per #238. Verified-call-site
3675
+ // tier (ADR-066).
3676
+ confidenceKind: "verified-call-site",
3621
3677
  evidence: {
3622
3678
  file: import_node_path24.default.relative(serviceDir, file.path),
3623
3679
  line,
@@ -3678,6 +3734,18 @@ async function addExternalEndpointEdges(graph, services) {
3678
3734
  const edgeId = (0, import_types4.extractedEdgeId)(service.node.id, ep.infraId, edgeType);
3679
3735
  if (seenEdges.has(edgeId)) continue;
3680
3736
  seenEdges.add(edgeId);
3737
+ const confidence = (0, import_types14.confidenceForExtracted)(ep.confidenceKind);
3738
+ if (!(0, import_types14.passesExtractedFloor)(confidence)) {
3739
+ noteExtractedDropped({
3740
+ source: service.node.id,
3741
+ target: ep.infraId,
3742
+ type: edgeType,
3743
+ confidence,
3744
+ confidenceKind: ep.confidenceKind,
3745
+ evidence: ep.evidence
3746
+ });
3747
+ continue;
3748
+ }
3681
3749
  if (!graph.hasEdge(edgeId)) {
3682
3750
  const edge = {
3683
3751
  id: edgeId,
@@ -3685,6 +3753,7 @@ async function addExternalEndpointEdges(graph, services) {
3685
3753
  target: ep.infraId,
3686
3754
  type: edgeType,
3687
3755
  provenance: import_types14.Provenance.EXTRACTED,
3756
+ confidence,
3688
3757
  evidence: ep.evidence
3689
3758
  };
3690
3759
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -3804,6 +3873,7 @@ async function addComposeInfra(graph, scanPath, services) {
3804
3873
  target: targetId,
3805
3874
  type: import_types16.EdgeType.DEPENDS_ON,
3806
3875
  provenance: import_types16.Provenance.EXTRACTED,
3876
+ confidence: (0, import_types16.confidenceForExtracted)("structural"),
3807
3877
  evidence: { file: evidenceFile }
3808
3878
  };
3809
3879
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -3864,6 +3934,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
3864
3934
  target: node.id,
3865
3935
  type: import_types17.EdgeType.RUNS_ON,
3866
3936
  provenance: import_types17.Provenance.EXTRACTED,
3937
+ confidence: (0, import_types17.confidenceForExtracted)("structural"),
3867
3938
  evidence: {
3868
3939
  file: import_node_path26.default.relative(scanPath, dockerfilePath).split(import_node_path26.default.sep).join("/")
3869
3940
  }
@@ -3980,6 +4051,9 @@ async function addInfra(graph, scanPath, services) {
3980
4051
  };
3981
4052
  }
3982
4053
 
4054
+ // src/extract/index.ts
4055
+ var import_node_path30 = __toESM(require("path"), 1);
4056
+
3983
4057
  // src/extract/retire.ts
3984
4058
  init_cjs_shims();
3985
4059
  var import_node_fs17 = require("fs");
@@ -4044,13 +4118,26 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
4044
4118
  );
4045
4119
  }
4046
4120
  }
4121
+ const droppedEntries = drainDroppedExtracted();
4122
+ if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
4123
+ const rejectedPath = import_node_path30.default.join(import_node_path30.default.dirname(opts.errorsPath), "rejected.ndjson");
4124
+ try {
4125
+ await writeRejectedExtracted(droppedEntries, rejectedPath);
4126
+ } catch (err) {
4127
+ console.warn(
4128
+ `[neat] failed to write rejected extracted edges to ${rejectedPath}: ${err.message}`
4129
+ );
4130
+ }
4131
+ }
4047
4132
  const result = {
4048
4133
  nodesAdded: phase1Nodes + phase2.nodesAdded + phase3.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
4049
4134
  edgesAdded: phase2.edgesAdded + phase3.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
4050
4135
  frontiersPromoted,
4051
4136
  extractionErrors: errorEntries.length,
4052
4137
  errorEntries,
4053
- ghostsRetired
4138
+ ghostsRetired,
4139
+ extractedDropped: droppedEntries.length,
4140
+ droppedEntries
4054
4141
  };
4055
4142
  emitNeatEvent({
4056
4143
  type: "extraction-complete",
@@ -4068,7 +4155,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
4068
4155
  // src/persist.ts
4069
4156
  init_cjs_shims();
4070
4157
  var import_node_fs18 = require("fs");
4071
- var import_node_path30 = __toESM(require("path"), 1);
4158
+ var import_node_path31 = __toESM(require("path"), 1);
4072
4159
  var SCHEMA_VERSION = 2;
4073
4160
  function migrateV1ToV2(payload) {
4074
4161
  const nodes = payload.graph.nodes;
@@ -4082,7 +4169,7 @@ function migrateV1ToV2(payload) {
4082
4169
  return { ...payload, schemaVersion: 2 };
4083
4170
  }
4084
4171
  async function ensureDir(filePath) {
4085
- await import_node_fs18.promises.mkdir(import_node_path30.default.dirname(filePath), { recursive: true });
4172
+ await import_node_fs18.promises.mkdir(import_node_path31.default.dirname(filePath), { recursive: true });
4086
4173
  }
4087
4174
  async function saveGraphToDisk(graph, outPath) {
4088
4175
  await ensureDir(outPath);
@@ -4152,7 +4239,7 @@ function startPersistLoop(graph, outPath, intervalMs = 6e4) {
4152
4239
  // src/watch.ts
4153
4240
  init_cjs_shims();
4154
4241
  var import_node_fs22 = __toESM(require("fs"), 1);
4155
- var import_node_path36 = __toESM(require("path"), 1);
4242
+ var import_node_path37 = __toESM(require("path"), 1);
4156
4243
  var import_chokidar = __toESM(require("chokidar"), 1);
4157
4244
 
4158
4245
  // src/api.ts
@@ -4200,14 +4287,6 @@ function nodeIsFrontier(graph, nodeId) {
4200
4287
  const attrs = graph.getNodeAttributes(nodeId);
4201
4288
  return attrs.type === import_types19.NodeType.FrontierNode;
4202
4289
  }
4203
- function hasAnyObservedFromSource(graph, sourceId) {
4204
- if (!graph.hasNode(sourceId)) return false;
4205
- for (const edgeId of graph.outboundEdges(sourceId)) {
4206
- const e = graph.getEdgeAttributes(edgeId);
4207
- if (e.provenance === import_types19.Provenance.OBSERVED) return true;
4208
- }
4209
- return false;
4210
- }
4211
4290
  function clampConfidence(n) {
4212
4291
  if (!Number.isFinite(n)) return 0;
4213
4292
  return Math.max(0, Math.min(1, n));
@@ -4221,32 +4300,34 @@ function reasonForMissingExtracted(source, target, type) {
4221
4300
  var RECOMMENDATION_MISSING_OBSERVED = "Verify the code path is exercised in production; check feature flags or conditional branches that might gate the call.";
4222
4301
  var RECOMMENDATION_MISSING_EXTRACTED = "Likely dynamic dispatch, reflection, or a coverage gap in tree-sitter extraction. Consider an `aliases` entry on the source service or file an extractor issue.";
4223
4302
  var RECOMMENDATION_HOST_MISMATCH = "Check environment-specific config overrides \u2014 the runtime host differs from what static configuration declares.";
4303
+ function gradedConfidence(edge) {
4304
+ if (typeof edge.confidence === "number") return clampConfidence(edge.confidence);
4305
+ return clampConfidence(confidenceForEdge(edge));
4306
+ }
4224
4307
  function detectMissingDivergences(graph, bucket) {
4225
4308
  const out = [];
4226
4309
  if (bucket.extracted && !bucket.observed) {
4227
4310
  if (!nodeIsFrontier(graph, bucket.target)) {
4228
- const sourceHasTraffic = hasAnyObservedFromSource(graph, bucket.source);
4229
4311
  out.push({
4230
4312
  type: "missing-observed",
4231
4313
  source: bucket.source,
4232
4314
  target: bucket.target,
4233
4315
  edgeType: bucket.type,
4234
4316
  extracted: bucket.extracted,
4235
- confidence: sourceHasTraffic ? 1 : 0.5,
4317
+ confidence: gradedConfidence(bucket.extracted),
4236
4318
  reason: reasonForMissingObserved(bucket.source, bucket.target, bucket.type),
4237
4319
  recommendation: RECOMMENDATION_MISSING_OBSERVED
4238
4320
  });
4239
4321
  }
4240
4322
  }
4241
4323
  if (bucket.observed && !bucket.extracted) {
4242
- const cascaded = clampConfidence(confidenceForEdge(bucket.observed));
4243
4324
  out.push({
4244
4325
  type: "missing-extracted",
4245
4326
  source: bucket.source,
4246
4327
  target: bucket.target,
4247
4328
  edgeType: bucket.type,
4248
4329
  observed: bucket.observed,
4249
- confidence: cascaded,
4330
+ confidence: gradedConfidence(bucket.observed),
4250
4331
  reason: reasonForMissingExtracted(bucket.source, bucket.target, bucket.type),
4251
4332
  recommendation: RECOMMENDATION_MISSING_EXTRACTED
4252
4333
  });
@@ -4385,8 +4466,17 @@ function computeDivergences(graph, opts = {}) {
4385
4466
  const target = opts.node;
4386
4467
  filtered = filtered.filter((d) => involvesNode(d, target));
4387
4468
  }
4469
+ const TYPE_LEADERSHIP = {
4470
+ "missing-extracted": 0,
4471
+ "missing-observed": 1,
4472
+ "version-mismatch": 2,
4473
+ "host-mismatch": 3,
4474
+ "compat-violation": 4
4475
+ };
4388
4476
  filtered.sort((a, b) => {
4389
4477
  if (b.confidence !== a.confidence) return b.confidence - a.confidence;
4478
+ const lead = TYPE_LEADERSHIP[a.type] - TYPE_LEADERSHIP[b.type];
4479
+ if (lead !== 0) return lead;
4390
4480
  if (a.type !== b.type) return a.type.localeCompare(b.type);
4391
4481
  if (a.source !== b.source) return a.source.localeCompare(b.source);
4392
4482
  return a.target.localeCompare(b.target);
@@ -4477,23 +4567,23 @@ function canonicalJson(value) {
4477
4567
 
4478
4568
  // src/projects.ts
4479
4569
  init_cjs_shims();
4480
- var import_node_path31 = __toESM(require("path"), 1);
4570
+ var import_node_path32 = __toESM(require("path"), 1);
4481
4571
  function pathsForProject(project, baseDir) {
4482
4572
  if (project === DEFAULT_PROJECT) {
4483
4573
  return {
4484
- snapshotPath: import_node_path31.default.join(baseDir, "graph.json"),
4485
- errorsPath: import_node_path31.default.join(baseDir, "errors.ndjson"),
4486
- staleEventsPath: import_node_path31.default.join(baseDir, "stale-events.ndjson"),
4487
- embeddingsCachePath: import_node_path31.default.join(baseDir, "embeddings.json"),
4488
- policyViolationsPath: import_node_path31.default.join(baseDir, "policy-violations.ndjson")
4574
+ snapshotPath: import_node_path32.default.join(baseDir, "graph.json"),
4575
+ errorsPath: import_node_path32.default.join(baseDir, "errors.ndjson"),
4576
+ staleEventsPath: import_node_path32.default.join(baseDir, "stale-events.ndjson"),
4577
+ embeddingsCachePath: import_node_path32.default.join(baseDir, "embeddings.json"),
4578
+ policyViolationsPath: import_node_path32.default.join(baseDir, "policy-violations.ndjson")
4489
4579
  };
4490
4580
  }
4491
4581
  return {
4492
- snapshotPath: import_node_path31.default.join(baseDir, `${project}.json`),
4493
- errorsPath: import_node_path31.default.join(baseDir, `errors.${project}.ndjson`),
4494
- staleEventsPath: import_node_path31.default.join(baseDir, `stale-events.${project}.ndjson`),
4495
- embeddingsCachePath: import_node_path31.default.join(baseDir, `embeddings.${project}.json`),
4496
- policyViolationsPath: import_node_path31.default.join(baseDir, `policy-violations.${project}.ndjson`)
4582
+ snapshotPath: import_node_path32.default.join(baseDir, `${project}.json`),
4583
+ errorsPath: import_node_path32.default.join(baseDir, `errors.${project}.ndjson`),
4584
+ staleEventsPath: import_node_path32.default.join(baseDir, `stale-events.${project}.ndjson`),
4585
+ embeddingsCachePath: import_node_path32.default.join(baseDir, `embeddings.${project}.json`),
4586
+ policyViolationsPath: import_node_path32.default.join(baseDir, `policy-violations.${project}.ndjson`)
4497
4587
  };
4498
4588
  }
4499
4589
  var Projects = class {
@@ -4531,23 +4621,23 @@ var Projects = class {
4531
4621
  init_cjs_shims();
4532
4622
  var import_node_fs20 = require("fs");
4533
4623
  var import_node_os2 = __toESM(require("os"), 1);
4534
- var import_node_path32 = __toESM(require("path"), 1);
4624
+ var import_node_path33 = __toESM(require("path"), 1);
4535
4625
  var import_types20 = require("@neat.is/types");
4536
4626
  var LOCK_TIMEOUT_MS = 5e3;
4537
4627
  var LOCK_RETRY_MS = 50;
4538
4628
  function neatHome() {
4539
4629
  const override = process.env.NEAT_HOME;
4540
- if (override && override.length > 0) return import_node_path32.default.resolve(override);
4541
- return import_node_path32.default.join(import_node_os2.default.homedir(), ".neat");
4630
+ if (override && override.length > 0) return import_node_path33.default.resolve(override);
4631
+ return import_node_path33.default.join(import_node_os2.default.homedir(), ".neat");
4542
4632
  }
4543
4633
  function registryPath() {
4544
- return import_node_path32.default.join(neatHome(), "projects.json");
4634
+ return import_node_path33.default.join(neatHome(), "projects.json");
4545
4635
  }
4546
4636
  function registryLockPath() {
4547
- return import_node_path32.default.join(neatHome(), "projects.json.lock");
4637
+ return import_node_path33.default.join(neatHome(), "projects.json.lock");
4548
4638
  }
4549
4639
  async function normalizeProjectPath(input) {
4550
- const resolved = import_node_path32.default.resolve(input);
4640
+ const resolved = import_node_path33.default.resolve(input);
4551
4641
  try {
4552
4642
  return await import_node_fs20.promises.realpath(resolved);
4553
4643
  } catch {
@@ -4555,7 +4645,7 @@ async function normalizeProjectPath(input) {
4555
4645
  }
4556
4646
  }
4557
4647
  async function writeAtomically(target, contents) {
4558
- await import_node_fs20.promises.mkdir(import_node_path32.default.dirname(target), { recursive: true });
4648
+ await import_node_fs20.promises.mkdir(import_node_path33.default.dirname(target), { recursive: true });
4559
4649
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
4560
4650
  const fd = await import_node_fs20.promises.open(tmp, "w");
4561
4651
  try {
@@ -4568,7 +4658,7 @@ async function writeAtomically(target, contents) {
4568
4658
  }
4569
4659
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS) {
4570
4660
  const deadline = Date.now() + timeoutMs;
4571
- await import_node_fs20.promises.mkdir(import_node_path32.default.dirname(lockPath), { recursive: true });
4661
+ await import_node_fs20.promises.mkdir(import_node_path33.default.dirname(lockPath), { recursive: true });
4572
4662
  while (true) {
4573
4663
  try {
4574
4664
  const fd = await import_node_fs20.promises.open(lockPath, "wx");
@@ -5175,7 +5265,7 @@ init_otel_grpc();
5175
5265
  // src/search.ts
5176
5266
  init_cjs_shims();
5177
5267
  var import_node_fs21 = require("fs");
5178
- var import_node_path35 = __toESM(require("path"), 1);
5268
+ var import_node_path36 = __toESM(require("path"), 1);
5179
5269
  var import_node_crypto = require("crypto");
5180
5270
  var DEFAULT_LIMIT = 10;
5181
5271
  var NOMIC_DIM = 768;
@@ -5314,7 +5404,7 @@ async function readCache(cachePath) {
5314
5404
  }
5315
5405
  }
5316
5406
  async function writeCache(cachePath, cache) {
5317
- await import_node_fs21.promises.mkdir(import_node_path35.default.dirname(cachePath), { recursive: true });
5407
+ await import_node_fs21.promises.mkdir(import_node_path36.default.dirname(cachePath), { recursive: true });
5318
5408
  await import_node_fs21.promises.writeFile(cachePath, JSON.stringify(cache));
5319
5409
  }
5320
5410
  var VectorIndex = class {
@@ -5470,8 +5560,8 @@ var ALL_PHASES = [
5470
5560
  ];
5471
5561
  function classifyChange(relPath) {
5472
5562
  const phases = /* @__PURE__ */ new Set();
5473
- const base = import_node_path36.default.basename(relPath).toLowerCase();
5474
- const segments = relPath.split(import_node_path36.default.sep).map((s) => s.toLowerCase());
5563
+ const base = import_node_path37.default.basename(relPath).toLowerCase();
5564
+ const segments = relPath.split(import_node_path37.default.sep).map((s) => s.toLowerCase());
5475
5565
  if (base === "package.json" || base === "requirements.txt" || base === "pyproject.toml" || base === "setup.py") {
5476
5566
  phases.add("services");
5477
5567
  phases.add("aliases");
@@ -5573,9 +5663,9 @@ function countWatchableDirs(scanPath, limit) {
5573
5663
  for (const e of entries) {
5574
5664
  if (count >= limit) return;
5575
5665
  if (!e.isDirectory()) continue;
5576
- if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path36.default.join(dir, e.name) + import_node_path36.default.sep))) continue;
5666
+ if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path37.default.join(dir, e.name) + import_node_path37.default.sep))) continue;
5577
5667
  count++;
5578
- if (depth < 2) visit(import_node_path36.default.join(dir, e.name), depth + 1);
5668
+ if (depth < 2) visit(import_node_path37.default.join(dir, e.name), depth + 1);
5579
5669
  }
5580
5670
  };
5581
5671
  visit(scanPath, 0);
@@ -5593,8 +5683,8 @@ async function startWatch(graph, opts) {
5593
5683
  const projectName = opts.project ?? DEFAULT_PROJECT;
5594
5684
  await loadGraphFromDisk(graph, opts.outPath);
5595
5685
  const detachEventBus = attachGraphToEventBus(graph, { project: projectName });
5596
- const policyFilePath = import_node_path36.default.join(opts.scanPath, "policy.json");
5597
- const policyViolationsPath = import_node_path36.default.join(import_node_path36.default.dirname(opts.outPath), "policy-violations.ndjson");
5686
+ const policyFilePath = import_node_path37.default.join(opts.scanPath, "policy.json");
5687
+ const policyViolationsPath = import_node_path37.default.join(import_node_path37.default.dirname(opts.outPath), "policy-violations.ndjson");
5598
5688
  let policies = [];
5599
5689
  try {
5600
5690
  policies = await loadPolicyFile(policyFilePath);
@@ -5643,7 +5733,7 @@ async function startWatch(graph, opts) {
5643
5733
  const host = opts.host ?? "0.0.0.0";
5644
5734
  const port = opts.port ?? 8080;
5645
5735
  const otelPort = opts.otelPort ?? 4318;
5646
- const cachePath = opts.embeddingsCachePath ?? import_node_path36.default.join(import_node_path36.default.dirname(opts.outPath), "embeddings.json");
5736
+ const cachePath = opts.embeddingsCachePath ?? import_node_path37.default.join(import_node_path37.default.dirname(opts.outPath), "embeddings.json");
5647
5737
  let searchIndex;
5648
5738
  try {
5649
5739
  searchIndex = await buildSearchIndex(graph, { cachePath });
@@ -5661,7 +5751,7 @@ async function startWatch(graph, opts) {
5661
5751
  // Paths are derived from the explicit options the watch caller passes
5662
5752
  // — pathsForProject is only used to fill in the embeddings/snapshot
5663
5753
  // fields so the registry shape is complete.
5664
- ...pathsForProject(projectName, import_node_path36.default.dirname(opts.outPath)),
5754
+ ...pathsForProject(projectName, import_node_path37.default.dirname(opts.outPath)),
5665
5755
  snapshotPath: opts.outPath,
5666
5756
  errorsPath: opts.errorsPath,
5667
5757
  staleEventsPath: opts.staleEventsPath
@@ -5746,9 +5836,9 @@ async function startWatch(graph, opts) {
5746
5836
  };
5747
5837
  const onPath = (absPath) => {
5748
5838
  if (shouldIgnore(absPath)) return;
5749
- const rel = import_node_path36.default.relative(opts.scanPath, absPath);
5839
+ const rel = import_node_path37.default.relative(opts.scanPath, absPath);
5750
5840
  if (!rel || rel.startsWith("..")) return;
5751
- pendingPaths.add(rel.split(import_node_path36.default.sep).join("/"));
5841
+ pendingPaths.add(rel.split(import_node_path37.default.sep).join("/"));
5752
5842
  const phases = classifyChange(rel);
5753
5843
  if (phases.size === 0) {
5754
5844
  for (const p of ALL_PHASES) pending.add(p);
@@ -5806,7 +5896,7 @@ init_cjs_shims();
5806
5896
  // src/installers/javascript.ts
5807
5897
  init_cjs_shims();
5808
5898
  var import_node_fs23 = require("fs");
5809
- var import_node_path37 = __toESM(require("path"), 1);
5899
+ var import_node_path38 = __toESM(require("path"), 1);
5810
5900
  var SDK_PACKAGES = [
5811
5901
  { name: "@opentelemetry/api", version: "^1.9.0" },
5812
5902
  { name: "@opentelemetry/sdk-node", version: "^0.57.0" },
@@ -5822,7 +5912,7 @@ var OTEL_ENV = {
5822
5912
  };
5823
5913
  async function readPackageJson(serviceDir) {
5824
5914
  try {
5825
- const raw = await import_node_fs23.promises.readFile(import_node_path37.default.join(serviceDir, "package.json"), "utf8");
5915
+ const raw = await import_node_fs23.promises.readFile(import_node_path38.default.join(serviceDir, "package.json"), "utf8");
5826
5916
  return JSON.parse(raw);
5827
5917
  } catch {
5828
5918
  return null;
@@ -5841,7 +5931,7 @@ function rewriteStartScript(start) {
5841
5931
  }
5842
5932
  async function plan(serviceDir) {
5843
5933
  const pkg = await readPackageJson(serviceDir);
5844
- const manifestPath = import_node_path37.default.join(serviceDir, "package.json");
5934
+ const manifestPath = import_node_path38.default.join(serviceDir, "package.json");
5845
5935
  const empty = {
5846
5936
  language: "javascript",
5847
5937
  serviceDir,
@@ -5940,7 +6030,7 @@ async function rollback(installPlan, originals) {
5940
6030
  ...restored.map((f) => `restored: ${f}`),
5941
6031
  ""
5942
6032
  ];
5943
- const rollbackPath = import_node_path37.default.join(installPlan.serviceDir, "neat-rollback.patch");
6033
+ const rollbackPath = import_node_path38.default.join(installPlan.serviceDir, "neat-rollback.patch");
5944
6034
  await import_node_fs23.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
5945
6035
  }
5946
6036
  var javascriptInstaller = {
@@ -5953,7 +6043,7 @@ var javascriptInstaller = {
5953
6043
  // src/installers/python.ts
5954
6044
  init_cjs_shims();
5955
6045
  var import_node_fs24 = require("fs");
5956
- var import_node_path38 = __toESM(require("path"), 1);
6046
+ var import_node_path39 = __toESM(require("path"), 1);
5957
6047
  var SDK_PACKAGES2 = [
5958
6048
  { name: "opentelemetry-distro", version: ">=0.49b0" },
5959
6049
  { name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
@@ -5974,7 +6064,7 @@ async function exists2(p) {
5974
6064
  async function detect2(serviceDir) {
5975
6065
  const markers = ["requirements.txt", "pyproject.toml", "setup.py"];
5976
6066
  for (const m of markers) {
5977
- if (await exists2(import_node_path38.default.join(serviceDir, m))) return true;
6067
+ if (await exists2(import_node_path39.default.join(serviceDir, m))) return true;
5978
6068
  }
5979
6069
  return false;
5980
6070
  }
@@ -5984,7 +6074,7 @@ function reqPackageName(line) {
5984
6074
  return head.replace(/[<>=!~].*$/, "").toLowerCase();
5985
6075
  }
5986
6076
  async function planRequirementsTxtEdits(serviceDir) {
5987
- const file = import_node_path38.default.join(serviceDir, "requirements.txt");
6077
+ const file = import_node_path39.default.join(serviceDir, "requirements.txt");
5988
6078
  if (!await exists2(file)) return null;
5989
6079
  const raw = await import_node_fs24.promises.readFile(file, "utf8");
5990
6080
  const presentNames = new Set(
@@ -5994,7 +6084,7 @@ async function planRequirementsTxtEdits(serviceDir) {
5994
6084
  return { manifest: file, missing: [...missing] };
5995
6085
  }
5996
6086
  async function planProcfileEdits(serviceDir) {
5997
- const procfile = import_node_path38.default.join(serviceDir, "Procfile");
6087
+ const procfile = import_node_path39.default.join(serviceDir, "Procfile");
5998
6088
  if (!await exists2(procfile)) return [];
5999
6089
  const raw = await import_node_fs24.promises.readFile(procfile, "utf8");
6000
6090
  const edits = [];
@@ -6079,7 +6169,7 @@ async function apply2(installPlan) {
6079
6169
  if (raw === void 0) {
6080
6170
  throw new Error(`python installer: cannot read ${file} during apply`);
6081
6171
  }
6082
- const base = import_node_path38.default.basename(file);
6172
+ const base = import_node_path39.default.basename(file);
6083
6173
  if (base === "requirements.txt") {
6084
6174
  const edits = installPlan.dependencyEdits.filter((e) => e.file === file);
6085
6175
  if (edits.length > 0) await applyRequirementsTxt(file, edits, raw);
@@ -6111,7 +6201,7 @@ async function rollback2(installPlan, originals) {
6111
6201
  ...restored.map((f) => `restored: ${f}`),
6112
6202
  ""
6113
6203
  ];
6114
- const rollbackPath = import_node_path38.default.join(installPlan.serviceDir, "neat-rollback.patch");
6204
+ const rollbackPath = import_node_path39.default.join(installPlan.serviceDir, "neat-rollback.patch");
6115
6205
  await import_node_fs24.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
6116
6206
  }
6117
6207
  var pythonInstaller = {
@@ -6225,10 +6315,10 @@ var TransportError = class extends Error {
6225
6315
  function createHttpClient(baseUrl) {
6226
6316
  const root = baseUrl.replace(/\/$/, "");
6227
6317
  return {
6228
- async get(path40) {
6318
+ async get(path41) {
6229
6319
  let res;
6230
6320
  try {
6231
- res = await fetch(`${root}${path40}`);
6321
+ res = await fetch(`${root}${path41}`);
6232
6322
  } catch (err) {
6233
6323
  throw new TransportError(
6234
6324
  `cannot reach neat-core at ${root}: ${err.message}`
@@ -6238,16 +6328,16 @@ function createHttpClient(baseUrl) {
6238
6328
  const body = await res.text().catch(() => "");
6239
6329
  throw new HttpError(
6240
6330
  res.status,
6241
- `${res.status} ${res.statusText} on GET ${path40}: ${body}`,
6331
+ `${res.status} ${res.statusText} on GET ${path41}: ${body}`,
6242
6332
  body
6243
6333
  );
6244
6334
  }
6245
6335
  return await res.json();
6246
6336
  },
6247
- async post(path40, body) {
6337
+ async post(path41, body) {
6248
6338
  let res;
6249
6339
  try {
6250
- res = await fetch(`${root}${path40}`, {
6340
+ res = await fetch(`${root}${path41}`, {
6251
6341
  method: "POST",
6252
6342
  headers: { "content-type": "application/json" },
6253
6343
  body: JSON.stringify(body)
@@ -6261,7 +6351,7 @@ function createHttpClient(baseUrl) {
6261
6351
  const text = await res.text().catch(() => "");
6262
6352
  throw new HttpError(
6263
6353
  res.status,
6264
- `${res.status} ${res.statusText} on POST ${path40}: ${text}`,
6354
+ `${res.status} ${res.statusText} on POST ${path41}: ${text}`,
6265
6355
  text
6266
6356
  );
6267
6357
  }
@@ -6275,12 +6365,12 @@ function projectPath(project, suffix) {
6275
6365
  }
6276
6366
  async function runRootCause(client, input) {
6277
6367
  const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
6278
- const path40 = projectPath(
6368
+ const path41 = projectPath(
6279
6369
  input.project,
6280
6370
  `/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
6281
6371
  );
6282
6372
  try {
6283
- const result = await client.get(path40);
6373
+ const result = await client.get(path41);
6284
6374
  const arrowPath = result.traversalPath.join(" \u2190 ");
6285
6375
  const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
6286
6376
  const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
@@ -6306,12 +6396,12 @@ async function runRootCause(client, input) {
6306
6396
  }
6307
6397
  async function runBlastRadius(client, input) {
6308
6398
  const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
6309
- const path40 = projectPath(
6399
+ const path41 = projectPath(
6310
6400
  input.project,
6311
6401
  `/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
6312
6402
  );
6313
6403
  try {
6314
- const result = await client.get(path40);
6404
+ const result = await client.get(path41);
6315
6405
  if (result.totalAffected === 0) {
6316
6406
  return {
6317
6407
  summary: `${result.origin} has no downstream dependencies. Nothing else would break if it failed.`
@@ -6345,12 +6435,12 @@ function formatBlastEntry(n) {
6345
6435
  }
6346
6436
  async function runDependencies(client, input) {
6347
6437
  const depth = input.depth ?? 3;
6348
- const path40 = projectPath(
6438
+ const path41 = projectPath(
6349
6439
  input.project,
6350
6440
  `/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
6351
6441
  );
6352
6442
  try {
6353
- const result = await client.get(path40);
6443
+ const result = await client.get(path41);
6354
6444
  if (result.total === 0) {
6355
6445
  return {
6356
6446
  summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
@@ -6431,9 +6521,9 @@ function formatDuration(ms) {
6431
6521
  return `${Math.round(h / 24)}d`;
6432
6522
  }
6433
6523
  async function runIncidents(client, input) {
6434
- const path40 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
6524
+ const path41 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
6435
6525
  try {
6436
- const body = await client.get(path40);
6526
+ const body = await client.get(path41);
6437
6527
  const events = body.events;
6438
6528
  if (events.length === 0) {
6439
6529
  return {
@@ -6957,7 +7047,7 @@ async function runInit(opts) {
6957
7047
  printDiscoveryReport(opts, services);
6958
7048
  const sections = opts.noInstall ? [] : await buildPatchSections(services);
6959
7049
  const patch = renderPatch(sections);
6960
- const patchPath = import_node_path39.default.join(opts.scanPath, "neat.patch");
7050
+ const patchPath = import_node_path40.default.join(opts.scanPath, "neat.patch");
6961
7051
  if (opts.dryRun) {
6962
7052
  await import_node_fs25.promises.writeFile(patchPath, patch, "utf8");
6963
7053
  written.push(patchPath);
@@ -6970,9 +7060,9 @@ async function runInit(opts) {
6970
7060
  const graph = getGraph(graphKey);
6971
7061
  const projectPaths = pathsForProject(
6972
7062
  graphKey,
6973
- import_node_path39.default.join(opts.scanPath, "neat-out")
7063
+ import_node_path40.default.join(opts.scanPath, "neat-out")
6974
7064
  );
6975
- const errorsPath = import_node_path39.default.join(import_node_path39.default.dirname(opts.outPath), import_node_path39.default.basename(projectPaths.errorsPath));
7065
+ const errorsPath = import_node_path40.default.join(import_node_path40.default.dirname(opts.outPath), import_node_path40.default.basename(projectPaths.errorsPath));
6976
7066
  const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
6977
7067
  await saveGraphToDisk(graph, opts.outPath);
6978
7068
  written.push(opts.outPath);
@@ -7022,6 +7112,7 @@ async function runInit(opts) {
7022
7112
  if (result.extractionErrors > 0) {
7023
7113
  console.log(`errors: ${errorsPath}`);
7024
7114
  }
7115
+ console.log(formatPrecisionFloorBanner(result.extractedDropped));
7025
7116
  const incompatibilities = findIncompatibilities(nodes);
7026
7117
  if (incompatibilities.length > 0) {
7027
7118
  console.log("");
@@ -7051,9 +7142,9 @@ var CLAUDE_SKILL_CONFIG = {
7051
7142
  };
7052
7143
  function claudeConfigPath() {
7053
7144
  const override = process.env.NEAT_CLAUDE_CONFIG;
7054
- if (override && override.length > 0) return import_node_path39.default.resolve(override);
7145
+ if (override && override.length > 0) return import_node_path40.default.resolve(override);
7055
7146
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
7056
- return import_node_path39.default.join(home, ".claude.json");
7147
+ return import_node_path40.default.join(home, ".claude.json");
7057
7148
  }
7058
7149
  async function runSkill(opts) {
7059
7150
  const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
@@ -7077,7 +7168,7 @@ async function runSkill(opts) {
7077
7168
  ...existing,
7078
7169
  mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
7079
7170
  };
7080
- await import_node_fs25.promises.mkdir(import_node_path39.default.dirname(target), { recursive: true });
7171
+ await import_node_fs25.promises.mkdir(import_node_path40.default.dirname(target), { recursive: true });
7081
7172
  await import_node_fs25.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
7082
7173
  console.log(`neat skill: wrote mcpServers.neat to ${target}`);
7083
7174
  console.log("restart Claude Code to pick up the new MCP server.");
@@ -7112,12 +7203,12 @@ async function main() {
7112
7203
  console.error("neat init: --apply and --dry-run are mutually exclusive");
7113
7204
  process.exit(2);
7114
7205
  }
7115
- const scanPath = import_node_path39.default.resolve(target);
7206
+ const scanPath = import_node_path40.default.resolve(target);
7116
7207
  const projectExplicit = parsed.project !== null;
7117
- const projectName = projectExplicit ? project : import_node_path39.default.basename(scanPath);
7208
+ const projectName = projectExplicit ? project : import_node_path40.default.basename(scanPath);
7118
7209
  const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
7119
- const fallback = pathsForProject(projectKey, import_node_path39.default.join(scanPath, "neat-out")).snapshotPath;
7120
- const outPath = import_node_path39.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
7210
+ const fallback = pathsForProject(projectKey, import_node_path40.default.join(scanPath, "neat-out")).snapshotPath;
7211
+ const outPath = import_node_path40.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
7121
7212
  const result = await runInit({
7122
7213
  scanPath,
7123
7214
  outPath,
@@ -7137,21 +7228,21 @@ async function main() {
7137
7228
  usage();
7138
7229
  process.exit(2);
7139
7230
  }
7140
- const scanPath = import_node_path39.default.resolve(target);
7231
+ const scanPath = import_node_path40.default.resolve(target);
7141
7232
  const stat = await import_node_fs25.promises.stat(scanPath).catch(() => null);
7142
7233
  if (!stat || !stat.isDirectory()) {
7143
7234
  console.error(`neat watch: ${scanPath} is not a directory`);
7144
7235
  process.exit(2);
7145
7236
  }
7146
- const projectPaths = pathsForProject(project, import_node_path39.default.join(scanPath, "neat-out"));
7147
- const outPath = import_node_path39.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
7148
- const errorsPath = import_node_path39.default.resolve(
7149
- process.env.NEAT_ERRORS_PATH ?? import_node_path39.default.join(import_node_path39.default.dirname(outPath), import_node_path39.default.basename(projectPaths.errorsPath))
7237
+ const projectPaths = pathsForProject(project, import_node_path40.default.join(scanPath, "neat-out"));
7238
+ const outPath = import_node_path40.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
7239
+ const errorsPath = import_node_path40.default.resolve(
7240
+ process.env.NEAT_ERRORS_PATH ?? import_node_path40.default.join(import_node_path40.default.dirname(outPath), import_node_path40.default.basename(projectPaths.errorsPath))
7150
7241
  );
7151
- const staleEventsPath = import_node_path39.default.resolve(
7152
- process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path39.default.join(import_node_path39.default.dirname(outPath), import_node_path39.default.basename(projectPaths.staleEventsPath))
7242
+ const staleEventsPath = import_node_path40.default.resolve(
7243
+ process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path40.default.join(import_node_path40.default.dirname(outPath), import_node_path40.default.basename(projectPaths.staleEventsPath))
7153
7244
  );
7154
- const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path39.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
7245
+ const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path40.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
7155
7246
  const handle = await startWatch(getGraph(project), {
7156
7247
  scanPath,
7157
7248
  outPath,