@neat.is/core 0.4.15 → 0.4.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -57,13 +57,47 @@ import {
57
57
  } from "./chunk-6CO7C4IU.js";
58
58
 
59
59
  // src/cli.ts
60
- import path8 from "path";
61
- import { promises as fs7, readFileSync } from "fs";
60
+ import path9 from "path";
61
+ import { promises as fs7 } from "fs";
62
+
63
+ // src/banner.ts
64
+ import path from "path";
65
+ import { readFileSync } from "fs";
62
66
  import { fileURLToPath } from "url";
67
+ function readPackageVersion() {
68
+ const here = typeof __dirname !== "undefined" ? __dirname : path.dirname(fileURLToPath(import.meta.url));
69
+ const candidates = [
70
+ path.resolve(here, "../package.json"),
71
+ path.resolve(here, "../../package.json")
72
+ ];
73
+ for (const candidate of candidates) {
74
+ try {
75
+ const raw = readFileSync(candidate, "utf8");
76
+ const parsed = JSON.parse(raw);
77
+ if (parsed.name === "@neat.is/core" && typeof parsed.version === "string") {
78
+ return parsed.version;
79
+ }
80
+ } catch {
81
+ }
82
+ }
83
+ return "unknown";
84
+ }
85
+ function printBanner() {
86
+ console.log("\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557");
87
+ console.log("\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D");
88
+ console.log("\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 ");
89
+ console.log("\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2551 ");
90
+ console.log("\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 ");
91
+ console.log("\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D ");
92
+ console.log("");
93
+ console.log(" Network Expressive Architecting Tool");
94
+ console.log(` neat.is \xB7 v${readPackageVersion()} \xB7 Apache 2.0`);
95
+ console.log("");
96
+ }
63
97
 
64
98
  // src/gitignore.ts
65
99
  import { promises as fs } from "fs";
66
- import path from "path";
100
+ import path2 from "path";
67
101
  var NEAT_OUT_LINE = "neat-out/";
68
102
  var NEAT_HEADER = "# NEAT \u2014 machine-local snapshots and events";
