@neat.is/core 0.9.17-dev.20260915 → 0.9.17
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 +151 -20
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +3 -2
- package/dist/cli.d.ts +3 -2
- package/dist/cli.js +150 -20
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.cjs
CHANGED
|
@@ -844,6 +844,17 @@ function printBanner() {
|
|
|
844
844
|
console.log(` neat.is \xB7 v${readPackageVersion()} \xB7 Apache 2.0`);
|
|
845
845
|
console.log("");
|
|
846
846
|
}
|
|
847
|
+
function isNpxInvocation() {
|
|
848
|
+
if (process.env.npm_command === "exec") return true;
|
|
849
|
+
const execpath = process.env.npm_execpath ?? "";
|
|
850
|
+
if (execpath.includes("npx")) return true;
|
|
851
|
+
const entry2 = process.argv[1] ?? "";
|
|
852
|
+
if (entry2.includes("/_npx/") || entry2.includes("\\_npx\\")) return true;
|
|
853
|
+
return false;
|
|
854
|
+
}
|
|
855
|
+
function commandPrefix() {
|
|
856
|
+
return isNpxInvocation() ? "npx neat.is" : "neat";
|
|
857
|
+
}
|
|
847
858
|
|
|
848
859
|
// src/graph.ts
|
|
849
860
|
init_cjs_shims();
|
|
@@ -28848,17 +28859,17 @@ async function runOrchestrator(opts) {
|
|
|
28848
28859
|
}
|
|
28849
28860
|
const webPort = allocated?.web ?? NEAT_PORTS[2];
|
|
28850
28861
|
const dashboardUrl = opts.dashboardUrl ?? `http://localhost:${webPort}`;
|
|
28851
|
-
if (opts.noOpen
|
|
28852
|
-
result.steps.browser = "skipped";
|
|
28853
|
-
} else {
|
|
28862
|
+
if (opts.open === true && !opts.noOpen && process.stdout.isTTY) {
|
|
28854
28863
|
result.steps.browser = openBrowser(dashboardUrl);
|
|
28864
|
+
} else {
|
|
28865
|
+
result.steps.browser = "skipped";
|
|
28855
28866
|
}
|
|
28856
28867
|
const daemonRunning = result.steps.daemon === "spawned" || result.steps.daemon === "already-running";
|
|
28857
28868
|
const daemonLog = daemonRunning ? import_node_path88.default.relative(opts.scanPath, daemonLogPath(opts.scanPath)) : null;
|
|
28858
|
-
printSummary(result, graph,
|
|
28869
|
+
printSummary(result, graph, daemonLog);
|
|
28859
28870
|
return result;
|
|
28860
28871
|
}
|
|
28861
|
-
function printSummary(result, graph,
|
|
28872
|
+
function printSummary(result, graph, daemonLog) {
|
|
28862
28873
|
const nodes = [];
|
|
28863
28874
|
graph.forEachNode((_id, attrs) => nodes.push(attrs));
|
|
28864
28875
|
const edges = [];
|
|
@@ -28873,12 +28884,21 @@ function printSummary(result, graph, dashboardUrl, daemonLog) {
|
|
|
28873
28884
|
for (const [t, c] of [...byNode.entries()].sort()) console.log(` ${t}: ${c}`);
|
|
28874
28885
|
for (const [t, c] of [...byEdge.entries()].sort()) console.log(` ${t}: ${c}`);
|
|
28875
28886
|
console.log("");
|
|
28876
|
-
|
|
28887
|
+
const neat = commandPrefix();
|
|
28888
|
+
console.log("query the graph from your coding agent \u2014 NEAT runs as an MCP server, so");
|
|
28889
|
+
console.log("Claude Code, Cursor, and the rest read it directly:");
|
|
28890
|
+
console.log(` ${neat} skill --apply put NEAT in front of Claude Code`);
|
|
28891
|
+
console.log(` (\`${neat} --help\` lists Cursor, Codex, Gemini, and the other clients)`);
|
|
28892
|
+
console.log("or ask it straight from this terminal:");
|
|
28893
|
+
console.log(` ${neat} ask "how does checkout work?"`);
|
|
28894
|
+
console.log("");
|
|
28895
|
+
console.log(`want a visual dashboard? log in to hosted NEAT \u2014 \`${neat} login\``);
|
|
28877
28896
|
const token = process.env.NEAT_AUTH_TOKEN;
|
|
28897
|
+
console.log("");
|
|
28878
28898
|
if (typeof token === "string" && token.length > 0) {
|
|
28879
|
-
console.log(`auth token: ${token}`);
|
|
28899
|
+
console.log(`auth token (needed to reach the daemon and route OTel): ${token}`);
|
|
28880
28900
|
} else {
|
|
28881
|
-
console.log("running locally \u2014
|
|
28901
|
+
console.log("running locally \u2014 no auth token needed");
|
|
28882
28902
|
}
|
|
28883
28903
|
const failedInstalls = (result.steps.apply.packageManagerInstalls ?? []).filter(
|
|
28884
28904
|
(i) => i.exitCode !== 0
|
|
@@ -30254,7 +30274,7 @@ init_cjs_shims();
|
|
|
30254
30274
|
var import_node_http2 = require("http");
|
|
30255
30275
|
var import_node_child_process4 = require("child_process");
|
|
30256
30276
|
var import_node_crypto6 = require("crypto");
|
|
30257
|
-
var DEFAULT_CP_URL = "https://
|
|
30277
|
+
var DEFAULT_CP_URL = "https://neat-control-plane-bg5yqctn2q-nw.a.run.app";
|
|
30258
30278
|
var DEFAULT_WEB_URL = "https://app.neat.is";
|
|
30259
30279
|
var CALLBACK_TIMEOUT_MS = 5 * 6e4;
|
|
30260
30280
|
function resolveCpUrl(env = process.env, override) {
|
|
@@ -30742,6 +30762,99 @@ async function defaultReadSecret(prompt) {
|
|
|
30742
30762
|
});
|
|
30743
30763
|
}
|
|
30744
30764
|
|
|
30765
|
+
// src/welcome.ts
|
|
30766
|
+
init_cjs_shims();
|
|
30767
|
+
var import_promises3 = __toESM(require("readline/promises"), 1);
|
|
30768
|
+
var WORDMARK = [
|
|
30769
|
+
"\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",
|
|
30770
|
+
"\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",
|
|
30771
|
+
"\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 ",
|
|
30772
|
+
"\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 ",
|
|
30773
|
+
"\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 ",
|
|
30774
|
+
"\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 "
|
|
30775
|
+
];
|
|
30776
|
+
var AGENT_SETUP_PROMPT = `Set up self-hosted NEAT for this project. NEAT keeps a live graph of the code
|
|
30777
|
+
fused with runtime OpenTelemetry data, queryable over MCP \u2014 so you can ask it
|
|
30778
|
+
about the system instead of grepping files.
|
|
30779
|
+
|
|
30780
|
+
1. Extract the code graph and instrument the services for OpenTelemetry:
|
|
30781
|
+
npx neat.is init . --apply
|
|
30782
|
+
Then install the deps it added (npm install, or your language's equivalent).
|
|
30783
|
+
2. Start the NEAT daemon so it watches the code and ingests live traces:
|
|
30784
|
+
npx neat.is watch .
|
|
30785
|
+
3. Wire NEAT's MCP server into your agent so it queries the graph first:
|
|
30786
|
+
npx neat.is skill --apply
|
|
30787
|
+
(other agents: npx neat.is codex|cursor|gemini \u2026 --apply)
|
|
30788
|
+
|
|
30789
|
+
Then ask NEAT instead of reading files, e.g.
|
|
30790
|
+
npx neat.is ask "why is checkout failing?"`;
|
|
30791
|
+
function printHeader(out) {
|
|
30792
|
+
for (const line of WORDMARK) out(line);
|
|
30793
|
+
out("");
|
|
30794
|
+
out(` neat.is \xB7 v${readPackageVersion()}`);
|
|
30795
|
+
out("");
|
|
30796
|
+
}
|
|
30797
|
+
async function runWelcome(deps = {}) {
|
|
30798
|
+
const out = deps.out ?? ((line) => console.log(line));
|
|
30799
|
+
const readLine = deps.readLine ?? defaultReadLine2;
|
|
30800
|
+
const login = deps.login ?? (() => Promise.resolve(0));
|
|
30801
|
+
const orchestrator = deps.orchestrator ?? (() => Promise.resolve(0));
|
|
30802
|
+
const cwd = deps.cwd ?? process.cwd();
|
|
30803
|
+
printHeader(out);
|
|
30804
|
+
out("Welcome to NEAT. Let's get you a graph of this system.");
|
|
30805
|
+
out("");
|
|
30806
|
+
out(" 1) I have a NEAT account \u2014 log in");
|
|
30807
|
+
out(" 2) Self-hosted \u2014 set up NEAT on this machine");
|
|
30808
|
+
out("");
|
|
30809
|
+
for (; ; ) {
|
|
30810
|
+
const choice = (await readLine("Choose 1 or 2 (default 2): "))?.trim();
|
|
30811
|
+
if (choice === void 0) return runSelfHosted(out, readLine, orchestrator, cwd);
|
|
30812
|
+
if (choice === "1") {
|
|
30813
|
+
return login(["--browser"]);
|
|
30814
|
+
}
|
|
30815
|
+
if (choice === "2" || choice === "") {
|
|
30816
|
+
return runSelfHosted(out, readLine, orchestrator, cwd);
|
|
30817
|
+
}
|
|
30818
|
+
out(`"${choice}" isn't 1 or 2 \u2014 try again, or press Enter for self-hosted.`);
|
|
30819
|
+
}
|
|
30820
|
+
}
|
|
30821
|
+
async function runSelfHosted(out, readLine, orchestrator, cwd) {
|
|
30822
|
+
const wantsPrompt = (await readLine("Print a copy-paste setup prompt for your coding agent? [Y/n]: "))?.trim().toLowerCase();
|
|
30823
|
+
if (wantsPrompt !== "n" && wantsPrompt !== "no") {
|
|
30824
|
+
out("");
|
|
30825
|
+
out("\u2500\u2500\u2500 copy the prompt below into your coding agent \u2500\u2500\u2500");
|
|
30826
|
+
out("");
|
|
30827
|
+
out(AGENT_SETUP_PROMPT);
|
|
30828
|
+
out("");
|
|
30829
|
+
out("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
30830
|
+
out("");
|
|
30831
|
+
}
|
|
30832
|
+
out("Building your local graph now\u2026");
|
|
30833
|
+
out("");
|
|
30834
|
+
return orchestrator(cwd);
|
|
30835
|
+
}
|
|
30836
|
+
async function shouldShowWelcome(deps = {}) {
|
|
30837
|
+
const stdinTTY = deps.stdinIsTTY ?? Boolean(process.stdin.isTTY);
|
|
30838
|
+
const stdoutTTY = deps.stdoutIsTTY ?? Boolean(process.stdout.isTTY);
|
|
30839
|
+
if (!stdinTTY || !stdoutTTY) return false;
|
|
30840
|
+
const readProfiles = deps.readProfiles ?? (() => readProfilesConfig());
|
|
30841
|
+
try {
|
|
30842
|
+
const { profiles } = await readProfiles();
|
|
30843
|
+
return profiles.length === 0;
|
|
30844
|
+
} catch {
|
|
30845
|
+
return false;
|
|
30846
|
+
}
|
|
30847
|
+
}
|
|
30848
|
+
async function defaultReadLine2(prompt) {
|
|
30849
|
+
if (!process.stdin.isTTY) return void 0;
|
|
30850
|
+
const rl = import_promises3.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
30851
|
+
try {
|
|
30852
|
+
return await rl.question(prompt);
|
|
30853
|
+
} finally {
|
|
30854
|
+
rl.close();
|
|
30855
|
+
}
|
|
30856
|
+
}
|
|
30857
|
+
|
|
30745
30858
|
// src/hooks-cli.ts
|
|
30746
30859
|
init_cjs_shims();
|
|
30747
30860
|
var import_node_path91 = __toESM(require("path"), 1);
|
|
@@ -32288,17 +32401,6 @@ async function runSync(opts) {
|
|
|
32288
32401
|
|
|
32289
32402
|
// src/cli.ts
|
|
32290
32403
|
var import_types110 = require("@neat.is/types");
|
|
32291
|
-
function isNpxInvocation() {
|
|
32292
|
-
if (process.env.npm_command === "exec") return true;
|
|
32293
|
-
const execpath = process.env.npm_execpath ?? "";
|
|
32294
|
-
if (execpath.includes("npx")) return true;
|
|
32295
|
-
const entry2 = process.argv[1] ?? "";
|
|
32296
|
-
if (entry2.includes("/_npx/") || entry2.includes("\\_npx\\")) return true;
|
|
32297
|
-
return false;
|
|
32298
|
-
}
|
|
32299
|
-
function commandPrefix() {
|
|
32300
|
-
return isNpxInvocation() ? "npx neat.is" : "neat";
|
|
32301
|
-
}
|
|
32302
32404
|
function usage5() {
|
|
32303
32405
|
const neat = commandPrefix();
|
|
32304
32406
|
console.log("Installed via npx? Prefix commands with `npx neat.is`, or install once: `npm i -g neat.is`.");
|
|
@@ -32306,8 +32408,15 @@ function usage5() {
|
|
|
32306
32408
|
console.log(`usage: ${neat} <command> [args] [--project <name>]`);
|
|
32307
32409
|
console.log("");
|
|
32308
32410
|
console.log(`Run \`${neat}\` with no command from inside your project to go zero-to-graph in one step.`);
|
|
32411
|
+
console.log(`On your first run in a terminal, \`${neat}\` opens a short menu \u2014 log in to a hosted`);
|
|
32412
|
+
console.log(`NEAT, or set up self-hosted here. Run \`${neat} welcome\` to open it again anytime.`);
|
|
32413
|
+
console.log(`The run stays in your terminal \u2014 query the graph over MCP or the CLI. For a visual`);
|
|
32414
|
+
console.log(`dashboard, log in to hosted NEAT; \`--open\` launches the local preview one by choice.`);
|
|
32309
32415
|
console.log("");
|
|
32310
32416
|
console.log("lifecycle commands:");
|
|
32417
|
+
console.log(" welcome Open the first-run menu: log in to a hosted NEAT, or set up");
|
|
32418
|
+
console.log(" self-hosted (print an agent-setup prompt, then build the graph");
|
|
32419
|
+
console.log(" for the current directory).");
|
|
32311
32420
|
console.log(" init <path> One-time install: discover, extract, register, plan SDK install.");
|
|
32312
32421
|
console.log(" Snapshot lands in <path>/neat-out/graph.json by default");
|
|
32313
32422
|
console.log(" (or <path>/neat-out/<project>.json for non-default).");
|
|
@@ -32518,6 +32627,7 @@ function parseArgs(rest) {
|
|
|
32518
32627
|
dryRun: false,
|
|
32519
32628
|
noInstall: false,
|
|
32520
32629
|
noInstrument: false,
|
|
32630
|
+
open: false,
|
|
32521
32631
|
noOpen: false,
|
|
32522
32632
|
yes: false,
|
|
32523
32633
|
verbose: false,
|
|
@@ -32555,6 +32665,10 @@ function parseArgs(rest) {
|
|
|
32555
32665
|
out.noInstrument = true;
|
|
32556
32666
|
continue;
|
|
32557
32667
|
}
|
|
32668
|
+
if (arg === "--open") {
|
|
32669
|
+
out.open = true;
|
|
32670
|
+
continue;
|
|
32671
|
+
}
|
|
32558
32672
|
if (arg === "--no-open") {
|
|
32559
32673
|
out.noOpen = true;
|
|
32560
32674
|
continue;
|
|
@@ -32934,6 +33048,11 @@ async function main() {
|
|
|
32934
33048
|
}
|
|
32935
33049
|
const argvParsed = parseArgs(argv);
|
|
32936
33050
|
if (argvParsed.positional.length === 0) {
|
|
33051
|
+
if (await shouldShowWelcome()) {
|
|
33052
|
+
const welcomeCode = await runWelcomeFlow(argvParsed);
|
|
33053
|
+
if (welcomeCode !== 0) process.exit(welcomeCode);
|
|
33054
|
+
return;
|
|
33055
|
+
}
|
|
32937
33056
|
const orchestratorCode2 = await tryOrchestrator(process.cwd(), argvParsed);
|
|
32938
33057
|
if (orchestratorCode2 !== null && orchestratorCode2 !== 0) process.exit(orchestratorCode2);
|
|
32939
33058
|
return;
|
|
@@ -32942,6 +33061,11 @@ async function main() {
|
|
|
32942
33061
|
const parsed = { ...argvParsed, positional: argvParsed.positional.slice(1) };
|
|
32943
33062
|
const { positional, apply: apply6, dryRun, noInstall } = parsed;
|
|
32944
33063
|
const project = parsed.project ?? DEFAULT_PROJECT;
|
|
33064
|
+
if (cmd === "welcome") {
|
|
33065
|
+
const code = await runWelcomeFlow(parsed);
|
|
33066
|
+
if (code !== 0) process.exit(code);
|
|
33067
|
+
return;
|
|
33068
|
+
}
|
|
32945
33069
|
if (cmd === "init") {
|
|
32946
33070
|
const target = positional[0];
|
|
32947
33071
|
if (!target) {
|
|
@@ -33194,11 +33318,18 @@ async function tryOrchestrator(cmd, parsed) {
|
|
|
33194
33318
|
project: projectName,
|
|
33195
33319
|
projectExplicit,
|
|
33196
33320
|
noInstrument: parsed.noInstrument,
|
|
33321
|
+
open: parsed.open,
|
|
33197
33322
|
noOpen: parsed.noOpen,
|
|
33198
33323
|
yes: parsed.yes
|
|
33199
33324
|
});
|
|
33200
33325
|
return result.exitCode;
|
|
33201
33326
|
}
|
|
33327
|
+
async function runWelcomeFlow(parsed) {
|
|
33328
|
+
return runWelcome({
|
|
33329
|
+
login: (loginArgv) => runLoginCommand(loginArgv),
|
|
33330
|
+
orchestrator: async (cwd) => await tryOrchestrator(cwd, parsed) ?? 0
|
|
33331
|
+
});
|
|
33332
|
+
}
|
|
33202
33333
|
var QUERY_VERBS = /* @__PURE__ */ new Set([
|
|
33203
33334
|
"root-cause",
|
|
33204
33335
|
"blast-radius",
|