@neat.is/core 0.9.17-dev.20260916 → 0.9.18-dev.20260917
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.d.cts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
declare function readPackageVersion(): string;
|
|
3
3
|
declare function printBanner(): void;
|
|
4
|
+
declare function isNpxInvocation(): boolean;
|
|
5
|
+
declare function commandPrefix(): string;
|
|
4
6
|
|
|
5
7
|
interface HttpClient {
|
|
6
8
|
get<T>(path: string): Promise<T>;
|
|
@@ -21,8 +23,6 @@ interface InitResult {
|
|
|
21
23
|
exitCode: number;
|
|
22
24
|
writtenFiles: string[];
|
|
23
25
|
}
|
|
24
|
-
declare function isNpxInvocation(): boolean;
|
|
25
|
-
declare function commandPrefix(): string;
|
|
26
26
|
declare function usage(): void;
|
|
27
27
|
interface ParsedArgs {
|
|
28
28
|
project: string | null;
|
|
@@ -31,6 +31,7 @@ interface ParsedArgs {
|
|
|
31
31
|
dryRun: boolean;
|
|
32
32
|
noInstall: boolean;
|
|
33
33
|
noInstrument: boolean;
|
|
34
|
+
open: boolean;
|
|
34
35
|
noOpen: boolean;
|
|
35
36
|
yes: boolean;
|
|
36
37
|
verbose: boolean;
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
declare function readPackageVersion(): string;
|
|
3
3
|
declare function printBanner(): void;
|
|
4
|
+
declare function isNpxInvocation(): boolean;
|
|
5
|
+
declare function commandPrefix(): string;
|
|
4
6
|
|
|
5
7
|
interface HttpClient {
|
|
6
8
|
get<T>(path: string): Promise<T>;
|
|
@@ -21,8 +23,6 @@ interface InitResult {
|
|
|
21
23
|
exitCode: number;
|
|
22
24
|
writtenFiles: string[];
|
|
23
25
|
}
|
|
24
|
-
declare function isNpxInvocation(): boolean;
|
|
25
|
-
declare function commandPrefix(): string;
|
|
26
26
|
declare function usage(): void;
|
|
27
27
|
interface ParsedArgs {
|
|
28
28
|
project: string | null;
|
|
@@ -31,6 +31,7 @@ interface ParsedArgs {
|
|
|
31
31
|
dryRun: boolean;
|
|
32
32
|
noInstall: boolean;
|
|
33
33
|
noInstrument: boolean;
|
|
34
|
+
open: boolean;
|
|
34
35
|
noOpen: boolean;
|
|
35
36
|
yes: boolean;
|
|
36
37
|
verbose: boolean;
|
package/dist/cli.js
CHANGED
|
@@ -129,6 +129,17 @@ function printBanner() {
|
|
|
129
129
|
console.log(` neat.is \xB7 v${readPackageVersion()} \xB7 Apache 2.0`);
|
|
130
130
|
console.log("");
|
|
131
131
|
}
|
|
132
|
+
function isNpxInvocation() {
|
|
133
|
+
if (process.env.npm_command === "exec") return true;
|
|
134
|
+
const execpath = process.env.npm_execpath ?? "";
|
|
135
|
+
if (execpath.includes("npx")) return true;
|
|
136
|
+
const entry2 = process.argv[1] ?? "";
|
|
137
|
+
if (entry2.includes("/_npx/") || entry2.includes("\\_npx\\")) return true;
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
function commandPrefix() {
|
|
141
|
+
return isNpxInvocation() ? "npx neat.is" : "neat";
|
|
142
|
+
}
|
|
132
143
|
|
|
133
144
|
// src/gitignore.ts
|
|
134
145
|
import { promises as fs } from "fs";
|
|
@@ -4379,17 +4390,17 @@ async function runOrchestrator(opts) {
|
|
|
4379
4390
|
}
|
|
4380
4391
|
const webPort = allocated?.web ?? NEAT_PORTS[2];
|
|
4381
4392
|
const dashboardUrl = opts.dashboardUrl ?? `http://localhost:${webPort}`;
|
|
4382
|
-
if (opts.noOpen
|
|
4383
|
-
result.steps.browser = "skipped";
|
|
4384
|
-
} else {
|
|
4393
|
+
if (opts.open === true && !opts.noOpen && process.stdout.isTTY) {
|
|
4385
4394
|
result.steps.browser = openBrowser(dashboardUrl);
|
|
4395
|
+
} else {
|
|
4396
|
+
result.steps.browser = "skipped";
|
|
4386
4397
|
}
|
|
4387
4398
|
const daemonRunning = result.steps.daemon === "spawned" || result.steps.daemon === "already-running";
|
|
4388
4399
|
const daemonLog = daemonRunning ? path10.relative(opts.scanPath, daemonLogPath(opts.scanPath)) : null;
|
|
4389
|
-
printSummary(result, graph,
|
|
4400
|
+
printSummary(result, graph, daemonLog);
|
|
4390
4401
|
return result;
|
|
4391
4402
|
}
|
|
4392
|
-
function printSummary(result, graph,
|
|
4403
|
+
function printSummary(result, graph, daemonLog) {
|
|
4393
4404
|
const nodes = [];
|
|
4394
4405
|
graph.forEachNode((_id, attrs) => nodes.push(attrs));
|
|
4395
4406
|
const edges = [];
|
|
@@ -4404,12 +4415,21 @@ function printSummary(result, graph, dashboardUrl, daemonLog) {
|
|
|
4404
4415
|
for (const [t, c] of [...byNode.entries()].sort()) console.log(` ${t}: ${c}`);
|
|
4405
4416
|
for (const [t, c] of [...byEdge.entries()].sort()) console.log(` ${t}: ${c}`);
|
|
4406
4417
|
console.log("");
|
|
4407
|
-
|
|
4418
|
+
const neat = commandPrefix();
|
|
4419
|
+
console.log("query the graph from your coding agent \u2014 NEAT runs as an MCP server, so");
|
|
4420
|
+
console.log("Claude Code, Cursor, and the rest read it directly:");
|
|
4421
|
+
console.log(` ${neat} skill --apply put NEAT in front of Claude Code`);
|
|
4422
|
+
console.log(` (\`${neat} --help\` lists Cursor, Codex, Gemini, and the other clients)`);
|
|
4423
|
+
console.log("or ask it straight from this terminal:");
|
|
4424
|
+
console.log(` ${neat} ask "how does checkout work?"`);
|
|
4425
|
+
console.log("");
|
|
4426
|
+
console.log(`want a visual dashboard? log in to hosted NEAT \u2014 \`${neat} login\``);
|
|
4408
4427
|
const token = process.env.NEAT_AUTH_TOKEN;
|
|
4428
|
+
console.log("");
|
|
4409
4429
|
if (typeof token === "string" && token.length > 0) {
|
|
4410
|
-
console.log(`auth token: ${token}`);
|
|
4430
|
+
console.log(`auth token (needed to reach the daemon and route OTel): ${token}`);
|
|
4411
4431
|
} else {
|
|
4412
|
-
console.log("running locally \u2014
|
|
4432
|
+
console.log("running locally \u2014 no auth token needed");
|
|
4413
4433
|
}
|
|
4414
4434
|
const failedInstalls = (result.steps.apply.packageManagerInstalls ?? []).filter(
|
|
4415
4435
|
(i) => i.exitCode !== 0
|
|
@@ -5779,7 +5799,7 @@ async function clearActiveProfile(home = neatHome()) {
|
|
|
5779
5799
|
import { createServer } from "http";
|
|
5780
5800
|
import { spawn as spawn3 } from "child_process";
|
|
5781
5801
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
5782
|
-
var DEFAULT_CP_URL = "https://
|
|
5802
|
+
var DEFAULT_CP_URL = "https://neat-control-plane-bg5yqctn2q-nw.a.run.app";
|
|
5783
5803
|
var DEFAULT_WEB_URL = "https://app.neat.is";
|
|
5784
5804
|
var CALLBACK_TIMEOUT_MS = 5 * 6e4;
|
|
5785
5805
|
function resolveCpUrl(env = process.env, override) {
|
|
@@ -6267,6 +6287,98 @@ async function defaultReadSecret(prompt) {
|
|
|
6267
6287
|
});
|
|
6268
6288
|
}
|
|
6269
6289
|
|
|
6290
|
+
// src/welcome.ts
|
|
6291
|
+
import readline4 from "readline/promises";
|
|
6292
|
+
var WORDMARK = [
|
|
6293
|
+
"\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",
|
|
6294
|
+
"\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",
|
|
6295
|
+
"\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 ",
|
|
6296
|
+
"\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 ",
|
|
6297
|
+
"\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 ",
|
|
6298
|
+
"\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 "
|
|
6299
|
+
];
|
|
6300
|
+
var AGENT_SETUP_PROMPT = `Set up self-hosted NEAT for this project. NEAT keeps a live graph of the code
|
|
6301
|
+
fused with runtime OpenTelemetry data, queryable over MCP \u2014 so you can ask it
|
|
6302
|
+
about the system instead of grepping files.
|
|
6303
|
+
|
|
6304
|
+
1. Extract the code graph and instrument the services for OpenTelemetry:
|
|
6305
|
+
npx neat.is init . --apply
|
|
6306
|
+
Then install the deps it added (npm install, or your language's equivalent).
|
|
6307
|
+
2. Start the NEAT daemon so it watches the code and ingests live traces:
|
|
6308
|
+
npx neat.is watch .
|
|
6309
|
+
3. Wire NEAT's MCP server into your agent so it queries the graph first:
|
|
6310
|
+
npx neat.is skill --apply
|
|
6311
|
+
(other agents: npx neat.is codex|cursor|gemini \u2026 --apply)
|
|
6312
|
+
|
|
6313
|
+
Then ask NEAT instead of reading files, e.g.
|
|
6314
|
+
npx neat.is ask "why is checkout failing?"`;
|
|
6315
|
+
function printHeader(out) {
|
|
6316
|
+
for (const line of WORDMARK) out(line);
|
|
6317
|
+
out("");
|
|
6318
|
+
out(` neat.is \xB7 v${readPackageVersion()}`);
|
|
6319
|
+
out("");
|
|
6320
|
+
}
|
|
6321
|
+
async function runWelcome(deps = {}) {
|
|
6322
|
+
const out = deps.out ?? ((line) => console.log(line));
|
|
6323
|
+
const readLine = deps.readLine ?? defaultReadLine2;
|
|
6324
|
+
const login = deps.login ?? (() => Promise.resolve(0));
|
|
6325
|
+
const orchestrator = deps.orchestrator ?? (() => Promise.resolve(0));
|
|
6326
|
+
const cwd = deps.cwd ?? process.cwd();
|
|
6327
|
+
printHeader(out);
|
|
6328
|
+
out("Welcome to NEAT. Let's get you a graph of this system.");
|
|
6329
|
+
out("");
|
|
6330
|
+
out(" 1) I have a NEAT account \u2014 log in");
|
|
6331
|
+
out(" 2) Self-hosted \u2014 set up NEAT on this machine");
|
|
6332
|
+
out("");
|
|
6333
|
+
for (; ; ) {
|
|
6334
|
+
const choice = (await readLine("Choose 1 or 2 (default 2): "))?.trim();
|
|
6335
|
+
if (choice === void 0) return runSelfHosted(out, readLine, orchestrator, cwd);
|
|
6336
|
+
if (choice === "1") {
|
|
6337
|
+
return login(["--browser"]);
|
|
6338
|
+
}
|
|
6339
|
+
if (choice === "2" || choice === "") {
|
|
6340
|
+
return runSelfHosted(out, readLine, orchestrator, cwd);
|
|
6341
|
+
}
|
|
6342
|
+
out(`"${choice}" isn't 1 or 2 \u2014 try again, or press Enter for self-hosted.`);
|
|
6343
|
+
}
|
|
6344
|
+
}
|
|
6345
|
+
async function runSelfHosted(out, readLine, orchestrator, cwd) {
|
|
6346
|
+
const wantsPrompt = (await readLine("Print a copy-paste setup prompt for your coding agent? [Y/n]: "))?.trim().toLowerCase();
|
|
6347
|
+
if (wantsPrompt !== "n" && wantsPrompt !== "no") {
|
|
6348
|
+
out("");
|
|
6349
|
+
out("\u2500\u2500\u2500 copy the prompt below into your coding agent \u2500\u2500\u2500");
|
|
6350
|
+
out("");
|
|
6351
|
+
out(AGENT_SETUP_PROMPT);
|
|
6352
|
+
out("");
|
|
6353
|
+
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");
|
|
6354
|
+
out("");
|
|
6355
|
+
}
|
|
6356
|
+
out("Building your local graph now\u2026");
|
|
6357
|
+
out("");
|
|
6358
|
+
return orchestrator(cwd);
|
|
6359
|
+
}
|
|
6360
|
+
async function shouldShowWelcome(deps = {}) {
|
|
6361
|
+
const stdinTTY = deps.stdinIsTTY ?? Boolean(process.stdin.isTTY);
|
|
6362
|
+
const stdoutTTY = deps.stdoutIsTTY ?? Boolean(process.stdout.isTTY);
|
|
6363
|
+
if (!stdinTTY || !stdoutTTY) return false;
|
|
6364
|
+
const readProfiles = deps.readProfiles ?? (() => readProfilesConfig());
|
|
6365
|
+
try {
|
|
6366
|
+
const { profiles } = await readProfiles();
|
|
6367
|
+
return profiles.length === 0;
|
|
6368
|
+
} catch {
|
|
6369
|
+
return false;
|
|
6370
|
+
}
|
|
6371
|
+
}
|
|
6372
|
+
async function defaultReadLine2(prompt) {
|
|
6373
|
+
if (!process.stdin.isTTY) return void 0;
|
|
6374
|
+
const rl = readline4.createInterface({ input: process.stdin, output: process.stdout });
|
|
6375
|
+
try {
|
|
6376
|
+
return await rl.question(prompt);
|
|
6377
|
+
} finally {
|
|
6378
|
+
rl.close();
|
|
6379
|
+
}
|
|
6380
|
+
}
|
|
6381
|
+
|
|
6270
6382
|
// src/hooks-cli.ts
|
|
6271
6383
|
import path13 from "path";
|
|
6272
6384
|
import os2 from "os";
|
|
@@ -7807,17 +7919,6 @@ async function runSync(opts) {
|
|
|
7807
7919
|
|
|
7808
7920
|
// src/cli.ts
|
|
7809
7921
|
import { DivergenceTypeSchema } from "@neat.is/types";
|
|
7810
|
-
function isNpxInvocation() {
|
|
7811
|
-
if (process.env.npm_command === "exec") return true;
|
|
7812
|
-
const execpath = process.env.npm_execpath ?? "";
|
|
7813
|
-
if (execpath.includes("npx")) return true;
|
|
7814
|
-
const entry2 = process.argv[1] ?? "";
|
|
7815
|
-
if (entry2.includes("/_npx/") || entry2.includes("\\_npx\\")) return true;
|
|
7816
|
-
return false;
|
|
7817
|
-
}
|
|
7818
|
-
function commandPrefix() {
|
|
7819
|
-
return isNpxInvocation() ? "npx neat.is" : "neat";
|
|
7820
|
-
}
|
|
7821
7922
|
function usage5() {
|
|
7822
7923
|
const neat = commandPrefix();
|
|
7823
7924
|
console.log("Installed via npx? Prefix commands with `npx neat.is`, or install once: `npm i -g neat.is`.");
|
|
@@ -7825,8 +7926,15 @@ function usage5() {
|
|
|
7825
7926
|
console.log(`usage: ${neat} <command> [args] [--project <name>]`);
|
|
7826
7927
|
console.log("");
|
|
7827
7928
|
console.log(`Run \`${neat}\` with no command from inside your project to go zero-to-graph in one step.`);
|
|
7929
|
+
console.log(`On your first run in a terminal, \`${neat}\` opens a short menu \u2014 log in to a hosted`);
|
|
7930
|
+
console.log(`NEAT, or set up self-hosted here. Run \`${neat} welcome\` to open it again anytime.`);
|
|
7931
|
+
console.log(`The run stays in your terminal \u2014 query the graph over MCP or the CLI. For a visual`);
|
|
7932
|
+
console.log(`dashboard, log in to hosted NEAT; \`--open\` launches the local preview one by choice.`);
|
|
7828
7933
|
console.log("");
|
|
7829
7934
|
console.log("lifecycle commands:");
|
|
7935
|
+
console.log(" welcome Open the first-run menu: log in to a hosted NEAT, or set up");
|
|
7936
|
+
console.log(" self-hosted (print an agent-setup prompt, then build the graph");
|
|
7937
|
+
console.log(" for the current directory).");
|
|
7830
7938
|
console.log(" init <path> One-time install: discover, extract, register, plan SDK install.");
|
|
7831
7939
|
console.log(" Snapshot lands in <path>/neat-out/graph.json by default");
|
|
7832
7940
|
console.log(" (or <path>/neat-out/<project>.json for non-default).");
|
|
@@ -8037,6 +8145,7 @@ function parseArgs(rest) {
|
|
|
8037
8145
|
dryRun: false,
|
|
8038
8146
|
noInstall: false,
|
|
8039
8147
|
noInstrument: false,
|
|
8148
|
+
open: false,
|
|
8040
8149
|
noOpen: false,
|
|
8041
8150
|
yes: false,
|
|
8042
8151
|
verbose: false,
|
|
@@ -8074,6 +8183,10 @@ function parseArgs(rest) {
|
|
|
8074
8183
|
out.noInstrument = true;
|
|
8075
8184
|
continue;
|
|
8076
8185
|
}
|
|
8186
|
+
if (arg === "--open") {
|
|
8187
|
+
out.open = true;
|
|
8188
|
+
continue;
|
|
8189
|
+
}
|
|
8077
8190
|
if (arg === "--no-open") {
|
|
8078
8191
|
out.noOpen = true;
|
|
8079
8192
|
continue;
|
|
@@ -8453,6 +8566,11 @@ async function main() {
|
|
|
8453
8566
|
}
|
|
8454
8567
|
const argvParsed = parseArgs(argv);
|
|
8455
8568
|
if (argvParsed.positional.length === 0) {
|
|
8569
|
+
if (await shouldShowWelcome()) {
|
|
8570
|
+
const welcomeCode = await runWelcomeFlow(argvParsed);
|
|
8571
|
+
if (welcomeCode !== 0) process.exit(welcomeCode);
|
|
8572
|
+
return;
|
|
8573
|
+
}
|
|
8456
8574
|
const orchestratorCode2 = await tryOrchestrator(process.cwd(), argvParsed);
|
|
8457
8575
|
if (orchestratorCode2 !== null && orchestratorCode2 !== 0) process.exit(orchestratorCode2);
|
|
8458
8576
|
return;
|
|
@@ -8461,6 +8579,11 @@ async function main() {
|
|
|
8461
8579
|
const parsed = { ...argvParsed, positional: argvParsed.positional.slice(1) };
|
|
8462
8580
|
const { positional, apply: apply6, dryRun, noInstall } = parsed;
|
|
8463
8581
|
const project = parsed.project ?? DEFAULT_PROJECT;
|
|
8582
|
+
if (cmd === "welcome") {
|
|
8583
|
+
const code = await runWelcomeFlow(parsed);
|
|
8584
|
+
if (code !== 0) process.exit(code);
|
|
8585
|
+
return;
|
|
8586
|
+
}
|
|
8464
8587
|
if (cmd === "init") {
|
|
8465
8588
|
const target = positional[0];
|
|
8466
8589
|
if (!target) {
|
|
@@ -8713,11 +8836,18 @@ async function tryOrchestrator(cmd, parsed) {
|
|
|
8713
8836
|
project: projectName,
|
|
8714
8837
|
projectExplicit,
|
|
8715
8838
|
noInstrument: parsed.noInstrument,
|
|
8839
|
+
open: parsed.open,
|
|
8716
8840
|
noOpen: parsed.noOpen,
|
|
8717
8841
|
yes: parsed.yes
|
|
8718
8842
|
});
|
|
8719
8843
|
return result.exitCode;
|
|
8720
8844
|
}
|
|
8845
|
+
async function runWelcomeFlow(parsed) {
|
|
8846
|
+
return runWelcome({
|
|
8847
|
+
login: (loginArgv) => runLoginCommand(loginArgv),
|
|
8848
|
+
orchestrator: async (cwd) => await tryOrchestrator(cwd, parsed) ?? 0
|
|
8849
|
+
});
|
|
8850
|
+
}
|
|
8721
8851
|
var QUERY_VERBS = /* @__PURE__ */ new Set([
|
|
8722
8852
|
"root-cause",
|
|
8723
8853
|
"blast-radius",
|