69
103
  function isNeatOutLine(line) {
@@ -71,7 +105,7 @@ function isNeatOutLine(line) {
71
105
  return trimmed === "neat-out/" || trimmed === "neat-out";
72
106
  }
73
107
  async function ensureNeatOutIgnored(projectDir) {
74
- const file = path.join(projectDir, ".gitignore");
108
+ const file = path2.join(projectDir, ".gitignore");
75
109
  let existing = null;
76
110
  try {
77
111
  existing = await fs.readFile(file, "utf8");
@@ -193,7 +227,7 @@ function formatIncompat(inc) {
193
227
 
194
228
  // src/watch.ts
195
229
  import fs2 from "fs";
196
- import path2 from "path";
230
+ import path3 from "path";
197
231
  import chokidar from "chokidar";
198
232
  var ALL_PHASES = [
199
233
  "services",
@@ -206,8 +240,8 @@ var ALL_PHASES = [
206
240
  ];
207
241
  function classifyChange(relPath) {
208
242
  const phases = /* @__PURE__ */ new Set();
209
- const base = path2.basename(relPath).toLowerCase();
210
- const segments = relPath.split(path2.sep).map((s) => s.toLowerCase());
243
+ const base = path3.basename(relPath).toLowerCase();
244
+ const segments = relPath.split(path3.sep).map((s) => s.toLowerCase());
211
245
  if (base === "package.json" || base === "requirements.txt" || base === "pyproject.toml" || base === "setup.py") {
212
246
  phases.add("services");
213
247
  phases.add("aliases");
@@ -328,9 +362,9 @@ function countWatchableDirs(scanPath, limit) {
328
362
  for (const e of entries) {
329
363
  if (count >= limit) return;
330
364
  if (!e.isDirectory()) continue;
331
- if (IGNORED_WATCH_PATHS.some((re) => re.test(path2.join(dir, e.name) + path2.sep))) continue;
365
+ if (IGNORED_WATCH_PATHS.some((re) => re.test(path3.join(dir, e.name) + path3.sep))) continue;
332
366
  count++;
333
- if (depth < 2) visit(path2.join(dir, e.name), depth + 1);
367
+ if (depth < 2) visit(path3.join(dir, e.name), depth + 1);
334
368
  }
335
369
  };
336
370
  visit(scanPath, 0);
@@ -348,8 +382,8 @@ async function startWatch(graph, opts) {
348
382
  const projectName = opts.project ?? DEFAULT_PROJECT;
349
383
  await loadGraphFromDisk(graph, opts.outPath);
350
384
  const detachEventBus = attachGraphToEventBus(graph, { project: projectName });
351
- const policyFilePath = path2.join(opts.scanPath, "policy.json");
352
- const policyViolationsPath = path2.join(path2.dirname(opts.outPath), "policy-violations.ndjson");
385
+ const policyFilePath = path3.join(opts.scanPath, "policy.json");
386
+ const policyViolationsPath = path3.join(path3.dirname(opts.outPath), "policy-violations.ndjson");
353
387
  let policies = [];
354
388
  try {
355
389
  policies = await loadPolicyFile(policyFilePath);
@@ -400,7 +434,7 @@ async function startWatch(graph, opts) {
400
434
  assertBindAuthority(host, auth.authToken);
401
435
  const port = opts.port ?? 8080;
402
436
  const otelPort = opts.otelPort ?? 4318;
403
- const cachePath = opts.embeddingsCachePath ?? path2.join(path2.dirname(opts.outPath), "embeddings.json");
437
+ const cachePath = opts.embeddingsCachePath ?? path3.join(path3.dirname(opts.outPath), "embeddings.json");
404
438
  let searchIndex;
405
439
  try {
406
440
  searchIndex = await buildSearchIndex(graph, { cachePath });
@@ -418,7 +452,7 @@ async function startWatch(graph, opts) {
418
452
  // Paths are derived from the explicit options the watch caller passes
419
453
  // — pathsForProject is only used to fill in the embeddings/snapshot
420
454
  // fields so the registry shape is complete.
421
- ...pathsForProject(projectName, path2.dirname(opts.outPath)),
455
+ ...pathsForProject(projectName, path3.dirname(opts.outPath)),
422
456
  snapshotPath: opts.outPath,
423
457
  errorsPath: opts.errorsPath,
424
458
  staleEventsPath: opts.staleEventsPath
@@ -505,9 +539,9 @@ async function startWatch(graph, opts) {
505
539
  };
506
540
  const onPath = (absPath) => {
507
541
  if (shouldIgnore(absPath)) return;
508
- const rel = path2.relative(opts.scanPath, absPath);
542
+ const rel = path3.relative(opts.scanPath, absPath);
509
543
  if (!rel || rel.startsWith("..")) return;
510
- pendingPaths.add(rel.split(path2.sep).join("/"));
544
+ pendingPaths.add(rel.split(path3.sep).join("/"));
511
545
  const phases = classifyChange(rel);
512
546
  if (phases.size === 0) {
513
547
  for (const p of ALL_PHASES) pending.add(p);
@@ -561,7 +595,7 @@ async function startWatch(graph, opts) {
561
595
 
562
596
  // src/deploy/detect.ts
563
597
  import { promises as fs3 } from "fs";
564
- import path3 from "path";
598
+ import path4 from "path";
565
599
  import { spawn } from "child_process";
566
600
  import { randomBytes } from "crypto";
567
601
  function generateToken() {
@@ -661,7 +695,7 @@ async function runDeploy(opts = {}) {
661
695
  const token = generateToken();
662
696
  switch (substrate) {
663
697
  case "docker-compose": {
664
- const artifactPath = path3.join(cwd, "docker-compose.neat.yml");
698
+ const artifactPath = path4.join(cwd, "docker-compose.neat.yml");
665
699
  const contents = emitDockerCompose(cwd);
666
700
  await fs3.writeFile(artifactPath, contents, "utf8");
667
701
  return {
@@ -669,11 +703,11 @@ async function runDeploy(opts = {}) {
669
703
  artifactPath,
670
704
  token,
671
705
  contents,
672
- startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${path3.basename(artifactPath)} up -d`
706
+ startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${path4.basename(artifactPath)} up -d`
673
707
  };
674
708
  }
675
709
  case "systemd": {
676
- const artifactPath = path3.join(cwd, "neat.service");
710
+ const artifactPath = path4.join(cwd, "neat.service");
677
711
  const contents = emitSystemdUnit(cwd);
678
712
  await fs3.writeFile(artifactPath, contents, "utf8");
679
713
  return {
@@ -705,7 +739,7 @@ ${contents}EOF
705
739
 
706
740
  // src/installers/javascript.ts
707
741
  import { promises as fs4 } from "fs";
708
- import path4 from "path";
742
+ import path5 from "path";
709
743
  import semver from "semver";
710
744
 
711
745
  // src/installers/templates.ts
@@ -1251,11 +1285,11 @@ var OTEL_ENV = {
1251
1285
  value: "http://localhost:4318/projects/<project>/v1/traces"
1252
1286
  };
1253
1287
  function serviceNodeName(pkg, serviceDir) {
1254
- return pkg.name ?? path4.basename(serviceDir);
1288
+ return pkg.name ?? path5.basename(serviceDir);
1255
1289
  }
1256
1290
  function projectToken(pkg, serviceDir, project) {
1257
1291
  if (project && project.length > 0) return project;
1258
- return pkg.name ?? path4.basename(serviceDir);
1292
+ return pkg.name ?? path5.basename(serviceDir);
1259
1293
  }
1260
1294
  async function readJsonFile(p) {
1261
1295
  try {
@@ -1268,16 +1302,16 @@ async function readJsonFile(p) {
1268
1302
  async function detectRuntimeKind(pkgRoot, pkg) {
1269
1303
  const deps = allDeps(pkg);
1270
1304
  if ("react-native" in deps || "expo" in deps) return "react-native";
1271
- const appJson = await readJsonFile(path4.join(pkgRoot, "app.json"));
1305
+ const appJson = await readJsonFile(path5.join(pkgRoot, "app.json"));
1272
1306
  if (appJson && typeof appJson === "object" && "expo" in appJson) {
1273
1307
  return "react-native";
1274
1308
  }
1275
- if (await exists(path4.join(pkgRoot, "vite.config.js")) || await exists(path4.join(pkgRoot, "vite.config.ts")) || await exists(path4.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
1309
+ if (await exists(path5.join(pkgRoot, "vite.config.js")) || await exists(path5.join(pkgRoot, "vite.config.ts")) || await exists(path5.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
1276
1310
  return "browser-bundle";
1277
1311
  }
1278
- if (await exists(path4.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
1279
- if (await exists(path4.join(pkgRoot, "bun.lockb"))) return "bun";
1280
- if (await exists(path4.join(pkgRoot, "deno.json")) || await exists(path4.join(pkgRoot, "deno.lock"))) {
1312
+ if (await exists(path5.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
1313
+ if (await exists(path5.join(pkgRoot, "bun.lockb"))) return "bun";
1314
+ if (await exists(path5.join(pkgRoot, "deno.json")) || await exists(path5.join(pkgRoot, "deno.lock"))) {
1281
1315
  return "deno";
1282
1316
  }
1283
1317
  const engines = pkg.engines ?? {};
@@ -1286,7 +1320,7 @@ async function detectRuntimeKind(pkgRoot, pkg) {
1286
1320
  }
1287
1321
  async function readPackageJson(serviceDir) {
1288
1322
  try {
1289
- const raw = await fs4.readFile(path4.join(serviceDir, "package.json"), "utf8");
1323
+ const raw = await fs4.readFile(path5.join(serviceDir, "package.json"), "utf8");
1290
1324
  return JSON.parse(raw);
1291
1325
  } catch {
1292
1326
  return null;
@@ -1330,7 +1364,7 @@ function needsVersionUpgrade(installed, expected) {
1330
1364
  var NEXT_CONFIG_CANDIDATES = ["next.config.js", "next.config.ts", "next.config.mjs"];
1331
1365
  async function findNextConfig(serviceDir) {
1332
1366
  for (const name of NEXT_CONFIG_CANDIDATES) {
1333
- const candidate = path4.join(serviceDir, name);
1367
+ const candidate = path5.join(serviceDir, name);
1334
1368
  if (await exists(candidate)) return candidate;
1335
1369
  }
1336
1370
  return null;
@@ -1357,7 +1391,7 @@ function hasRemixDependency(pkg) {
1357
1391
  }
1358
1392
  async function findRemixEntry(serviceDir) {
1359
1393
  for (const rel of REMIX_ENTRY_CANDIDATES) {
1360
- const candidate = path4.join(serviceDir, rel);
1394
+ const candidate = path5.join(serviceDir, rel);
1361
1395
  if (await exists(candidate)) return candidate;
1362
1396
  }
1363
1397
  return null;
@@ -1369,14 +1403,14 @@ function hasSvelteKitDependency(pkg) {
1369
1403
  }
1370
1404
  async function findSvelteKitHooks(serviceDir) {
1371
1405
  for (const rel of SVELTEKIT_HOOKS_CANDIDATES) {
1372
- const candidate = path4.join(serviceDir, rel);
1406
+ const candidate = path5.join(serviceDir, rel);
1373
1407
  if (await exists(candidate)) return candidate;
1374
1408
  }
1375
1409
  return null;
1376
1410
  }
1377
1411
  async function findSvelteKitConfig(serviceDir) {
1378
1412
  for (const rel of SVELTEKIT_CONFIG_CANDIDATES) {
1379
- const candidate = path4.join(serviceDir, rel);
1413
+ const candidate = path5.join(serviceDir, rel);
1380
1414
  if (await exists(candidate)) return candidate;
1381
1415
  }
1382
1416
  return null;
@@ -1387,7 +1421,7 @@ function hasNuxtDependency(pkg) {
1387
1421
  }
1388
1422
  async function findNuxtConfig(serviceDir) {
1389
1423
  for (const name of NUXT_CONFIG_CANDIDATES) {
1390
- const candidate = path4.join(serviceDir, name);
1424
+ const candidate = path5.join(serviceDir, name);
1391
1425
  if (await exists(candidate)) return candidate;
1392
1426
  }
1393
1427
  return null;
@@ -1398,7 +1432,7 @@ function hasAstroDependency(pkg) {
1398
1432
  }
1399
1433
  async function findAstroConfig(serviceDir) {
1400
1434
  for (const name of ASTRO_CONFIG_CANDIDATES) {
1401
- const candidate = path4.join(serviceDir, name);
1435
+ const candidate = path5.join(serviceDir, name);
1402
1436
  if (await exists(candidate)) return candidate;
1403
1437
  }
1404
1438
  return null;
@@ -1412,7 +1446,7 @@ function parseNextMajor(range) {
1412
1446
  return Number.isFinite(n) ? n : null;
1413
1447
  }
1414
1448
  async function isTypeScriptProject(serviceDir) {
1415
- return exists(path4.join(serviceDir, "tsconfig.json"));
1449
+ return exists(path5.join(serviceDir, "tsconfig.json"));
1416
1450
  }
1417
1451
  var INDEX_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs", ".cjs"];
1418
1452
  var INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `index${ext}`);
@@ -1458,7 +1492,7 @@ function entryFromScript(script) {
1458
1492
  }
1459
1493
  async function resolveEntry(serviceDir, pkg) {
1460
1494
  if (typeof pkg.main === "string" && pkg.main.length > 0) {
1461
- const candidate = path4.resolve(serviceDir, pkg.main);
1495
+ const candidate = path5.resolve(serviceDir, pkg.main);
1462
1496
  if (await exists(candidate)) return candidate;
1463
1497
  }
1464
1498
  if (pkg.bin) {
@@ -1472,36 +1506,36 @@ async function resolveEntry(serviceDir, pkg) {
1472
1506
  if (typeof first === "string") binEntry = first;
1473
1507
  }
1474
1508
  if (binEntry) {
1475
- const candidate = path4.resolve(serviceDir, binEntry);
1509
+ const candidate = path5.resolve(serviceDir, binEntry);
1476
1510
  if (await exists(candidate)) return candidate;
1477
1511
  }
1478
1512
  }
1479
1513
  const startEntry = entryFromScript(pkg.scripts?.start);
1480
1514
  if (startEntry) {
1481
- const candidate = path4.resolve(serviceDir, startEntry);
1515
+ const candidate = path5.resolve(serviceDir, startEntry);
1482
1516
  if (await exists(candidate)) return candidate;
1483
1517
  }
1484
1518
  const devEntry = entryFromScript(pkg.scripts?.dev);
1485
1519
  if (devEntry) {
1486
- const candidate = path4.resolve(serviceDir, devEntry);
1520
+ const candidate = path5.resolve(serviceDir, devEntry);
1487
1521
  if (await exists(candidate)) return candidate;
1488
1522
  }
1489
1523
  for (const rel of SRC_INDEX_CANDIDATES) {
1490
- const candidate = path4.join(serviceDir, rel);
1524
+ const candidate = path5.join(serviceDir, rel);
1491
1525
  if (await exists(candidate)) return candidate;
1492
1526
  }
1493
1527
  for (const rel of SRC_NAMED_CANDIDATES) {
1494
- const candidate = path4.join(serviceDir, rel);
1528
+ const candidate = path5.join(serviceDir, rel);
1495
1529
  if (await exists(candidate)) return candidate;
1496
1530
  }
1497
1531
  for (const name of INDEX_CANDIDATES) {
1498
- const candidate = path4.join(serviceDir, name);
1532
+ const candidate = path5.join(serviceDir, name);
1499
1533
  if (await exists(candidate)) return candidate;
1500
1534
  }
1501
1535
  return null;
1502
1536
  }
1503
1537
  function dispatchEntry(entryFile, pkg) {
1504
- const ext = path4.extname(entryFile).toLowerCase();
1538
+ const ext = path5.extname(entryFile).toLowerCase();
1505
1539
  if (ext === ".ts" || ext === ".tsx") return "ts";
1506
1540
  if (ext === ".mjs") return "esm";
1507
1541
  if (ext === ".cjs") return "cjs";
@@ -1518,9 +1552,9 @@ function otelInitContents(flavor) {
1518
1552
  return OTEL_INIT_CJS;
1519
1553
  }
1520
1554
  function injectionLine(flavor, entryFile, otelInitFile) {
1521
- let rel = path4.relative(path4.dirname(entryFile), otelInitFile);
1555
+ let rel = path5.relative(path5.dirname(entryFile), otelInitFile);
1522
1556
  if (!rel.startsWith(".")) rel = `./${rel}`;
1523
- rel = rel.split(path4.sep).join("/");
1557
+ rel = rel.split(path5.sep).join("/");
1524
1558
  if (flavor === "cjs") return `require('${rel}')`;
1525
1559
  if (flavor === "esm") return `import '${rel}'`;
1526
1560
  const tsRel = rel.replace(/\.ts$/, "");
@@ -1533,23 +1567,23 @@ function lineIsOtelInjection(line) {
1533
1567
  }
1534
1568
  async function detectsSrcLayout(serviceDir) {
1535
1569
  const [hasSrcApp, hasSrcPages, hasRootApp, hasRootPages] = await Promise.all([
1536
- exists(path4.join(serviceDir, "src", "app")),
1537
- exists(path4.join(serviceDir, "src", "pages")),
1538
- exists(path4.join(serviceDir, "app")),
1539
- exists(path4.join(serviceDir, "pages"))
1570
+ exists(path5.join(serviceDir, "src", "app")),
1571
+ exists(path5.join(serviceDir, "src", "pages")),
1572
+ exists(path5.join(serviceDir, "app")),
1573
+ exists(path5.join(serviceDir, "pages"))
1540
1574
  ]);
1541
1575
  return (hasSrcApp || hasSrcPages) && !hasRootApp && !hasRootPages;
1542
1576
  }
1543
1577
  async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
1544
1578
  const useTs = await isTypeScriptProject(serviceDir);
1545
1579
  const srcLayout = await detectsSrcLayout(serviceDir);
1546
- const baseDir = srcLayout ? path4.join(serviceDir, "src") : serviceDir;
1547
- const instrumentationFile = path4.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
1548
- const instrumentationNodeFile = path4.join(
1580
+ const baseDir = srcLayout ? path5.join(serviceDir, "src") : serviceDir;
1581
+ const instrumentationFile = path5.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
1582
+ const instrumentationNodeFile = path5.join(
1549
1583
  baseDir,
1550
1584
  useTs ? "instrumentation.node.ts" : "instrumentation.node.js"
1551
1585
  );
1552
- const envNeatFile = path4.join(baseDir, ".env.neat");
1586
+ const envNeatFile = path5.join(baseDir, ".env.neat");
1553
1587
  const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
1554
1588
  const dependencyEdits = [];
1555
1589
  for (const sdk of SDK_PACKAGES) {
@@ -1654,7 +1688,7 @@ function buildDependencyEdits(pkg, manifestPath) {
1654
1688
  return edits;
1655
1689
  }
1656
1690
  async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
1657
- const envNeatFile = path4.join(serviceDir, ".env.neat");
1691
+ const envNeatFile = path5.join(serviceDir, ".env.neat");
1658
1692
  if (!await exists(envNeatFile)) {
1659
1693
  generatedFiles.push({
1660
1694
  file: envNeatFile,
@@ -1689,7 +1723,7 @@ function fileImportsOtelHook(raw, specifiers) {
1689
1723
  }
1690
1724
  async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
1691
1725
  const useTs = await isTypeScriptProject(serviceDir);
1692
- const otelServerFile = path4.join(
1726
+ const otelServerFile = path5.join(
1693
1727
  serviceDir,
1694
1728
  useTs ? "app/otel.server.ts" : "app/otel.server.js"
1695
1729
  );
@@ -1746,11 +1780,11 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
1746
1780
  }
1747
1781
  async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project) {
1748
1782
  const useTs = await isTypeScriptProject(serviceDir);
1749
- const otelInitFile = path4.join(
1783
+ const otelInitFile = path5.join(
1750
1784
  serviceDir,
1751
1785
  useTs ? "src/otel-init.ts" : "src/otel-init.js"
1752
1786
  );
1753
- const resolvedHooksFile = hooksFile ?? path4.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
1787
+ const resolvedHooksFile = hooksFile ?? path5.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
1754
1788
  const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
1755
1789
  const generatedFiles = [];
1756
1790
  const entrypointEdits = [];
@@ -1812,11 +1846,11 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
1812
1846
  }
1813
1847
  async function planNuxt(serviceDir, pkg, manifestPath, project) {
1814
1848
  const useTs = await isTypeScriptProject(serviceDir);
1815
- const otelPluginFile = path4.join(
1849
+ const otelPluginFile = path5.join(
1816
1850
  serviceDir,
1817
1851
  useTs ? "server/plugins/otel.ts" : "server/plugins/otel.js"
1818
1852
  );
1819
- const otelInitFile = path4.join(
1853
+ const otelInitFile = path5.join(
1820
1854
  serviceDir,
1821
1855
  useTs ? "server/plugins/otel-init.ts" : "server/plugins/otel-init.js"
1822
1856
  );
@@ -1867,19 +1901,19 @@ async function planNuxt(serviceDir, pkg, manifestPath, project) {
1867
1901
  var ASTRO_MIDDLEWARE_CANDIDATES = ["src/middleware.ts", "src/middleware.js"];
1868
1902
  async function findAstroMiddleware(serviceDir) {
1869
1903
  for (const rel of ASTRO_MIDDLEWARE_CANDIDATES) {
1870
- const candidate = path4.join(serviceDir, rel);
1904
+ const candidate = path5.join(serviceDir, rel);
1871
1905
  if (await exists(candidate)) return candidate;
1872
1906
  }
1873
1907
  return null;
1874
1908
  }
1875
1909
  async function planAstro(serviceDir, pkg, manifestPath, project) {
1876
1910
  const useTs = await isTypeScriptProject(serviceDir);
1877
- const otelInitFile = path4.join(
1911
+ const otelInitFile = path5.join(
1878
1912
  serviceDir,
1879
1913
  useTs ? "src/otel-init.ts" : "src/otel-init.js"
1880
1914
  );
1881
1915
  const existingMiddleware = await findAstroMiddleware(serviceDir);
1882
- const middlewareFile = existingMiddleware ?? path4.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
1916
+ const middlewareFile = existingMiddleware ?? path5.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
1883
1917
  const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
1884
1918
  const generatedFiles = [];
1885
1919
  const entrypointEdits = [];
@@ -1975,7 +2009,7 @@ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
1975
2009
  }
1976
2010
  async function plan(serviceDir, opts) {
1977
2011
  const pkg = await readPackageJson(serviceDir);
1978
- const manifestPath = path4.join(serviceDir, "package.json");
2012
+ const manifestPath = path5.join(serviceDir, "package.json");
1979
2013
  const project = opts?.project;
1980
2014
  const empty = {
1981
2015
  language: "javascript",
@@ -2010,8 +2044,8 @@ async function plan(serviceDir, opts) {
2010
2044
  return { ...empty, libOnly: true };
2011
2045
  }
2012
2046
  const flavor = dispatchEntry(entryFile, pkg);
2013
- const otelInitFile = path4.join(path4.dirname(entryFile), otelInitFilename(flavor));
2014
- const envNeatFile = path4.join(serviceDir, ".env.neat");
2047
+ const otelInitFile = path5.join(path5.dirname(entryFile), otelInitFilename(flavor));
2048
+ const envNeatFile = path5.join(serviceDir, ".env.neat");
2015
2049
  const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
2016
2050
  const dependencyEdits = [];
2017
2051
  for (const sdk of SDK_PACKAGES) {
@@ -2080,13 +2114,13 @@ async function plan(serviceDir, opts) {
2080
2114
  };
2081
2115
  }
2082
2116
  function isAllowedWritePath(serviceDir, target) {
2083
- const rel = path4.relative(serviceDir, target);
2117
+ const rel = path5.relative(serviceDir, target);
2084
2118
  if (rel.startsWith("..")) return false;
2085
- const base = path4.basename(target);
2119
+ const base = path5.basename(target);
2086
2120
  if (base === "package.json") return true;
2087
2121
  if (base === ".env.neat") return true;
2088
2122
  if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
2089
- const relPosix = rel.split(path4.sep).join("/");
2123
+ const relPosix = rel.split(path5.sep).join("/");
2090
2124
  if (/^instrumentation(?:\.node)?\.(?:js|cjs|mjs|ts)$/.test(base)) {
2091
2125
  if (relPosix === base) return true;
2092
2126
  if (relPosix === `src/${base}`) return true;
@@ -2103,7 +2137,7 @@ function isAllowedWritePath(serviceDir, target) {
2103
2137
  return false;
2104
2138
  }
2105
2139
  async function writeAtomic(file, contents) {
2106
- await fs4.mkdir(path4.dirname(file), { recursive: true });
2140
+ await fs4.mkdir(path5.dirname(file), { recursive: true });
2107
2141
  const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
2108
2142
  await fs4.writeFile(tmp, contents, "utf8");
2109
2143
  await fs4.rename(tmp, file);
@@ -2287,7 +2321,7 @@ async function rollback(installPlan, originals, createdFiles) {
2287
2321
  ...removed.map((f) => `removed: ${f}`),
2288
2322
  ""
2289
2323
  ];
2290
- const rollbackPath = path4.join(installPlan.serviceDir, "neat-rollback.patch");
2324
+ const rollbackPath = path5.join(installPlan.serviceDir, "neat-rollback.patch");
2291
2325
  await fs4.writeFile(rollbackPath, lines.join("\n"), "utf8");
2292
2326
  }
2293
2327
  function injectInstrumentationHook(raw) {
@@ -2317,7 +2351,7 @@ var javascriptInstaller = {
2317
2351
 
2318
2352
  // src/installers/python.ts
2319
2353
  import { promises as fs5 } from "fs";
2320
- import path5 from "path";
2354
+ import path6 from "path";
2321
2355
  var SDK_PACKAGES2 = [
2322
2356
  { name: "opentelemetry-distro", version: ">=0.49b0" },
2323
2357
  { name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
@@ -2338,7 +2372,7 @@ async function exists2(p) {
2338
2372
  async function detect2(serviceDir) {
2339
2373
  const markers = ["requirements.txt", "pyproject.toml", "setup.py"];
2340
2374
  for (const m of markers) {
2341
- if (await exists2(path5.join(serviceDir, m))) return true;
2375
+ if (await exists2(path6.join(serviceDir, m))) return true;
2342
2376
  }
2343
2377
  return false;
2344
2378
  }
@@ -2348,7 +2382,7 @@ function reqPackageName(line) {
2348
2382
  return head.replace(/[<>=!~].*$/, "").toLowerCase();
2349
2383
  }
2350
2384
  async function planRequirementsTxtEdits(serviceDir) {
2351
- const file = path5.join(serviceDir, "requirements.txt");
2385
+ const file = path6.join(serviceDir, "requirements.txt");
2352
2386
  if (!await exists2(file)) return null;
2353
2387
  const raw = await fs5.readFile(file, "utf8");
2354
2388
  const presentNames = new Set(
@@ -2358,7 +2392,7 @@ async function planRequirementsTxtEdits(serviceDir) {
2358
2392
  return { manifest: file, missing: [...missing] };
2359
2393
  }
2360
2394
  async function planProcfileEdits(serviceDir) {
2361
- const procfile = path5.join(serviceDir, "Procfile");
2395
+ const procfile = path6.join(serviceDir, "Procfile");
2362
2396
  if (!await exists2(procfile)) return [];
2363
2397
  const raw = await fs5.readFile(procfile, "utf8");
2364
2398
  const edits = [];
@@ -2447,7 +2481,7 @@ async function apply2(installPlan) {
2447
2481
  if (raw === void 0) {
2448
2482
  throw new Error(`python installer: cannot read ${file} during apply`);
2449
2483
  }
2450
- const base = path5.basename(file);
2484
+ const base = path6.basename(file);
2451
2485
  if (base === "requirements.txt") {
2452
2486
  const edits = installPlan.dependencyEdits.filter((e) => e.file === file);
2453
2487
  if (edits.length > 0) {
@@ -2486,7 +2520,7 @@ async function rollback2(installPlan, originals) {
2486
2520
  ...restored.map((f) => `restored: ${f}`),
2487
2521
  ""
2488
2522
  ];
2489
- const rollbackPath = path5.join(installPlan.serviceDir, "neat-rollback.patch");
2523
+ const rollbackPath = path6.join(installPlan.serviceDir, "neat-rollback.patch");
2490
2524
  await fs5.writeFile(rollbackPath, lines.join("\n"), "utf8");
2491
2525
  }
2492
2526
  var pythonInstaller = {
@@ -2611,7 +2645,7 @@ function renderPatch(sections) {
2611
2645
  import { promises as fs6 } from "fs";
2612
2646
  import http from "http";
2613
2647
  import net from "net";
2614
- import path6 from "path";
2648
+ import path7 from "path";
2615
2649
  import { spawn as spawn2 } from "child_process";
2616
2650
  import readline from "readline";
2617
2651
  async function extractAndPersist(opts) {
@@ -2620,7 +2654,7 @@ async function extractAndPersist(opts) {
2620
2654
  const graphKey = opts.projectExplicit ? opts.project : DEFAULT_PROJECT;
2621
2655
  resetGraph(graphKey);
2622
2656
  const graph = getGraph(graphKey);
2623
- const projectPaths = pathsForProject(graphKey, path6.join(opts.scanPath, "neat-out"));
2657
+ const projectPaths = pathsForProject(graphKey, path7.join(opts.scanPath, "neat-out"));
2624
2658
  const extraction = await extractFromDirectory(graph, opts.scanPath, {
2625
2659
  errorsPath: projectPaths.errorsPath
2626
2660
  });
@@ -2674,7 +2708,7 @@ async function applyInstallersOver(services, project, options = {}) {
2674
2708
  console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
2675
2709
  } else if (outcome.outcome === "react-native") {
2676
2710
  reactNative++;
2677
- const svcName = path6.basename(svc.dir);
2711
+ const svcName = path7.basename(svc.dir);
2678
2712
  console.log(
2679
2713
  `neat: ${svc.dir} detected as React Native / Expo
2680
2714
  The installer doesn't cover this runtime deterministically.
@@ -2685,7 +2719,7 @@ async function applyInstallersOver(services, project, options = {}) {
2685
2719
  );
2686
2720
  } else if (outcome.outcome === "bun") {
2687
2721
  bun++;
2688
- const svcName = path6.basename(svc.dir);
2722
+ const svcName = path7.basename(svc.dir);
2689
2723
  console.log(
2690
2724
  `neat: ${svc.dir} detected as Bun
2691
2725
  The installer doesn't cover this runtime deterministically.
@@ -2696,7 +2730,7 @@ async function applyInstallersOver(services, project, options = {}) {
2696
2730
  );
2697
2731
  } else if (outcome.outcome === "deno") {
2698
2732
  deno++;
2699
- const svcName = path6.basename(svc.dir);
2733
+ const svcName = path7.basename(svc.dir);
2700
2734
  console.log(
2701
2735
  `neat: ${svc.dir} detected as Deno
2702
2736
  The installer doesn't cover this runtime deterministically.
@@ -2707,7 +2741,7 @@ async function applyInstallersOver(services, project, options = {}) {
2707
2741
  );
2708
2742
  } else if (outcome.outcome === "cloudflare-workers") {
2709
2743
  cloudflareWorkers++;
2710
- const svcName = path6.basename(svc.dir);
2744
+ const svcName = path7.basename(svc.dir);
2711
2745
  console.log(
2712
2746
  `neat: ${svc.dir} detected as Cloudflare Workers
2713
2747
  The installer doesn't cover this runtime deterministically.
@@ -2718,7 +2752,7 @@ async function applyInstallersOver(services, project, options = {}) {
2718
2752
  );
2719
2753
  } else if (outcome.outcome === "electron") {
2720
2754
  electron++;
2721
- const svcName = path6.basename(svc.dir);
2755
+ const svcName = path7.basename(svc.dir);
2722
2756
  console.log(
2723
2757
  `neat: ${svc.dir} detected as Electron
2724
2758
  The installer doesn't cover this runtime deterministically.
@@ -2892,10 +2926,10 @@ function formatPortCollisionMessage(port) {
2892
2926
  ];
2893
2927
  }
2894
2928
  function spawnDaemonDetached() {
2895
- const here = path6.dirname(new URL(import.meta.url).pathname);
2929
+ const here = path7.dirname(new URL(import.meta.url).pathname);
2896
2930
  const candidates = [
2897
- path6.join(here, "neatd.cjs"),
2898
- path6.join(here, "neatd.js")
2931
+ path7.join(here, "neatd.cjs"),
2932
+ path7.join(here, "neatd.js")
2899
2933
  ];
2900
2934
  let entry2 = null;
2901
2935
  const fsSync = __require("fs");
@@ -2959,6 +2993,7 @@ async function runOrchestrator(opts) {
2959
2993
  result.exitCode = 2;
2960
2994
  return result;
2961
2995
  }
2996
+ printBanner();
2962
2997
  console.log(`neat: ${opts.scanPath}`);
2963
2998
  console.log("");
2964
2999
  const persisted = await extractAndPersist({
@@ -2969,6 +3004,13 @@ async function runOrchestrator(opts) {
2969
3004
  const { graph, services, languages } = persisted;
2970
3005
  result.steps.discovery = { services: services.length, languages };
2971
3006
  console.log(`discovered ${services.length} service(s) across ${languages.length} language(s)`);
3007
+ if (services.length === 0) {
3008
+ console.error(
3009
+ `neat: no services found in ${opts.scanPath} \u2014 run from inside your project root, or \`npx neat.is <path>\``
3010
+ );
3011
+ result.exitCode = 2;
3012
+ return result;
3013
+ }
2972
3014
  let runApply = !opts.noInstrument;
2973
3015
  if (runApply && !opts.yes && process.stdout.isTTY && process.stdin.isTTY) {
2974
3016
  runApply = await promptYesNo("instrument your services and open the dashboard?");
@@ -3092,10 +3134,16 @@ function printSummary(result, graph, dashboardUrl) {
3092
3134
  for (const [t, c] of [...byEdge.entries()].sort()) console.log(` ${t}: ${c}`);
3093
3135
  console.log("");
3094
3136
  console.log(`dashboard: ${dashboardUrl}`);
3137
+ const token = process.env.NEAT_AUTH_TOKEN;
3138
+ if (typeof token === "string" && token.length > 0) {
3139
+ console.log(`auth token: ${token}`);
3140
+ } else {
3141
+ console.log("running locally \u2014 open the dashboard, no token needed");
3142
+ }
3095
3143
  }
3096
3144
 
3097
3145
  // src/cli-verbs.ts
3098
- import path7 from "path";
3146
+ import path8 from "path";
3099
3147
 
3100
3148
  // src/cli-client.ts
3101
3149
  import { Provenance as Provenance2 } from "@neat.is/types";
@@ -3123,10 +3171,10 @@ function createHttpClient(baseUrl, bearerToken) {
3123
3171
  const root = baseUrl.replace(/\/$/, "");
3124
3172
  const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
3125
3173
  return {
3126
- async get(path9) {
3174
+ async get(path10) {
3127
3175
  let res;
3128
3176
  try {
3129
- res = await fetch(`${root}${path9}`, {
3177
+ res = await fetch(`${root}${path10}`, {
3130
3178
  headers: { ...authHeader }
3131
3179
  });
3132
3180
  } catch (err) {
@@ -3138,16 +3186,16 @@ function createHttpClient(baseUrl, bearerToken) {
3138
3186
  const body = await res.text().catch(() => "");
3139
3187
  throw new HttpError(
3140
3188
  res.status,
3141
- `${res.status} ${res.statusText} on GET ${path9}: ${body}`,
3189
+ `${res.status} ${res.statusText} on GET ${path10}: ${body}`,
3142
3190
  body
3143
3191
  );
3144
3192
  }
3145
3193
  return await res.json();
3146
3194
  },
3147
- async post(path9, body) {
3195
+ async post(path10, body) {
3148
3196
  let res;
3149
3197
  try {
3150
- res = await fetch(`${root}${path9}`, {
3198
+ res = await fetch(`${root}${path10}`, {
3151
3199
  method: "POST",
3152
3200
  headers: { "content-type": "application/json", ...authHeader },
3153
3201
  body: JSON.stringify(body)
@@ -3161,7 +3209,7 @@ function createHttpClient(baseUrl, bearerToken) {
3161
3209
  const text = await res.text().catch(() => "");
3162
3210
  throw new HttpError(
3163
3211
  res.status,
3164
- `${res.status} ${res.statusText} on POST ${path9}: ${text}`,
3212
+ `${res.status} ${res.statusText} on POST ${path10}: ${text}`,
3165
3213
  text
3166
3214
  );
3167
3215
  }
@@ -3175,12 +3223,12 @@ function projectPath(project, suffix) {
3175
3223
  }
3176
3224
  async function runRootCause(client, input) {
3177
3225
  const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
3178
- const path9 = projectPath(
3226
+ const path10 = projectPath(
3179
3227
  input.project,
3180
3228
  `/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
3181
3229
  );
3182
3230
  try {
3183
- const result = await client.get(path9);
3231
+ const result = await client.get(path10);
3184
3232
  const arrowPath = result.traversalPath.join(" \u2190 ");
3185
3233
  const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
3186
3234
  const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
@@ -3206,12 +3254,12 @@ async function runRootCause(client, input) {
3206
3254
  }
3207
3255
  async function runBlastRadius(client, input) {
3208
3256
  const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
3209
- const path9 = projectPath(
3257
+ const path10 = projectPath(
3210
3258
  input.project,
3211
3259
  `/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
3212
3260
  );
3213
3261
  try {
3214
- const result = await client.get(path9);
3262
+ const result = await client.get(path10);
3215
3263
  if (result.totalAffected === 0) {
3216
3264
  return {
3217
3265
  summary: `${result.origin} has no downstream dependencies. Nothing else would break if it failed.`
@@ -3245,12 +3293,12 @@ function formatBlastEntry(n) {
3245
3293
  }
3246
3294
  async function runDependencies(client, input) {
3247
3295
  const depth = input.depth ?? 3;
3248
- const path9 = projectPath(
3296
+ const path10 = projectPath(
3249
3297
  input.project,
3250
3298
  `/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
3251
3299
  );
3252
3300
  try {
3253
- const result = await client.get(path9);
3301
+ const result = await client.get(path10);
3254
3302
  if (result.total === 0) {
3255
3303
  return {
3256
3304
  summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
@@ -3331,9 +3379,9 @@ function formatDuration(ms) {
3331
3379
  return `${Math.round(h / 24)}d`;
3332
3380
  }
3333
3381
  async function runIncidents(client, input) {
3334
- const path9 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
3382
+ const path10 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
3335
3383
  try {
3336
- const body = await client.get(path9);
3384
+ const body = await client.get(path10);
3337
3385
  const events = body.events;
3338
3386
  if (events.length === 0) {
3339
3387
  return {
@@ -3623,7 +3671,7 @@ async function resolveProjectEntry(opts) {
3623
3671
  const cwd = opts.cwd ?? process.cwd();
3624
3672
  const resolvedCwd = await normalizeProjectPath(cwd);
3625
3673
  for (const entry2 of entries) {
3626
- if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${path7.sep}`)) {
3674
+ if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${path8.sep}`)) {
3627
3675
  return entry2;
3628
3676
  }
3629
3677
  }
@@ -3774,8 +3822,24 @@ async function runSync(opts) {
3774
3822
 
3775
3823
  // src/cli.ts
3776
3824
  import { DivergenceTypeSchema } from "@neat.is/types";
3825
+ function isNpxInvocation() {
3826
+ if (process.env.npm_command === "exec") return true;
3827
+ const execpath = process.env.npm_execpath ?? "";
3828
+ if (execpath.includes("npx")) return true;
3829
+ const entry2 = process.argv[1] ?? "";
3830
+ if (entry2.includes("/_npx/") || entry2.includes("\\_npx\\")) return true;
3831
+ return false;
3832
+ }
3833
+ function commandPrefix() {
3834
+ return isNpxInvocation() ? "npx neat.is" : "neat";
3835
+ }
3777
3836
  function usage() {
3778
- console.log("usage: neat <command> [args] [--project <name>]");
3837
+ const neat = commandPrefix();
3838
+ console.log("Installed via npx? Prefix commands with `npx neat.is`, or install once: `npm i -g neat.is`.");
3839
+ console.log("");
3840
+ console.log(`usage: ${neat} <command> [args] [--project <name>]`);
3841
+ console.log("");
3842
+ console.log(`Run \`${neat}\` with no command from inside your project to go zero-to-graph in one step.`);
3779
3843
  console.log("");
3780
3844
  console.log("lifecycle commands:");
3781
3845
  console.log(" init <path> One-time install: discover, extract, register, plan SDK install.");
@@ -3815,30 +3879,30 @@ function usage() {
3815
3879
  console.log("");
3816
3880
  console.log("query commands (mirror the MCP tools, ADR-050):");
3817
3881
  console.log(" root-cause <node-id> Walk inbound edges to find what broke first.");
3818
- console.log(" example: neat root-cause service:<name>");
3882
+ console.log(` example: ${neat} root-cause service:<name>`);
3819
3883
  console.log(" blast-radius <node-id> BFS outbound \u2014 what would break if this dies.");
3820
- console.log(" example: neat blast-radius database:<host>");
3884
+ console.log(` example: ${neat} blast-radius database:<host>`);
3821
3885
  console.log(" dependencies <node-id> Transitive outbound dependencies.");
3822
3886
  console.log(" Flags: --depth N (default 3, max 10)");
3823
- console.log(" example: neat dependencies service:<name> --depth 2");
3887
+ console.log(` example: ${neat} dependencies service:<name> --depth 2`);
3824
3888
  console.log(" observed-dependencies <node-id> OBSERVED-only outbound edges (runtime traffic).");
3825
- console.log(" example: neat observed-dependencies service:<name>");
3889
+ console.log(` example: ${neat} observed-dependencies service:<name>`);
3826
3890
  console.log(" incidents [<node-id>] Recent error events; per-node when an id is given.");
3827
3891
  console.log(" Flags: --limit N (default 20)");
3828
- console.log(" example: neat incidents service:<name> --limit 5");
3892
+ console.log(` example: ${neat} incidents service:<name> --limit 5`);
3829
3893
  console.log(" search <query> Semantic (or substring) match on node names/ids.");
3830
- console.log(' example: neat search "checkout"');
3894
+ console.log(` example: ${neat} search "checkout"`);
3831
3895
  console.log(" diff --against <snapshot> Compare the live graph to a saved snapshot.");
3832
- console.log(" example: neat diff --against ./snapshots/baseline.json");
3896
+ console.log(` example: ${neat} diff --against ./snapshots/baseline.json`);
3833
3897
  console.log(" stale-edges Recent OBSERVED \u2192 STALE transitions.");
3834
3898
  console.log(" Flags: --limit N, --edge-type CALLS|CONNECTS_TO|...");
3835
- console.log(" example: neat stale-edges --edge-type CALLS");
3899
+ console.log(` example: ${neat} stale-edges --edge-type CALLS`);
3836
3900
  console.log(" policies Current policy violations.");
3837
3901
  console.log(" Flags: --node <id>, --hypothetical-action <json>");
3838
- console.log(" example: neat policies --node service:<name> --json");
3902
+ console.log(` example: ${neat} policies --node service:<name> --json`);
3839
3903
  console.log(" divergences Where code (EXTRACTED) and production (OBSERVED) disagree.");
3840
3904
  console.log(" Flags: --type <list>, --min-confidence <0..1>, --node <id>");
3841
- console.log(" example: neat divergences --min-confidence 0.7");
3905
+ console.log(` example: ${neat} divergences --min-confidence 0.7`);
3842
3906
  console.log("");
3843
3907
  console.log("flags:");
3844
3908
  console.log(' --project <name> Name the project this command targets. Default: "default".');
@@ -3982,40 +4046,10 @@ function assignFlag(out, field, value) {
3982
4046
  ;
3983
4047
  out[field] = value;
3984
4048
  }
3985
- function readPackageVersion() {
3986
- const here = typeof __dirname !== "undefined" ? __dirname : path8.dirname(fileURLToPath(import.meta.url));
3987
- const candidates = [
3988
- path8.resolve(here, "../package.json"),
3989
- path8.resolve(here, "../../package.json")
3990
- ];
3991
- for (const candidate of candidates) {
3992
- try {
3993
- const raw = readFileSync(candidate, "utf8");
3994
- const parsed = JSON.parse(raw);
3995
- if (parsed.name === "@neat.is/core" && typeof parsed.version === "string") {
3996
- return parsed.version;
3997
- }
3998
- } catch {
3999
- }
4000
- }
4001
- return "unknown";
4002
- }
4003
4049
  function printVersion() {
4004
4050
  process.stdout.write(`${readPackageVersion()}
4005
4051
  `);
4006
4052
  }
4007
- function printBanner() {
4008
- console.log("\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557");
4009
- console.log("\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D");
4010
- console.log("\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 ");
4011
- console.log("\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2551 ");
4012
- console.log("\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 ");
4013
- console.log("\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D ");
4014
- console.log("");
4015
- console.log(" Network Expressive Architecting Tool");
4016
- console.log(` neat.is \xB7 v${readPackageVersion()} \xB7 Apache 2.0`);
4017
- console.log("");
4018
- }
4019
4053
  function printDiscoveryReport(opts, services) {
4020
4054
  const languages = [...new Set(services.map((s) => s.node.language))].sort();
4021
4055
  const mode = opts.dryRun ? "dry-run" : opts.apply ? "apply" : "patch-only";
@@ -4063,12 +4097,12 @@ async function runInit(opts) {
4063
4097
  printDiscoveryReport(opts, services);
4064
4098
  const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
4065
4099
  const patch = renderPatch(sections);
4066
- const patchPath = path8.join(opts.scanPath, "neat.patch");
4100
+ const patchPath = path9.join(opts.scanPath, "neat.patch");
4067
4101
  if (opts.dryRun) {
4068
4102
  await fs7.writeFile(patchPath, patch, "utf8");
4069
4103
  written.push(patchPath);
4070
4104
  console.log(`dry-run: patch written to ${patchPath}`);
4071
- const gitignorePath = path8.join(opts.scanPath, ".gitignore");
4105
+ const gitignorePath = path9.join(opts.scanPath, ".gitignore");
4072
4106
  const gitignoreExists = await fs7.stat(gitignorePath).then(() => true).catch(() => false);
4073
4107
  const verb = gitignoreExists ? "append" : "create";
4074
4108
  console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
@@ -4080,9 +4114,9 @@ async function runInit(opts) {
4080
4114
  const graph = getGraph(graphKey);
4081
4115
  const projectPaths = pathsForProject(
4082
4116
  graphKey,
4083
- path8.join(opts.scanPath, "neat-out")
4117
+ path9.join(opts.scanPath, "neat-out")
4084
4118
  );
4085
- const errorsPath = path8.join(path8.dirname(opts.outPath), path8.basename(projectPaths.errorsPath));
4119
+ const errorsPath = path9.join(path9.dirname(opts.outPath), path9.basename(projectPaths.errorsPath));
4086
4120
  const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
4087
4121
  await saveGraphToDisk(graph, opts.outPath);
4088
4122
  written.push(opts.outPath);
@@ -4201,9 +4235,9 @@ var CLAUDE_SKILL_CONFIG = {
4201
4235
  };
4202
4236
  function claudeConfigPath() {
4203
4237
  const override = process.env.NEAT_CLAUDE_CONFIG;
4204
- if (override && override.length > 0) return path8.resolve(override);
4238
+ if (override && override.length > 0) return path9.resolve(override);
4205
4239
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
4206
- return path8.join(home, ".claude.json");
4240
+ return path9.join(home, ".claude.json");
4207
4241
  }
4208
4242
  async function runSkill(opts) {
4209
4243
  const snippet = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
@@ -4227,7 +4261,7 @@ async function runSkill(opts) {
4227
4261
  ...existing,
4228
4262
  mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
4229
4263
  };
4230
- await fs7.mkdir(path8.dirname(target), { recursive: true });
4264
+ await fs7.mkdir(path9.dirname(target), { recursive: true });
4231
4265
  await fs7.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
4232
4266
  console.log(`neat skill: wrote mcpServers.neat to ${target}`);
4233
4267
  console.log("restart Claude Code to pick up the new MCP server.");
@@ -4243,16 +4277,24 @@ async function runSkill(opts) {
4243
4277
  return { exitCode: 0 };
4244
4278
  }
4245
4279
  async function main() {
4246
- const [, , cmd, ...rest] = process.argv;
4247
- if (!cmd || cmd === "-h" || cmd === "--help") {
4280
+ const argv = process.argv.slice(2);
4281
+ const cmd0 = argv[0];
4282
+ if (cmd0 === "-h" || cmd0 === "--help") {
4248
4283
  usage();
4249
4284
  process.exit(0);
4250
4285
  }
4251
- if (cmd === "--version" || cmd === "-v" || cmd === "version") {
4286
+ if (cmd0 === "--version" || cmd0 === "-v" || cmd0 === "version") {
4252
4287
  printVersion();
4253
4288
  process.exit(0);
4254
4289
  }
4255
- const parsed = parseArgs(rest);
4290
+ const argvParsed = parseArgs(argv);
4291
+ if (argvParsed.positional.length === 0) {
4292
+ const orchestratorCode2 = await tryOrchestrator(process.cwd(), argvParsed);
4293
+ if (orchestratorCode2 !== null && orchestratorCode2 !== 0) process.exit(orchestratorCode2);
4294
+ return;
4295
+ }
4296
+ const cmd = argvParsed.positional[0];
4297
+ const parsed = { ...argvParsed, positional: argvParsed.positional.slice(1) };
4256
4298
  const { positional, apply: apply3, dryRun, noInstall } = parsed;
4257
4299
  const project = parsed.project ?? DEFAULT_PROJECT;
4258
4300
  if (cmd === "init") {
@@ -4266,12 +4308,12 @@ async function main() {
4266
4308
  console.error("neat init: --apply and --dry-run are mutually exclusive");
4267
4309
  process.exit(2);
4268
4310
  }
4269
- const scanPath = path8.resolve(target);
4311
+ const scanPath = path9.resolve(target);
4270
4312
  const projectExplicit = parsed.project !== null;
4271
- const projectName = projectExplicit ? project : path8.basename(scanPath);
4313
+ const projectName = projectExplicit ? project : path9.basename(scanPath);
4272
4314
  const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
4273
- const fallback = pathsForProject(projectKey, path8.join(scanPath, "neat-out")).snapshotPath;
4274
- const outPath = path8.resolve(process.env.NEAT_OUT_PATH ?? fallback);
4315
+ const fallback = pathsForProject(projectKey, path9.join(scanPath, "neat-out")).snapshotPath;
4316
+ const outPath = path9.resolve(process.env.NEAT_OUT_PATH ?? fallback);
4275
4317
  const result = await runInit({
4276
4318
  scanPath,
4277
4319
  outPath,
@@ -4292,21 +4334,21 @@ async function main() {
4292
4334
  usage();
4293
4335
  process.exit(2);
4294
4336
  }
4295
- const scanPath = path8.resolve(target);
4337
+ const scanPath = path9.resolve(target);
4296
4338
  const stat = await fs7.stat(scanPath).catch(() => null);
4297
4339
  if (!stat || !stat.isDirectory()) {
4298
4340
  console.error(`neat watch: ${scanPath} is not a directory`);
4299
4341
  process.exit(2);
4300
4342
  }
4301
- const projectPaths = pathsForProject(project, path8.join(scanPath, "neat-out"));
4302
- const outPath = path8.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
4303
- const errorsPath = path8.resolve(
4304
- process.env.NEAT_ERRORS_PATH ?? path8.join(path8.dirname(outPath), path8.basename(projectPaths.errorsPath))
4343
+ const projectPaths = pathsForProject(project, path9.join(scanPath, "neat-out"));
4344
+ const outPath = path9.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
4345
+ const errorsPath = path9.resolve(
4346
+ process.env.NEAT_ERRORS_PATH ?? path9.join(path9.dirname(outPath), path9.basename(projectPaths.errorsPath))
4305
4347
  );
4306
- const staleEventsPath = path8.resolve(
4307
- process.env.NEAT_STALE_EVENTS_PATH ?? path8.join(path8.dirname(outPath), path8.basename(projectPaths.staleEventsPath))
4348
+ const staleEventsPath = path9.resolve(
4349
+ process.env.NEAT_STALE_EVENTS_PATH ?? path9.join(path9.dirname(outPath), path9.basename(projectPaths.staleEventsPath))
4308
4350
  );
4309
- const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? path8.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
4351
+ const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? path9.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
4310
4352
  const handle = await startWatch(getGraph(project), {
4311
4353
  scanPath,
4312
4354
  outPath,
@@ -4452,11 +4494,11 @@ async function main() {
4452
4494
  process.exit(1);
4453
4495
  }
4454
4496
  async function tryOrchestrator(cmd, parsed) {
4455
- const scanPath = path8.resolve(cmd);
4497
+ const scanPath = path9.resolve(cmd);
4456
4498
  const stat = await fs7.stat(scanPath).catch(() => null);
4457
4499
  if (!stat || !stat.isDirectory()) return null;
4458
4500
  const projectExplicit = parsed.project !== null;
4459
- const projectName = projectExplicit ? parsed.project : path8.basename(scanPath);
4501
+ const projectName = projectExplicit ? parsed.project : path9.basename(scanPath);
4460
4502
  const result = await runOrchestrator({
4461
4503
  scanPath,
4462
4504
  project: projectName,
@@ -4656,11 +4698,15 @@ if (/[\\/]cli\.(?:cjs|js)$/.test(entry) || entry.endsWith("/cli") || entry.endsW
4656
4698
  export {
4657
4699
  CLAUDE_SKILL_CONFIG,
4658
4700
  QUERY_VERBS,
4701
+ commandPrefix,
4702
+ isNpxInvocation,
4703
+ main,
4659
4704
  parseArgs,
4660
4705
  printBanner,
4661
4706
  readPackageVersion,
4662
4707
  runInit,
4663
4708
  runQueryVerb,
4664
- runSkill
4709
+ runSkill,
4710
+ usage
4665
4711
  };
4666
4712
  //# sourceMappingURL=cli.js.map