@neat.is/core 0.10.6 → 0.10.7-dev.20261011
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/{chunk-MW2XYLCS.js → chunk-6K22WGO7.js} +49 -1
- package/dist/{chunk-MW2XYLCS.js.map → chunk-6K22WGO7.js.map} +1 -1
- package/dist/chunk-AEMLUMHC.js +42 -0
- package/dist/chunk-AEMLUMHC.js.map +1 -0
- package/dist/{chunk-OKM4N7GH.js → chunk-S5S2FYCQ.js} +26 -5
- package/dist/chunk-S5S2FYCQ.js.map +1 -0
- package/dist/cli.cjs +137 -10
- 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 +53 -12
- package/dist/cli.js.map +1 -1
- package/dist/incidents-render.cjs +66 -0
- package/dist/incidents-render.cjs.map +1 -0
- package/dist/incidents-render.d.cts +5 -0
- package/dist/incidents-render.d.ts +5 -0
- package/dist/incidents-render.js +8 -0
- package/dist/incidents-render.js.map +1 -0
- package/dist/index.cjs +72 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +78 -6
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +8 -5
- package/dist/neatd.js.map +1 -1
- package/dist/server.cjs +48 -0
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +7 -2
- package/dist/chunk-OKM4N7GH.js.map +0 -1
package/dist/cli.cjs
CHANGED
|
@@ -801,6 +801,7 @@ __export(cli_exports, {
|
|
|
801
801
|
cwdProjectApplies: () => cwdProjectApplies,
|
|
802
802
|
isLoopbackEndpoint: () => isLoopbackEndpoint,
|
|
803
803
|
isNpxInvocation: () => isNpxInvocation,
|
|
804
|
+
loopbackResolutionHint: () => loopbackResolutionHint,
|
|
804
805
|
main: () => main,
|
|
805
806
|
parseArgs: () => parseArgs,
|
|
806
807
|
printBanner: () => printBanner,
|
|
@@ -18500,6 +18501,36 @@ var import_types64 = require("@neat.is/types");
|
|
|
18500
18501
|
var CODE_FILEPATH_ATTR2 = "code.filepath";
|
|
18501
18502
|
var CODE_LINENO_ATTR2 = "code.lineno";
|
|
18502
18503
|
var BLAST_NEAREST_LIMIT = 5;
|
|
18504
|
+
function deployContextOf(ev) {
|
|
18505
|
+
const a = ev.attributes;
|
|
18506
|
+
if (!a) return void 0;
|
|
18507
|
+
const str = (k) => typeof a[k] === "string" ? a[k] : void 0;
|
|
18508
|
+
const int = (k) => typeof a[k] === "number" && Number.isFinite(a[k]) ? Math.trunc(a[k]) : void 0;
|
|
18509
|
+
const strs = (k) => {
|
|
18510
|
+
const v = a[k];
|
|
18511
|
+
return Array.isArray(v) && v.every((x) => typeof x === "string") && v.length > 0 ? v : void 0;
|
|
18512
|
+
};
|
|
18513
|
+
const ctx = {};
|
|
18514
|
+
const fault = str("k8s.fault");
|
|
18515
|
+
if (fault) ctx.fault = fault;
|
|
18516
|
+
const waiting = str("k8s.waitingReason");
|
|
18517
|
+
if (waiting) ctx.waitingReason = waiting;
|
|
18518
|
+
const image = str("k8s.image");
|
|
18519
|
+
if (image) ctx.image = image;
|
|
18520
|
+
const restarts = int("k8s.restartCount");
|
|
18521
|
+
if (restarts !== void 0) ctx.restartCount = restarts;
|
|
18522
|
+
const termReason = str("k8s.terminatedReason");
|
|
18523
|
+
if (termReason) ctx.terminatedReason = termReason;
|
|
18524
|
+
const exitCode = int("k8s.exitCode");
|
|
18525
|
+
if (exitCode !== void 0) ctx.exitCode = exitCode;
|
|
18526
|
+
const env = strs("k8s.containerEnv");
|
|
18527
|
+
if (env) ctx.containerEnv = env;
|
|
18528
|
+
const args = strs("k8s.containerArgs");
|
|
18529
|
+
if (args) ctx.containerArgs = args;
|
|
18530
|
+
const log = str("k8s.processLog");
|
|
18531
|
+
if (log) ctx.processLog = log;
|
|
18532
|
+
return Object.keys(ctx).length > 0 ? ctx : void 0;
|
|
18533
|
+
}
|
|
18503
18534
|
function grainOf2(graph, nodeId) {
|
|
18504
18535
|
if (graph.hasNode(nodeId)) {
|
|
18505
18536
|
const t = graph.getNodeAttributes(nodeId).type;
|
|
@@ -18552,6 +18583,11 @@ function promoteCauseLocus(graph, causeNode, incidents) {
|
|
|
18552
18583
|
return locusFromNode(graph, causeNode);
|
|
18553
18584
|
}
|
|
18554
18585
|
function divergenceSummary(d) {
|
|
18586
|
+
if (d.type === "observed-symbol-mismatch" || d.source === d.target) {
|
|
18587
|
+
const symbol = "symbol" in d && d.symbol ? d.symbol : d.source;
|
|
18588
|
+
const at = "location" in d && d.location ? ` at ${d.location}` : "";
|
|
18589
|
+
return `${symbol}${at}`;
|
|
18590
|
+
}
|
|
18555
18591
|
const column = "column" in d && d.column ? `.${d.column}` : "";
|
|
18556
18592
|
const label = "table" in d && d.table ? `${d.table}${column}` : `${d.source} \u2192 ${d.target}`;
|
|
18557
18593
|
return label;
|
|
@@ -18642,6 +18678,10 @@ function buildIncidentCard(graph, errorEvent, incidents, policies) {
|
|
|
18642
18678
|
...nearest.length > 0 ? { blastRadius: { totalAffected: blast.totalAffected, nearest } } : {},
|
|
18643
18679
|
...policyCards.length > 0 ? { policies: policyCards } : {},
|
|
18644
18680
|
...divergences.length > 0 ? { divergence: divergences } : {},
|
|
18681
|
+
...(() => {
|
|
18682
|
+
const dc = deployContextOf(errorEvent);
|
|
18683
|
+
return dc ? { deployContext: dc } : {};
|
|
18684
|
+
})(),
|
|
18645
18685
|
headline: renderHeadline(graph, errorEvent, locus, rootCause?.node ?? null)
|
|
18646
18686
|
};
|
|
18647
18687
|
card.grade = gradeIncidentCard(card);
|
|
@@ -18996,6 +19036,15 @@ var TYPE_NOUNS = [
|
|
|
18996
19036
|
];
|
|
18997
19037
|
var EMBED_MIN_SCORE = 0.35;
|
|
18998
19038
|
async function resolveEntities(graph, question, searchIndex, maxNodes) {
|
|
19039
|
+
const verbatim = [];
|
|
19040
|
+
for (const word of question.split(/\s+/)) {
|
|
19041
|
+
const id = word.replace(/^["'`(<]+|["'`)>?!.,;]+$/g, "");
|
|
19042
|
+
if (!id || !graph.hasNode(id) || verbatim.some((m) => m.nodeId === id)) continue;
|
|
19043
|
+
const node = graph.getNodeAttributes(id);
|
|
19044
|
+
if (node.type === import_types65.NodeType.FrontierNode) continue;
|
|
19045
|
+
verbatim.push({ nodeId: id, label: nodeName(node), via: "id", score: 1 });
|
|
19046
|
+
}
|
|
19047
|
+
if (verbatim.length > 0) return { matched: verbatim.slice(0, maxNodes) };
|
|
18999
19048
|
const qTokens = queryTokens(question);
|
|
19000
19049
|
const normalized = question.toLowerCase();
|
|
19001
19050
|
const best = /* @__PURE__ */ new Map();
|
|
@@ -31503,7 +31552,7 @@ async function connectorAdd(args, deps) {
|
|
|
31503
31552
|
}
|
|
31504
31553
|
const { replaced } = await upsertConnectorEntry(entry2, deps.home);
|
|
31505
31554
|
const verb = replaced ? "updated" : "added";
|
|
31506
|
-
const where = project ? `project "${project}"` : "
|
|
31555
|
+
const where = project ? `project "${project}"` : "every project (no --project set)";
|
|
31507
31556
|
deps.out(`${verb} connector "${id}" (${provider}) for ${where}.`);
|
|
31508
31557
|
if (push) {
|
|
31509
31558
|
deps.out("The drain is live \u2014 traces arrive at the daemon's OTLP receiver. No poll or restart needed.");
|
|
@@ -31566,7 +31615,7 @@ async function connectorList(deps, filterProject, json = false) {
|
|
|
31566
31615
|
}
|
|
31567
31616
|
deps.out("id provider project credential");
|
|
31568
31617
|
for (const e of entries) {
|
|
31569
|
-
const project = e.project ?? "(
|
|
31618
|
+
const project = e.project ?? "(every project)";
|
|
31570
31619
|
const credential = describeCredential(e.credential, deps.env).map((c) => {
|
|
31571
31620
|
const status2 = c.status ? ` (${c.status})` : c.kind === "plaintext" ? " (plaintext)" : "";
|
|
31572
31621
|
return c.field ? `${c.field}=${c.display}${status2}` : `${c.display}${status2}`;
|
|
@@ -32580,6 +32629,47 @@ var import_node_path96 = __toESM(require("path"), 1);
|
|
|
32580
32629
|
// src/cli-client.ts
|
|
32581
32630
|
init_cjs_shims();
|
|
32582
32631
|
var import_types113 = require("@neat.is/types");
|
|
32632
|
+
|
|
32633
|
+
// src/incidents-render.ts
|
|
32634
|
+
init_cjs_shims();
|
|
32635
|
+
function deployHintLines(ev) {
|
|
32636
|
+
const a = ev.attributes;
|
|
32637
|
+
if (!a) return [];
|
|
32638
|
+
const s = (k) => typeof a[k] === "string" ? a[k] : void 0;
|
|
32639
|
+
const n = (k) => typeof a[k] === "number" ? a[k] : void 0;
|
|
32640
|
+
const arr = (k) => Array.isArray(a[k]) ? a[k].filter((x) => typeof x === "string") : [];
|
|
32641
|
+
const fault = s("k8s.fault");
|
|
32642
|
+
const env = arr("k8s.containerEnv");
|
|
32643
|
+
const args = arr("k8s.containerArgs");
|
|
32644
|
+
const log = s("k8s.processLog");
|
|
32645
|
+
if (!fault && env.length === 0 && args.length === 0 && !log) return [];
|
|
32646
|
+
const out = [];
|
|
32647
|
+
if (fault) {
|
|
32648
|
+
const bits = [fault];
|
|
32649
|
+
const wr = s("k8s.waitingReason");
|
|
32650
|
+
if (wr) bits.push(wr);
|
|
32651
|
+
const rc = n("k8s.restartCount");
|
|
32652
|
+
if (rc !== void 0) bits.push(`restarts=${rc}`);
|
|
32653
|
+
const ec = n("k8s.exitCode");
|
|
32654
|
+
if (ec !== void 0) bits.push(`exit=${ec}`);
|
|
32655
|
+
const tr = s("k8s.terminatedReason");
|
|
32656
|
+
if (tr) bits.push(tr);
|
|
32657
|
+
out.push(`deploy: ${bits.join(" ")}`);
|
|
32658
|
+
}
|
|
32659
|
+
const cap = (xs, max) => {
|
|
32660
|
+
const joined = xs.join(" ");
|
|
32661
|
+
return joined.length > max ? `${joined.slice(0, max)}\u2026(+${xs.length} entries)` : joined;
|
|
32662
|
+
};
|
|
32663
|
+
if (env.length > 0) out.push(`env: ${cap(env, 700)}`);
|
|
32664
|
+
if (args.length > 0) out.push(`args: ${cap(args, 300)}`);
|
|
32665
|
+
if (log) {
|
|
32666
|
+
const tail = log.split("\n").filter((l) => l.trim().length > 0).slice(-4);
|
|
32667
|
+
if (tail.length > 0) out.push(`log: ${tail.join(" \u23CE ")}`);
|
|
32668
|
+
}
|
|
32669
|
+
return out;
|
|
32670
|
+
}
|
|
32671
|
+
|
|
32672
|
+
// src/cli-client.ts
|
|
32583
32673
|
var HttpError = class extends Error {
|
|
32584
32674
|
constructor(status2, message, responseBody = "") {
|
|
32585
32675
|
super(message);
|
|
@@ -32836,6 +32926,8 @@ async function runIncidents(client, input) {
|
|
|
32836
32926
|
const blockLines = [];
|
|
32837
32927
|
for (const ev of ordered) {
|
|
32838
32928
|
blockLines.push(` ${ev.timestamp} \u2014 ${ev.service}: ${ev.errorMessage}`);
|
|
32929
|
+
if (ev.exceptionType) blockLines.push(` exception: ${ev.exceptionType}`);
|
|
32930
|
+
for (const l of deployHintLines(ev)) blockLines.push(` ${l}`);
|
|
32839
32931
|
blockLines.push(` trace=${ev.traceId} span=${ev.spanId}`);
|
|
32840
32932
|
}
|
|
32841
32933
|
const target = input.nodeId ?? "the project";
|
|
@@ -33691,7 +33783,7 @@ var import_promises5 = __toESM(require("readline/promises"), 1);
|
|
|
33691
33783
|
|
|
33692
33784
|
// src/agent-directive.generated.ts
|
|
33693
33785
|
init_cjs_shims();
|
|
33694
|
-
var AGENT_DIRECTIVE = '##
|
|
33786
|
+
var AGENT_DIRECTIVE = '## Let NEAT investigate before you search files\n\nThis project has NEAT. It does the investigation for this system so you can\nspend your effort on the code: what exists, what actually ran, where a failure\nbegan, and what a change will reach. It answers from a live graph that fuses\ncode, data and infrastructure declarations with runtime traffic, incidents and\nsupported provider telemetry. The graph is deterministic: you supply the\nreasoning about the fix; NEAT resolves named nodes and traverses recorded\nevidence rather than asking an LLM to infer architecture from file names.\n\nEvery claim carries provenance and confidence. `EXTRACTED` comes from recognized\nsource or configuration, `OBSERVED` from spans or supported provider signals,\n`INFERRED` from a bridged relationship, and `STALE` marks an observed edge that\nwent quiet. Missing runtime evidence does not prove a path never runs.\n\n**For questions about this system\'s behavior, structure, data dependencies,\nfailures, or change impact, call the MCP `ask` tool (`mcp__neat__ask`) before\nRead/Grep/Glob/Bash.** You do not need an exact node id. `ask` resolves names\nand routes to the relevant graph traversal, returning provenance-tagged facts.\nWhen MCP is unavailable, use the same door through the CLI:\n\n```\nnpx neat.is ask "why is checkout failing?"\nnpx neat.is ask "what breaks if I change the orders table?"\nnpx neat.is ask "what does the payments service depend on at runtime?"\n```\n\nFor a failure, call `get_incident_card` on the failing service, file or symbol\n(if you don\'t know which, `ask` what is failing first). The card is the\ninvestigation already done: the incident, its likely cause chain, blast radius,\nthe governing policies and divergence, in one work order. Use `expand` one hop\nat a time and `relate` to test whether the suspected cause and symptom share a\nsignal. Check provenance and confidence before acting.\n\nBefore a change, use `get_blast_radius` and `check_policies` to see dependents\nand advisory architectural rules. Use `get_divergences` to compare declared\nintent with observed behavior, `get_graph_diff` to compare a saved snapshot\nwith the live graph, and `get_recent_stale_edges` when traffic goes quiet.\n`get_dependencies` maps a node\'s outgoing graph; `get_observed_dependencies`\nshows only seen runtime/provider calls. `semantic_search` finds node labels,\nnot arbitrary source text.\n\nRead source when the graph does not model what you need: comments, arbitrary\nstring literals, config details, unsupported syntax, or a repository that has\nnot been extracted or connected. If a graph answer is empty, check the daemon\nwith `npx neat.is list` or restart it with `npx neat.is up`; wire MCP with\n`npx neat.is skill --apply`.\n';
|
|
33695
33787
|
|
|
33696
33788
|
// src/welcome.ts
|
|
33697
33789
|
var WORDMARK = [
|
|
@@ -33706,10 +33798,13 @@ function renderAgentSetupPrompt(project, restPort) {
|
|
|
33706
33798
|
const endpoint2 = restPort === void 0 ? "The daemon endpoint is unavailable; run `npx neat.is up` to start it." : `The local daemon is serving it at http://127.0.0.1:${restPort}.`;
|
|
33707
33799
|
return `NEAT is set up for project "${project}". ${endpoint2}
|
|
33708
33800
|
|
|
33709
|
-
NEAT
|
|
33710
|
-
|
|
33711
|
-
|
|
33712
|
-
what a
|
|
33801
|
+
NEAT does the investigation for this system, so you can spend your effort on the
|
|
33802
|
+
code. When something fails, call get_incident_card on the failing service, file
|
|
33803
|
+
or symbol: one work order with where the failure is, the cause chain (provenance
|
|
33804
|
+
on every hop), what a fix reaches, the policies that govern the code, and where
|
|
33805
|
+
code and runtime disagree. For anything else about how the system works, ask.
|
|
33806
|
+
NEAT answers from a live graph that fuses the code with what actually ran; a
|
|
33807
|
+
path it hasn't seen run is not proof the path never runs.
|
|
33713
33808
|
|
|
33714
33809
|
1. Wire NEAT's MCP server into this agent session:
|
|
33715
33810
|
npx neat.is skill --apply
|
|
@@ -33720,8 +33815,10 @@ what a change could affect. Missing observations do not prove a path never runs.
|
|
|
33720
33815
|
If the daemon is unreachable, run \`npx neat.is up\`.
|
|
33721
33816
|
|
|
33722
33817
|
${AGENT_DIRECTIVE}
|
|
33723
|
-
|
|
33724
|
-
neat-
|
|
33818
|
+
Runtime evidence arrives once the app runs with NEAT attached: for Node, load
|
|
33819
|
+
.env.neat first (\`set -a; . ./.env.neat; set +a\`); for Python, start under
|
|
33820
|
+
\`opentelemetry-instrument\`. Daemon log: neat-out/daemon.log. Reopen this
|
|
33821
|
+
door with \`npx neat.is welcome\`.`;
|
|
33725
33822
|
}
|
|
33726
33823
|
function printHeader(out) {
|
|
33727
33824
|
for (const line of WORDMARK) out(line);
|
|
@@ -36497,10 +36594,15 @@ async function main() {
|
|
|
36497
36594
|
return;
|
|
36498
36595
|
}
|
|
36499
36596
|
if (cmd === "sync") {
|
|
36597
|
+
const syncTarget = await resolveClientTarget({
|
|
36598
|
+
...parsed.project ? { project: parsed.project } : {},
|
|
36599
|
+
...parsed.profile ? { profile: parsed.profile } : {}
|
|
36600
|
+
});
|
|
36500
36601
|
const result = await runSync({
|
|
36501
36602
|
...parsed.project ? { project: parsed.project } : {},
|
|
36502
36603
|
...parsed.to ? { to: parsed.to } : {},
|
|
36503
36604
|
...parsed.token ? { token: parsed.token } : {},
|
|
36605
|
+
daemonUrl: syncTarget.endpoint,
|
|
36504
36606
|
dryRun: parsed.dryRun,
|
|
36505
36607
|
noInstrument: parsed.noInstrument,
|
|
36506
36608
|
sourceEdit: parsed.sourceEdit,
|
|
@@ -36659,9 +36761,28 @@ async function resolveClientTarget(opts = {}) {
|
|
|
36659
36761
|
if (daemon) {
|
|
36660
36762
|
return { endpoint: `http://localhost:${daemon.record.ports.rest}`, source: "daemon-record" };
|
|
36661
36763
|
}
|
|
36764
|
+
} else {
|
|
36765
|
+
const live = (await discoverDaemons()).filter((d) => d.live);
|
|
36766
|
+
if (live.length === 1) {
|
|
36767
|
+
return {
|
|
36768
|
+
endpoint: `http://localhost:${live[0].record.ports.rest}`,
|
|
36769
|
+
source: "sole-daemon"
|
|
36770
|
+
};
|
|
36771
|
+
}
|
|
36662
36772
|
}
|
|
36663
36773
|
return { endpoint: "http://localhost:8080", source: "default" };
|
|
36664
36774
|
}
|
|
36775
|
+
async function loopbackResolutionHint() {
|
|
36776
|
+
const live = (await discoverDaemons()).filter((d) => d.live);
|
|
36777
|
+
if (live.length === 0) {
|
|
36778
|
+
return "No NEAT daemon is running. Start one with `npx neat.is` in your project (or `neat up`), then re-run \u2014 or set NEAT_API_URL to a daemon.";
|
|
36779
|
+
}
|
|
36780
|
+
if (live.length > 1) {
|
|
36781
|
+
const list = live.map((d) => ` ${d.record.project} \u2192 http://localhost:${d.record.ports.rest}`).join("\n");
|
|
36782
|
+
return "This directory is not a registered NEAT project and several daemons are running, so NEAT cannot pick one. Run from the project directory, pass --project <name>, or set NEAT_API_URL:\n" + list;
|
|
36783
|
+
}
|
|
36784
|
+
return void 0;
|
|
36785
|
+
}
|
|
36665
36786
|
async function projectForCwd(cwd = process.cwd()) {
|
|
36666
36787
|
try {
|
|
36667
36788
|
return (await findProjectByPath(cwd))?.name;
|
|
@@ -36670,7 +36791,7 @@ async function projectForCwd(cwd = process.cwd()) {
|
|
|
36670
36791
|
}
|
|
36671
36792
|
}
|
|
36672
36793
|
function cwdProjectApplies(source) {
|
|
36673
|
-
return source === "daemon-record" || source === "default";
|
|
36794
|
+
return source === "daemon-record" || source === "sole-daemon" || source === "default";
|
|
36674
36795
|
}
|
|
36675
36796
|
async function resolveDaemonUrl(project, profile) {
|
|
36676
36797
|
return (await resolveClientTarget({ project, profile })).endpoint;
|
|
@@ -36692,6 +36813,11 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
36692
36813
|
}
|
|
36693
36814
|
throw err;
|
|
36694
36815
|
}
|
|
36816
|
+
if (target.source === "default" && !requestedProject && !cwdProject) {
|
|
36817
|
+
const hint = await loopbackResolutionHint();
|
|
36818
|
+
if (hint) process.stderr.write(`${hint}
|
|
36819
|
+
`);
|
|
36820
|
+
}
|
|
36695
36821
|
const client = createHttpClient(target.endpoint, target.authToken);
|
|
36696
36822
|
const positional = parsed.positional;
|
|
36697
36823
|
let makeWork;
|
|
@@ -36939,6 +37065,7 @@ if (/[\\/](?:cli\.(?:cjs|js)|cli|neat|neat\.is)$/.test(entry)) {
|
|
|
36939
37065
|
cwdProjectApplies,
|
|
36940
37066
|
isLoopbackEndpoint,
|
|
36941
37067
|
isNpxInvocation,
|
|
37068
|
+
loopbackResolutionHint,
|
|
36942
37069
|
main,
|
|
36943
37070
|
parseArgs,
|
|
36944
37071
|
printBanner,
|