@neat.is/core 0.5.2 → 0.5.3
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-BI3XKGVG.js → chunk-GJHEZC5K.js} +56 -21
- package/dist/chunk-GJHEZC5K.js.map +1 -0
- package/dist/{chunk-DPEPI2N6.js → chunk-X2AMX3QZ.js} +32 -14
- package/dist/chunk-X2AMX3QZ.js.map +1 -0
- package/dist/cli.cjs +366 -120
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +250 -58
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +85 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +85 -32
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +55 -20
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-BI3XKGVG.js.map +0 -1
- package/dist/chunk-DPEPI2N6.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
resolveNeatVersion,
|
|
10
10
|
validateConnectorEntry,
|
|
11
11
|
writeDaemonRecord
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-X2AMX3QZ.js";
|
|
13
13
|
import {
|
|
14
14
|
buildSearchIndex
|
|
15
15
|
} from "./chunk-BIY46Q6U.js";
|
|
@@ -68,7 +68,7 @@ import {
|
|
|
68
68
|
startPersistLoop,
|
|
69
69
|
startStalenessLoop,
|
|
70
70
|
upsertConnectorEntry
|
|
71
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-GJHEZC5K.js";
|
|
72
72
|
import {
|
|
73
73
|
startOtelGrpcReceiver
|
|
74
74
|
} from "./chunk-I72HTUOG.js";
|
|
@@ -82,8 +82,8 @@ import {
|
|
|
82
82
|
} from "./chunk-CFDPIMRP.js";
|
|
83
83
|
|
|
84
84
|
// src/cli.ts
|
|
85
|
-
import
|
|
86
|
-
import { promises as
|
|
85
|
+
import path10 from "path";
|
|
86
|
+
import { promises as fs8 } from "fs";
|
|
87
87
|
|
|
88
88
|
// src/banner.ts
|
|
89
89
|
import path from "path";
|
|
@@ -3852,8 +3852,181 @@ async function runConnectorCommand(rawArgs, deps = {}) {
|
|
|
3852
3852
|
}
|
|
3853
3853
|
}
|
|
3854
3854
|
|
|
3855
|
-
// src/cli
|
|
3855
|
+
// src/hooks-cli.ts
|
|
3856
3856
|
import path8 from "path";
|
|
3857
|
+
import os from "os";
|
|
3858
|
+
import { promises as fs7 } from "fs";
|
|
3859
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
3860
|
+
var HOOK_FILENAME = "neat-search-nudge.mjs";
|
|
3861
|
+
var GUIDE_FILENAME = "GRAPH_FIRST.md";
|
|
3862
|
+
var GUIDE_INSTALL_NAME = "neat-graph-first.md";
|
|
3863
|
+
var HOOK_MATCHER = "Grep|Glob|Bash";
|
|
3864
|
+
function moduleDir() {
|
|
3865
|
+
return typeof __dirname !== "undefined" ? __dirname : path8.dirname(fileURLToPath3(import.meta.url));
|
|
3866
|
+
}
|
|
3867
|
+
async function readSkillAsset(rel) {
|
|
3868
|
+
const here = moduleDir();
|
|
3869
|
+
const candidates = [
|
|
3870
|
+
path8.resolve(here, "../../claude-skill", rel),
|
|
3871
|
+
path8.resolve(here, "../../../claude-skill", rel),
|
|
3872
|
+
path8.resolve(here, "../claude-skill", rel)
|
|
3873
|
+
];
|
|
3874
|
+
for (const candidate of candidates) {
|
|
3875
|
+
try {
|
|
3876
|
+
return await fs7.readFile(candidate, "utf8");
|
|
3877
|
+
} catch {
|
|
3878
|
+
}
|
|
3879
|
+
}
|
|
3880
|
+
throw new Error(
|
|
3881
|
+
`neat hooks: could not find @neat.is/claude-skill/${rel} \u2014 is the package installed?`
|
|
3882
|
+
);
|
|
3883
|
+
}
|
|
3884
|
+
function neatHome() {
|
|
3885
|
+
const override = process.env.NEAT_HOME;
|
|
3886
|
+
if (override && override.length > 0) return path8.resolve(override);
|
|
3887
|
+
return path8.join(os.homedir(), ".neat");
|
|
3888
|
+
}
|
|
3889
|
+
function claudeSettingsPath() {
|
|
3890
|
+
const override = process.env.NEAT_CLAUDE_SETTINGS;
|
|
3891
|
+
if (override && override.length > 0) return path8.resolve(override);
|
|
3892
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? os.homedir();
|
|
3893
|
+
return path8.join(home, ".claude", "settings.json");
|
|
3894
|
+
}
|
|
3895
|
+
function installedHookPath() {
|
|
3896
|
+
return path8.join(neatHome(), "hooks", HOOK_FILENAME);
|
|
3897
|
+
}
|
|
3898
|
+
function isNeatSearchEntry(entry2) {
|
|
3899
|
+
return (entry2.hooks ?? []).some(
|
|
3900
|
+
(h) => typeof h.command === "string" && h.command.includes(HOOK_FILENAME)
|
|
3901
|
+
);
|
|
3902
|
+
}
|
|
3903
|
+
function neatHookEntry(command) {
|
|
3904
|
+
return { matcher: HOOK_MATCHER, hooks: [{ type: "command", command }] };
|
|
3905
|
+
}
|
|
3906
|
+
function hookCommand(scriptPath) {
|
|
3907
|
+
return `node "${scriptPath}"`;
|
|
3908
|
+
}
|
|
3909
|
+
async function runHooks(opts) {
|
|
3910
|
+
if (opts.printHook) {
|
|
3911
|
+
process.stdout.write(await readSkillAsset(`hooks/${HOOK_FILENAME}`));
|
|
3912
|
+
return { exitCode: 0 };
|
|
3913
|
+
}
|
|
3914
|
+
if (opts.printGuide) {
|
|
3915
|
+
process.stdout.write(await readSkillAsset(GUIDE_FILENAME));
|
|
3916
|
+
return { exitCode: 0 };
|
|
3917
|
+
}
|
|
3918
|
+
if (opts.printSettings) {
|
|
3919
|
+
const block = {
|
|
3920
|
+
hooks: { PreToolUse: [neatHookEntry(hookCommand(installedHookPath()))] }
|
|
3921
|
+
};
|
|
3922
|
+
process.stdout.write(JSON.stringify(block, null, 2) + "\n");
|
|
3923
|
+
return { exitCode: 0 };
|
|
3924
|
+
}
|
|
3925
|
+
if (opts.apply) {
|
|
3926
|
+
const hookScript = await readSkillAsset(`hooks/${HOOK_FILENAME}`);
|
|
3927
|
+
const guide = await readSkillAsset(GUIDE_FILENAME);
|
|
3928
|
+
const scriptPath = installedHookPath();
|
|
3929
|
+
await fs7.mkdir(path8.dirname(scriptPath), { recursive: true });
|
|
3930
|
+
await fs7.writeFile(scriptPath, hookScript, { mode: 493 });
|
|
3931
|
+
const guidePath = path8.join(neatHome(), GUIDE_INSTALL_NAME);
|
|
3932
|
+
await fs7.writeFile(guidePath, guide, "utf8");
|
|
3933
|
+
const settingsFile = claudeSettingsPath();
|
|
3934
|
+
let settings = {};
|
|
3935
|
+
try {
|
|
3936
|
+
settings = JSON.parse(await fs7.readFile(settingsFile, "utf8"));
|
|
3937
|
+
} catch (err) {
|
|
3938
|
+
if (err.code !== "ENOENT") {
|
|
3939
|
+
console.error(
|
|
3940
|
+
`neat hooks: failed to read ${settingsFile} \u2014 ${err.message}`
|
|
3941
|
+
);
|
|
3942
|
+
return { exitCode: 1 };
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3945
|
+
const hooks = settings.hooks ?? {};
|
|
3946
|
+
const preToolUse = Array.isArray(hooks.PreToolUse) ? [...hooks.PreToolUse] : [];
|
|
3947
|
+
const command = hookCommand(scriptPath);
|
|
3948
|
+
const existingIdx = preToolUse.findIndex(isNeatSearchEntry);
|
|
3949
|
+
if (existingIdx >= 0) {
|
|
3950
|
+
preToolUse[existingIdx] = neatHookEntry(command);
|
|
3951
|
+
} else {
|
|
3952
|
+
preToolUse.push(neatHookEntry(command));
|
|
3953
|
+
}
|
|
3954
|
+
const merged = {
|
|
3955
|
+
...settings,
|
|
3956
|
+
hooks: { ...hooks, PreToolUse: preToolUse }
|
|
3957
|
+
};
|
|
3958
|
+
await fs7.mkdir(path8.dirname(settingsFile), { recursive: true });
|
|
3959
|
+
await fs7.writeFile(settingsFile, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
3960
|
+
console.log(`neat hooks: installed the search-nudge hook`);
|
|
3961
|
+
console.log(` script: ${scriptPath}`);
|
|
3962
|
+
console.log(` settings: ${settingsFile} (PreToolUse \u2192 ${HOOK_MATCHER})`);
|
|
3963
|
+
console.log(` guidance: ${guidePath}`);
|
|
3964
|
+
console.log("");
|
|
3965
|
+
console.log("restart Claude Code to load the hook. On a Grep/Glob or a Bash grep,");
|
|
3966
|
+
console.log("your agent will now be nudged to query NEAT first.");
|
|
3967
|
+
console.log("");
|
|
3968
|
+
console.log("The hook is Claude-Code-specific. For agents on other harnesses, paste");
|
|
3969
|
+
console.log(`the guidance above into your project instructions (CLAUDE.md / AGENTS.md).`);
|
|
3970
|
+
return { exitCode: 0 };
|
|
3971
|
+
}
|
|
3972
|
+
usage();
|
|
3973
|
+
return { exitCode: 0 };
|
|
3974
|
+
}
|
|
3975
|
+
function usage() {
|
|
3976
|
+
console.log("neat hooks \u2014 wire NEAT into your agent so it queries the graph before grepping");
|
|
3977
|
+
console.log("");
|
|
3978
|
+
console.log(" --apply install the Claude Code search-nudge hook and write the");
|
|
3979
|
+
console.log(" graph-first guidance to ~/.neat/, merging into");
|
|
3980
|
+
console.log(" ~/.claude/settings.json without touching your other hooks");
|
|
3981
|
+
console.log(" --print-hook print the hook script to stdout");
|
|
3982
|
+
console.log(" --print-guide print the agent-agnostic graph-first guidance to stdout");
|
|
3983
|
+
console.log(" --print-settings print the settings.json PreToolUse block --apply would add");
|
|
3984
|
+
console.log("");
|
|
3985
|
+
console.log("The hook is a gentle, non-blocking nudge \u2014 searches still run. It is");
|
|
3986
|
+
console.log("Claude-Code-specific; other harnesses get the same steer from the guidance.");
|
|
3987
|
+
}
|
|
3988
|
+
async function runHooksCommand(args) {
|
|
3989
|
+
const opts = {
|
|
3990
|
+
apply: false,
|
|
3991
|
+
printHook: false,
|
|
3992
|
+
printGuide: false,
|
|
3993
|
+
printSettings: false
|
|
3994
|
+
};
|
|
3995
|
+
for (const arg of args) {
|
|
3996
|
+
switch (arg) {
|
|
3997
|
+
case "--apply":
|
|
3998
|
+
opts.apply = true;
|
|
3999
|
+
break;
|
|
4000
|
+
case "--print-hook":
|
|
4001
|
+
opts.printHook = true;
|
|
4002
|
+
break;
|
|
4003
|
+
case "--print-guide":
|
|
4004
|
+
opts.printGuide = true;
|
|
4005
|
+
break;
|
|
4006
|
+
case "--print-settings":
|
|
4007
|
+
opts.printSettings = true;
|
|
4008
|
+
break;
|
|
4009
|
+
case "-h":
|
|
4010
|
+
case "--help":
|
|
4011
|
+
usage();
|
|
4012
|
+
return 0;
|
|
4013
|
+
default:
|
|
4014
|
+
console.error(`neat hooks: unknown flag "${arg}"`);
|
|
4015
|
+
usage();
|
|
4016
|
+
return 2;
|
|
4017
|
+
}
|
|
4018
|
+
}
|
|
4019
|
+
try {
|
|
4020
|
+
const { exitCode } = await runHooks(opts);
|
|
4021
|
+
return exitCode;
|
|
4022
|
+
} catch (err) {
|
|
4023
|
+
console.error(err.message);
|
|
4024
|
+
return 1;
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
4027
|
+
|
|
4028
|
+
// src/cli-verbs.ts
|
|
4029
|
+
import path9 from "path";
|
|
3857
4030
|
|
|
3858
4031
|
// src/cli-client.ts
|
|
3859
4032
|
import { Provenance as Provenance2 } from "@neat.is/types";
|
|
@@ -3881,10 +4054,10 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
3881
4054
|
const root = baseUrl.replace(/\/$/, "");
|
|
3882
4055
|
const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
|
|
3883
4056
|
return {
|
|
3884
|
-
async get(
|
|
4057
|
+
async get(path11) {
|
|
3885
4058
|
let res;
|
|
3886
4059
|
try {
|
|
3887
|
-
res = await fetch(`${root}${
|
|
4060
|
+
res = await fetch(`${root}${path11}`, {
|
|
3888
4061
|
headers: { ...authHeader }
|
|
3889
4062
|
});
|
|
3890
4063
|
} catch (err) {
|
|
@@ -3896,16 +4069,16 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
3896
4069
|
const body = await res.text().catch(() => "");
|
|
3897
4070
|
throw new HttpError(
|
|
3898
4071
|
res.status,
|
|
3899
|
-
`${res.status} ${res.statusText} on GET ${
|
|
4072
|
+
`${res.status} ${res.statusText} on GET ${path11}: ${body}`,
|
|
3900
4073
|
body
|
|
3901
4074
|
);
|
|
3902
4075
|
}
|
|
3903
4076
|
return await res.json();
|
|
3904
4077
|
},
|
|
3905
|
-
async post(
|
|
4078
|
+
async post(path11, body) {
|
|
3906
4079
|
let res;
|
|
3907
4080
|
try {
|
|
3908
|
-
res = await fetch(`${root}${
|
|
4081
|
+
res = await fetch(`${root}${path11}`, {
|
|
3909
4082
|
method: "POST",
|
|
3910
4083
|
headers: { "content-type": "application/json", ...authHeader },
|
|
3911
4084
|
body: JSON.stringify(body)
|
|
@@ -3919,7 +4092,7 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
3919
4092
|
const text = await res.text().catch(() => "");
|
|
3920
4093
|
throw new HttpError(
|
|
3921
4094
|
res.status,
|
|
3922
|
-
`${res.status} ${res.statusText} on POST ${
|
|
4095
|
+
`${res.status} ${res.statusText} on POST ${path11}: ${text}`,
|
|
3923
4096
|
text
|
|
3924
4097
|
);
|
|
3925
4098
|
}
|
|
@@ -3933,12 +4106,12 @@ function projectPath(project, suffix) {
|
|
|
3933
4106
|
}
|
|
3934
4107
|
async function runRootCause(client, input) {
|
|
3935
4108
|
const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
|
|
3936
|
-
const
|
|
4109
|
+
const path11 = projectPath(
|
|
3937
4110
|
input.project,
|
|
3938
4111
|
`/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
|
|
3939
4112
|
);
|
|
3940
4113
|
try {
|
|
3941
|
-
const result = await client.get(
|
|
4114
|
+
const result = await client.get(path11);
|
|
3942
4115
|
const arrowPath = result.traversalPath.join(" \u2190 ");
|
|
3943
4116
|
const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
|
|
3944
4117
|
const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
|
|
@@ -3964,12 +4137,12 @@ async function runRootCause(client, input) {
|
|
|
3964
4137
|
}
|
|
3965
4138
|
async function runBlastRadius(client, input) {
|
|
3966
4139
|
const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
|
|
3967
|
-
const
|
|
4140
|
+
const path11 = projectPath(
|
|
3968
4141
|
input.project,
|
|
3969
4142
|
`/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
|
|
3970
4143
|
);
|
|
3971
4144
|
try {
|
|
3972
|
-
const result = await client.get(
|
|
4145
|
+
const result = await client.get(path11);
|
|
3973
4146
|
if (result.totalAffected === 0) {
|
|
3974
4147
|
return {
|
|
3975
4148
|
summary: `${result.origin} has no dependents. Nothing else would break if it failed.`
|
|
@@ -4003,12 +4176,12 @@ function formatBlastEntry(n) {
|
|
|
4003
4176
|
}
|
|
4004
4177
|
async function runDependencies(client, input) {
|
|
4005
4178
|
const depth = input.depth ?? 3;
|
|
4006
|
-
const
|
|
4179
|
+
const path11 = projectPath(
|
|
4007
4180
|
input.project,
|
|
4008
4181
|
`/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
|
|
4009
4182
|
);
|
|
4010
4183
|
try {
|
|
4011
|
-
const result = await client.get(
|
|
4184
|
+
const result = await client.get(path11);
|
|
4012
4185
|
if (result.total === 0) {
|
|
4013
4186
|
return {
|
|
4014
4187
|
summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
|
|
@@ -4100,9 +4273,9 @@ function formatDuration(ms) {
|
|
|
4100
4273
|
return `${Math.round(h / 24)}d`;
|
|
4101
4274
|
}
|
|
4102
4275
|
async function runIncidents(client, input) {
|
|
4103
|
-
const
|
|
4276
|
+
const path11 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
|
|
4104
4277
|
try {
|
|
4105
|
-
const body = await client.get(
|
|
4278
|
+
const body = await client.get(path11);
|
|
4106
4279
|
const events = body.events;
|
|
4107
4280
|
if (events.length === 0) {
|
|
4108
4281
|
return {
|
|
@@ -4392,7 +4565,7 @@ async function resolveProjectEntry(opts) {
|
|
|
4392
4565
|
const cwd = opts.cwd ?? process.cwd();
|
|
4393
4566
|
const resolvedCwd = await normalizeProjectPath(cwd);
|
|
4394
4567
|
for (const entry2 of entries) {
|
|
4395
|
-
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${
|
|
4568
|
+
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${path9.sep}`)) {
|
|
4396
4569
|
return entry2;
|
|
4397
4570
|
}
|
|
4398
4571
|
}
|
|
@@ -4557,7 +4730,7 @@ function isNpxInvocation() {
|
|
|
4557
4730
|
function commandPrefix() {
|
|
4558
4731
|
return isNpxInvocation() ? "npx neat.is" : "neat";
|
|
4559
4732
|
}
|
|
4560
|
-
function
|
|
4733
|
+
function usage2() {
|
|
4561
4734
|
const neat = commandPrefix();
|
|
4562
4735
|
console.log("Installed via npx? Prefix commands with `npx neat.is`, or install once: `npm i -g neat.is`.");
|
|
4563
4736
|
console.log("");
|
|
@@ -4594,6 +4767,14 @@ function usage() {
|
|
|
4594
4767
|
console.log(" Flags:");
|
|
4595
4768
|
console.log(" --print-config print the JSON snippet to stdout");
|
|
4596
4769
|
console.log(" --apply merge mcpServers.neat into ~/.claude.json");
|
|
4770
|
+
console.log(" hooks Wire NEAT into your agent so it queries the graph before");
|
|
4771
|
+
console.log(" grepping. Installs a gentle Claude Code search-nudge hook and");
|
|
4772
|
+
console.log(" writes agent-agnostic graph-first guidance for other harnesses.");
|
|
4773
|
+
console.log(" Flags:");
|
|
4774
|
+
console.log(" --apply install the hook + guidance");
|
|
4775
|
+
console.log(" --print-hook print the hook script");
|
|
4776
|
+
console.log(" --print-guide print the graph-first guidance");
|
|
4777
|
+
console.log(" --print-settings print the settings.json block --apply adds");
|
|
4597
4778
|
console.log(" deploy Detect the deploy substrate, generate NEAT_AUTH_TOKEN,");
|
|
4598
4779
|
console.log(" emit a docker-compose / systemd / docker run artifact, and");
|
|
4599
4780
|
console.log(" print the OTel env-vars block to paste into your platform.");
|
|
@@ -4838,7 +5019,7 @@ async function buildPatchSections(services, project) {
|
|
|
4838
5019
|
}
|
|
4839
5020
|
async function runInit(opts) {
|
|
4840
5021
|
const written = [];
|
|
4841
|
-
const stat = await
|
|
5022
|
+
const stat = await fs8.stat(opts.scanPath).catch(() => null);
|
|
4842
5023
|
if (!stat || !stat.isDirectory()) {
|
|
4843
5024
|
console.error(`neat init: ${opts.scanPath} is not a directory`);
|
|
4844
5025
|
return { exitCode: 2, writtenFiles: written };
|
|
@@ -4847,13 +5028,13 @@ async function runInit(opts) {
|
|
|
4847
5028
|
printDiscoveryReport(opts, services);
|
|
4848
5029
|
const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
|
|
4849
5030
|
const patch = renderPatch(sections);
|
|
4850
|
-
const patchPath =
|
|
5031
|
+
const patchPath = path10.join(opts.scanPath, "neat.patch");
|
|
4851
5032
|
if (opts.dryRun) {
|
|
4852
|
-
await
|
|
5033
|
+
await fs8.writeFile(patchPath, patch, "utf8");
|
|
4853
5034
|
written.push(patchPath);
|
|
4854
5035
|
console.log(`dry-run: patch written to ${patchPath}`);
|
|
4855
|
-
const gitignorePath =
|
|
4856
|
-
const gitignoreExists = await
|
|
5036
|
+
const gitignorePath = path10.join(opts.scanPath, ".gitignore");
|
|
5037
|
+
const gitignoreExists = await fs8.stat(gitignorePath).then(() => true).catch(() => false);
|
|
4857
5038
|
const verb = gitignoreExists ? "append" : "create";
|
|
4858
5039
|
console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
|
|
4859
5040
|
console.log("rerun without --dry-run to register and snapshot.");
|
|
@@ -4864,9 +5045,9 @@ async function runInit(opts) {
|
|
|
4864
5045
|
const graph = getGraph(graphKey);
|
|
4865
5046
|
const projectPaths = pathsForProject(
|
|
4866
5047
|
graphKey,
|
|
4867
|
-
|
|
5048
|
+
path10.join(opts.scanPath, "neat-out")
|
|
4868
5049
|
);
|
|
4869
|
-
const errorsPath =
|
|
5050
|
+
const errorsPath = path10.join(path10.dirname(opts.outPath), path10.basename(projectPaths.errorsPath));
|
|
4870
5051
|
const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
|
|
4871
5052
|
await saveGraphToDisk(graph, opts.outPath);
|
|
4872
5053
|
written.push(opts.outPath);
|
|
@@ -4945,7 +5126,7 @@ async function runInit(opts) {
|
|
|
4945
5126
|
console.log("Run `npm install` (or your language equivalent) to refresh lockfiles.");
|
|
4946
5127
|
}
|
|
4947
5128
|
} else {
|
|
4948
|
-
await
|
|
5129
|
+
await fs8.writeFile(patchPath, patch, "utf8");
|
|
4949
5130
|
written.push(patchPath);
|
|
4950
5131
|
}
|
|
4951
5132
|
}
|
|
@@ -4985,9 +5166,9 @@ var CLAUDE_SKILL_CONFIG = {
|
|
|
4985
5166
|
};
|
|
4986
5167
|
function claudeConfigPath() {
|
|
4987
5168
|
const override = process.env.NEAT_CLAUDE_CONFIG;
|
|
4988
|
-
if (override && override.length > 0) return
|
|
5169
|
+
if (override && override.length > 0) return path10.resolve(override);
|
|
4989
5170
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
4990
|
-
return
|
|
5171
|
+
return path10.join(home, ".claude.json");
|
|
4991
5172
|
}
|
|
4992
5173
|
async function runSkill(opts) {
|
|
4993
5174
|
const snippet = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
|
|
@@ -4999,7 +5180,7 @@ async function runSkill(opts) {
|
|
|
4999
5180
|
const target = claudeConfigPath();
|
|
5000
5181
|
let existing = {};
|
|
5001
5182
|
try {
|
|
5002
|
-
existing = JSON.parse(await
|
|
5183
|
+
existing = JSON.parse(await fs8.readFile(target, "utf8"));
|
|
5003
5184
|
} catch (err) {
|
|
5004
5185
|
if (err.code !== "ENOENT") {
|
|
5005
5186
|
console.error(`neat skill: failed to read ${target} \u2014 ${err.message}`);
|
|
@@ -5011,10 +5192,13 @@ async function runSkill(opts) {
|
|
|
5011
5192
|
...existing,
|
|
5012
5193
|
mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
|
|
5013
5194
|
};
|
|
5014
|
-
await
|
|
5015
|
-
await
|
|
5195
|
+
await fs8.mkdir(path10.dirname(target), { recursive: true });
|
|
5196
|
+
await fs8.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
5016
5197
|
console.log(`neat skill: wrote mcpServers.neat to ${target}`);
|
|
5017
5198
|
console.log("restart Claude Code to pick up the new MCP server.");
|
|
5199
|
+
console.log("");
|
|
5200
|
+
console.log("Tip: run `neat hooks --apply` to also install the search-nudge hook, so");
|
|
5201
|
+
console.log("your agent reaches for the graph before it grep-scans the repo.");
|
|
5018
5202
|
return { exitCode: 0 };
|
|
5019
5203
|
}
|
|
5020
5204
|
console.log("neat skill \u2014 Claude Code MCP drop-in for NEAT");
|
|
@@ -5027,13 +5211,16 @@ async function runSkill(opts) {
|
|
|
5027
5211
|
console.log("");
|
|
5028
5212
|
console.log("The MCP server reads NEAT_CORE_URL for the daemon URL \u2014 point it at a");
|
|
5029
5213
|
console.log("non-default daemon by editing that value in the generated config.");
|
|
5214
|
+
console.log("");
|
|
5215
|
+
console.log("See also `neat hooks --apply` \u2014 the search-nudge hook + graph-first guidance");
|
|
5216
|
+
console.log("that steer an agent to query the graph before falling back to text search.");
|
|
5030
5217
|
return { exitCode: 0 };
|
|
5031
5218
|
}
|
|
5032
5219
|
async function main() {
|
|
5033
5220
|
const argv = process.argv.slice(2);
|
|
5034
5221
|
const cmd0 = argv[0];
|
|
5035
5222
|
if (cmd0 === "-h" || cmd0 === "--help") {
|
|
5036
|
-
|
|
5223
|
+
usage2();
|
|
5037
5224
|
process.exit(0);
|
|
5038
5225
|
}
|
|
5039
5226
|
if (cmd0 === "--version" || cmd0 === "-v" || cmd0 === "version") {
|
|
@@ -5045,6 +5232,11 @@ async function main() {
|
|
|
5045
5232
|
if (code !== 0) process.exit(code);
|
|
5046
5233
|
return;
|
|
5047
5234
|
}
|
|
5235
|
+
if (cmd0 === "hooks") {
|
|
5236
|
+
const code = await runHooksCommand(argv.slice(1));
|
|
5237
|
+
if (code !== 0) process.exit(code);
|
|
5238
|
+
return;
|
|
5239
|
+
}
|
|
5048
5240
|
const argvParsed = parseArgs(argv);
|
|
5049
5241
|
if (argvParsed.positional.length === 0) {
|
|
5050
5242
|
const orchestratorCode2 = await tryOrchestrator(process.cwd(), argvParsed);
|
|
@@ -5059,19 +5251,19 @@ async function main() {
|
|
|
5059
5251
|
const target = positional[0];
|
|
5060
5252
|
if (!target) {
|
|
5061
5253
|
console.error("neat init: missing <path>");
|
|
5062
|
-
|
|
5254
|
+
usage2();
|
|
5063
5255
|
process.exit(2);
|
|
5064
5256
|
}
|
|
5065
5257
|
if (apply3 && dryRun) {
|
|
5066
5258
|
console.error("neat init: --apply and --dry-run are mutually exclusive");
|
|
5067
5259
|
process.exit(2);
|
|
5068
5260
|
}
|
|
5069
|
-
const scanPath =
|
|
5261
|
+
const scanPath = path10.resolve(target);
|
|
5070
5262
|
const projectExplicit = parsed.project !== null;
|
|
5071
|
-
const projectName = projectExplicit ? project :
|
|
5263
|
+
const projectName = projectExplicit ? project : path10.basename(scanPath);
|
|
5072
5264
|
const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
|
|
5073
|
-
const fallback = pathsForProject(projectKey,
|
|
5074
|
-
const outPath =
|
|
5265
|
+
const fallback = pathsForProject(projectKey, path10.join(scanPath, "neat-out")).snapshotPath;
|
|
5266
|
+
const outPath = path10.resolve(process.env.NEAT_OUT_PATH ?? fallback);
|
|
5075
5267
|
const result = await runInit({
|
|
5076
5268
|
scanPath,
|
|
5077
5269
|
outPath,
|
|
@@ -5089,24 +5281,24 @@ async function main() {
|
|
|
5089
5281
|
const target = positional[0];
|
|
5090
5282
|
if (!target) {
|
|
5091
5283
|
console.error("neat watch: missing <path>");
|
|
5092
|
-
|
|
5284
|
+
usage2();
|
|
5093
5285
|
process.exit(2);
|
|
5094
5286
|
}
|
|
5095
|
-
const scanPath =
|
|
5096
|
-
const stat = await
|
|
5287
|
+
const scanPath = path10.resolve(target);
|
|
5288
|
+
const stat = await fs8.stat(scanPath).catch(() => null);
|
|
5097
5289
|
if (!stat || !stat.isDirectory()) {
|
|
5098
5290
|
console.error(`neat watch: ${scanPath} is not a directory`);
|
|
5099
5291
|
process.exit(2);
|
|
5100
5292
|
}
|
|
5101
|
-
const projectPaths = pathsForProject(project,
|
|
5102
|
-
const outPath =
|
|
5103
|
-
const errorsPath =
|
|
5104
|
-
process.env.NEAT_ERRORS_PATH ??
|
|
5293
|
+
const projectPaths = pathsForProject(project, path10.join(scanPath, "neat-out"));
|
|
5294
|
+
const outPath = path10.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
|
|
5295
|
+
const errorsPath = path10.resolve(
|
|
5296
|
+
process.env.NEAT_ERRORS_PATH ?? path10.join(path10.dirname(outPath), path10.basename(projectPaths.errorsPath))
|
|
5105
5297
|
);
|
|
5106
|
-
const staleEventsPath =
|
|
5107
|
-
process.env.NEAT_STALE_EVENTS_PATH ??
|
|
5298
|
+
const staleEventsPath = path10.resolve(
|
|
5299
|
+
process.env.NEAT_STALE_EVENTS_PATH ?? path10.join(path10.dirname(outPath), path10.basename(projectPaths.staleEventsPath))
|
|
5108
5300
|
);
|
|
5109
|
-
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ?
|
|
5301
|
+
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? path10.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
|
|
5110
5302
|
const handle = await startWatch(getGraph(project), {
|
|
5111
5303
|
scanPath,
|
|
5112
5304
|
outPath,
|
|
@@ -5148,7 +5340,7 @@ async function main() {
|
|
|
5148
5340
|
const name = positional[0];
|
|
5149
5341
|
if (!name) {
|
|
5150
5342
|
console.error("neat pause: missing <name>");
|
|
5151
|
-
|
|
5343
|
+
usage2();
|
|
5152
5344
|
process.exit(2);
|
|
5153
5345
|
}
|
|
5154
5346
|
const daemon = await findDaemonByProject(name);
|
|
@@ -5173,7 +5365,7 @@ async function main() {
|
|
|
5173
5365
|
const name = positional[0];
|
|
5174
5366
|
if (!name) {
|
|
5175
5367
|
console.error("neat resume: missing <name>");
|
|
5176
|
-
|
|
5368
|
+
usage2();
|
|
5177
5369
|
process.exit(2);
|
|
5178
5370
|
}
|
|
5179
5371
|
const daemon = await findDaemonByProject(name);
|
|
@@ -5203,7 +5395,7 @@ async function main() {
|
|
|
5203
5395
|
const name = positional[0];
|
|
5204
5396
|
if (!name) {
|
|
5205
5397
|
console.error("neat uninstall: missing <name>");
|
|
5206
|
-
|
|
5398
|
+
usage2();
|
|
5207
5399
|
process.exit(2);
|
|
5208
5400
|
}
|
|
5209
5401
|
const daemon = await findDaemonByProject(name);
|
|
@@ -5285,15 +5477,15 @@ async function main() {
|
|
|
5285
5477
|
return;
|
|
5286
5478
|
}
|
|
5287
5479
|
console.error(`neat: unknown command "${cmd}"`);
|
|
5288
|
-
|
|
5480
|
+
usage2();
|
|
5289
5481
|
process.exit(1);
|
|
5290
5482
|
}
|
|
5291
5483
|
async function tryOrchestrator(cmd, parsed) {
|
|
5292
|
-
const scanPath =
|
|
5293
|
-
const stat = await
|
|
5484
|
+
const scanPath = path10.resolve(cmd);
|
|
5485
|
+
const stat = await fs8.stat(scanPath).catch(() => null);
|
|
5294
5486
|
if (!stat || !stat.isDirectory()) return null;
|
|
5295
5487
|
const projectExplicit = parsed.project !== null;
|
|
5296
|
-
const projectName = projectExplicit ? parsed.project :
|
|
5488
|
+
const projectName = projectExplicit ? parsed.project : path10.basename(scanPath);
|
|
5297
5489
|
const result = await runOrchestrator({
|
|
5298
5490
|
scanPath,
|
|
5299
5491
|
project: projectName,
|
|
@@ -5549,6 +5741,6 @@ export {
|
|
|
5549
5741
|
runInit,
|
|
5550
5742
|
runQueryVerb,
|
|
5551
5743
|
runSkill,
|
|
5552
|
-
usage
|
|
5744
|
+
usage2 as usage
|
|
5553
5745
|
};
|
|
5554
5746
|
//# sourceMappingURL=cli.js.map
|