@node9/proxy 1.35.1 → 1.35.2
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 +127 -214
- package/dist/cli.mjs +127 -214
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8154,7 +8154,7 @@ function binaryInPath(binary) {
|
|
|
8154
8154
|
return false;
|
|
8155
8155
|
}
|
|
8156
8156
|
function detectAgents(homeDir2 = import_os12.default.homedir()) {
|
|
8157
|
-
const
|
|
8157
|
+
const exists2 = (p) => {
|
|
8158
8158
|
try {
|
|
8159
8159
|
return import_fs13.default.existsSync(p);
|
|
8160
8160
|
} catch (err2) {
|
|
@@ -8168,7 +8168,7 @@ function detectAgents(homeDir2 = import_os12.default.homedir()) {
|
|
|
8168
8168
|
};
|
|
8169
8169
|
const desktopPath = claudeDesktopConfigPath(homeDir2);
|
|
8170
8170
|
return {
|
|
8171
|
-
claude:
|
|
8171
|
+
claude: exists2(import_path15.default.join(homeDir2, ".claude")) || exists2(import_path15.default.join(homeDir2, ".claude.json")),
|
|
8172
8172
|
// Antigravity (agy) shares the ~/.gemini root, so a bare
|
|
8173
8173
|
// `exists(~/.gemini)` would report the (EOL'd) Gemini CLI as
|
|
8174
8174
|
// installed on every agy machine — and `node9 init` would then
|
|
@@ -8178,35 +8178,35 @@ function detectAgents(homeDir2 = import_os12.default.homedir()) {
|
|
|
8178
8178
|
// creates ~/.gemini/settings.json on first run; agy does not touch
|
|
8179
8179
|
// it (it uses antigravity-cli/settings.json) — that file is the
|
|
8180
8180
|
// legacy-CLI discriminator.
|
|
8181
|
-
gemini:
|
|
8181
|
+
gemini: exists2(import_path15.default.join(homeDir2, ".gemini", "settings.json")) || binaryInPath("gemini"),
|
|
8182
8182
|
// agy creates ~/.gemini/antigravity-cli/ on first launch; the IDE
|
|
8183
8183
|
// creates antigravity-ide/. PATH fallback covers installed-but-
|
|
8184
8184
|
// never-launched (same class as opencode #186).
|
|
8185
|
-
antigravity:
|
|
8185
|
+
antigravity: exists2(import_path15.default.join(homeDir2, ".gemini", "antigravity-cli")) || exists2(import_path15.default.join(homeDir2, ".gemini", "antigravity-ide")) || binaryInPath("agy"),
|
|
8186
8186
|
// GitHub Copilot CLI creates ~/.copilot on first launch; PATH
|
|
8187
8187
|
// fallback covers installed-but-never-launched (same as opencode #186).
|
|
8188
|
-
copilot:
|
|
8189
|
-
cursor:
|
|
8190
|
-
codex:
|
|
8191
|
-
windsurf:
|
|
8192
|
-
vscode:
|
|
8193
|
-
claudeDesktop: desktopPath !== null &&
|
|
8188
|
+
copilot: exists2(import_path15.default.join(homeDir2, ".copilot")) || binaryInPath("copilot"),
|
|
8189
|
+
cursor: exists2(import_path15.default.join(homeDir2, ".cursor")),
|
|
8190
|
+
codex: exists2(import_path15.default.join(homeDir2, ".codex")),
|
|
8191
|
+
windsurf: exists2(import_path15.default.join(homeDir2, ".codeium", "windsurf")),
|
|
8192
|
+
vscode: exists2(import_path15.default.join(homeDir2, ".vscode")),
|
|
8193
|
+
claudeDesktop: desktopPath !== null && exists2(import_path15.default.dirname(desktopPath)),
|
|
8194
8194
|
// Opencode creates ~/.config/opencode lazily on first launch — fall back
|
|
8195
8195
|
// to a PATH lookup so installed-but-never-launched CLIs are still wired.
|
|
8196
|
-
opencode:
|
|
8196
|
+
opencode: exists2(import_path15.default.join(homeDir2, ".config", "opencode")) || binaryInPath("opencode"),
|
|
8197
8197
|
// Pi (https://pi.dev): config dir is ~/.pi/agent (CONFIG_DIR_NAME=".pi"
|
|
8198
8198
|
// + agentDir, verified against opensources/pi-main/packages/coding-agent/
|
|
8199
8199
|
// src/config.ts:449,475). The Bun-compiled binary path may create this
|
|
8200
8200
|
// dir lazily on first launch — same class of bug as opencode's #186
|
|
8201
8201
|
// (design R6) — so fall back to PATH lookup for installed-but-never-
|
|
8202
8202
|
// launched pi.
|
|
8203
|
-
pi:
|
|
8203
|
+
pi: exists2(import_path15.default.join(homeDir2, ".pi", "agent")) || binaryInPath("pi"),
|
|
8204
8204
|
// Hermes Agent (https://github.com/NousResearch/hermes-agent): home dir
|
|
8205
8205
|
// is $HERMES_HOME (default ~/.hermes) per hermes_constants.py:30. config.yaml
|
|
8206
8206
|
// appears after `hermes setup` has run; the directory alone exists from
|
|
8207
8207
|
// install time. PATH fallback covers the rare case where the user blew
|
|
8208
8208
|
// away ~/.hermes but kept the binary.
|
|
8209
|
-
hermes:
|
|
8209
|
+
hermes: exists2(hermesHomeDir(homeDir2)) || binaryInPath("hermes")
|
|
8210
8210
|
};
|
|
8211
8211
|
}
|
|
8212
8212
|
async function setupCursor() {
|
|
@@ -19983,118 +19983,151 @@ function matchersHaveNode9Hook(matchers) {
|
|
|
19983
19983
|
function flatHaveNode9Hook(entries) {
|
|
19984
19984
|
return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
|
|
19985
19985
|
}
|
|
19986
|
-
function
|
|
19987
|
-
|
|
19988
|
-
if (parsed === null) return "absent";
|
|
19989
|
-
if (parsed === "invalid") return "invalid";
|
|
19990
|
-
return read(parsed) ? "wired" : "unwired";
|
|
19991
|
-
}
|
|
19992
|
-
function hermesWire(home) {
|
|
19993
|
-
const configPath = hermesConfigPath(home);
|
|
19994
|
-
if (!import_fs38.default.existsSync(configPath)) return "absent";
|
|
19986
|
+
function readHookRoot(filePath, format) {
|
|
19987
|
+
if (!import_fs38.default.existsSync(filePath)) return "absent";
|
|
19995
19988
|
let raw;
|
|
19996
19989
|
try {
|
|
19997
|
-
raw = import_fs38.default.readFileSync(
|
|
19990
|
+
raw = import_fs38.default.readFileSync(filePath, "utf-8");
|
|
19998
19991
|
} catch {
|
|
19999
19992
|
return "absent";
|
|
20000
19993
|
}
|
|
20001
19994
|
try {
|
|
20002
|
-
const
|
|
20003
|
-
|
|
20004
|
-
(e) => typeof e?.command === "string" && isNode9Hook(e.command)
|
|
20005
|
-
);
|
|
20006
|
-
return pre ? "wired" : "unwired";
|
|
19995
|
+
const parsed = format === "yaml" ? yaml2.parse(raw) : JSON.parse(raw);
|
|
19996
|
+
return parsed?.hooks ?? {};
|
|
20007
19997
|
} catch {
|
|
20008
19998
|
return "invalid";
|
|
20009
19999
|
}
|
|
20010
20000
|
}
|
|
20001
|
+
function eventWired(root, ev, format) {
|
|
20002
|
+
const arr = root[ev.key];
|
|
20003
|
+
if (format === "matcher") return matchersHaveNode9Hook(arr);
|
|
20004
|
+
return flatHaveNode9Hook(arr);
|
|
20005
|
+
}
|
|
20006
|
+
function readMcp(filePath) {
|
|
20007
|
+
const parsed = readJson2(filePath);
|
|
20008
|
+
if (parsed === null || parsed === "invalid") return { wrapped: [], present: false };
|
|
20009
|
+
const servers = parsed.mcpServers ?? {};
|
|
20010
|
+
const entries = Object.entries(servers);
|
|
20011
|
+
const present = entries.some(([, s]) => s?.command === "node9");
|
|
20012
|
+
const wrapped = entries.filter(([, s]) => s?.command === "node9" && Array.isArray(s.args) && s.args.length > 0).map(([name, s]) => `${name} \u2192 ${s.args.join(" ")}`);
|
|
20013
|
+
return { wrapped, present };
|
|
20014
|
+
}
|
|
20015
|
+
var exists = (p) => {
|
|
20016
|
+
try {
|
|
20017
|
+
return import_fs38.default.existsSync(p);
|
|
20018
|
+
} catch {
|
|
20019
|
+
return false;
|
|
20020
|
+
}
|
|
20021
|
+
};
|
|
20022
|
+
var ck = (key) => ({ key, kind: "check" });
|
|
20023
|
+
var lg = (key) => ({ key, kind: "log" });
|
|
20024
|
+
var DEFAULT_LABEL_PAD = 11;
|
|
20025
|
+
var hookLabelOf = (ev, pad) => `${ev.key.padEnd(pad)} (node9 ${ev.kind})`;
|
|
20011
20026
|
var AGENT_SPECS = [
|
|
20012
20027
|
{
|
|
20013
20028
|
id: "claude",
|
|
20014
20029
|
label: "Claude Code",
|
|
20015
|
-
hookLabel: "PreToolUse hook",
|
|
20016
20030
|
setupCommand: "node9 setup claude",
|
|
20017
|
-
|
|
20018
|
-
|
|
20019
|
-
|
|
20020
|
-
|
|
20021
|
-
)
|
|
20031
|
+
hookFile: (h) => import_path39.default.join(h, ".claude", "settings.json"),
|
|
20032
|
+
hookFormat: "matcher",
|
|
20033
|
+
hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
|
|
20034
|
+
mcpFile: (h) => import_path39.default.join(h, ".claude.json"),
|
|
20035
|
+
present: (h) => exists(import_path39.default.join(h, ".claude", "settings.json")) || exists(import_path39.default.join(h, ".claude.json"))
|
|
20022
20036
|
},
|
|
20023
20037
|
{
|
|
20024
20038
|
id: "gemini",
|
|
20025
20039
|
label: "Gemini CLI",
|
|
20026
|
-
hookLabel: "BeforeTool hook",
|
|
20027
20040
|
setupCommand: "node9 setup gemini",
|
|
20028
|
-
|
|
20029
|
-
|
|
20030
|
-
|
|
20031
|
-
|
|
20032
|
-
)
|
|
20041
|
+
hookFile: (h) => import_path39.default.join(h, ".gemini", "settings.json"),
|
|
20042
|
+
hookFormat: "matcher",
|
|
20043
|
+
hookEvents: [ck("BeforeTool"), lg("AfterTool")],
|
|
20044
|
+
mcpFile: (h) => import_path39.default.join(h, ".gemini", "settings.json"),
|
|
20045
|
+
present: (h) => exists(import_path39.default.join(h, ".gemini", "settings.json"))
|
|
20033
20046
|
},
|
|
20034
20047
|
{
|
|
20035
20048
|
id: "codex",
|
|
20036
20049
|
label: "Codex",
|
|
20037
|
-
hookLabel: "PreToolUse hook",
|
|
20038
20050
|
setupCommand: "node9 setup codex",
|
|
20039
|
-
|
|
20040
|
-
|
|
20041
|
-
|
|
20042
|
-
|
|
20043
|
-
)
|
|
20051
|
+
hookFile: (h) => import_path39.default.join(h, ".codex", "hooks.json"),
|
|
20052
|
+
hookFormat: "matcher",
|
|
20053
|
+
hookEvents: [ck("PreToolUse"), ck("UserPromptSubmit")],
|
|
20054
|
+
present: (h) => exists(import_path39.default.join(h, ".codex"))
|
|
20044
20055
|
},
|
|
20045
20056
|
{
|
|
20046
20057
|
id: "antigravity",
|
|
20047
20058
|
label: "Antigravity",
|
|
20048
|
-
hookLabel: "PreToolUse hook",
|
|
20049
20059
|
setupCommand: "node9 setup antigravity",
|
|
20050
|
-
|
|
20051
|
-
|
|
20052
|
-
|
|
20053
|
-
|
|
20054
|
-
)
|
|
20060
|
+
hookFile: (h) => import_path39.default.join(h, ".gemini", "config", "hooks.json"),
|
|
20061
|
+
hookFormat: "matcher",
|
|
20062
|
+
hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
|
|
20063
|
+
mcpFile: (h) => import_path39.default.join(h, ".gemini", "config", "mcp_config.json"),
|
|
20064
|
+
present: (h) => exists(import_path39.default.join(h, ".gemini", "config", "hooks.json")) || exists(import_path39.default.join(h, ".gemini", "antigravity-cli")) || exists(import_path39.default.join(h, ".gemini", "antigravity-ide"))
|
|
20055
20065
|
},
|
|
20056
20066
|
{
|
|
20057
20067
|
id: "copilot",
|
|
20058
20068
|
label: "GitHub Copilot",
|
|
20059
|
-
hookLabel: "PreToolUse hook",
|
|
20060
20069
|
setupCommand: "node9 setup copilot",
|
|
20061
|
-
|
|
20062
|
-
|
|
20063
|
-
|
|
20064
|
-
|
|
20065
|
-
)
|
|
20070
|
+
hookFile: (h) => import_path39.default.join(h, ".copilot", "hooks", "node9.json"),
|
|
20071
|
+
hookFormat: "flat",
|
|
20072
|
+
hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
|
|
20073
|
+
mcpFile: (h) => import_path39.default.join(h, ".copilot", "mcp-config.json"),
|
|
20074
|
+
present: (h) => exists(import_path39.default.join(h, ".copilot"))
|
|
20066
20075
|
},
|
|
20067
20076
|
{
|
|
20068
20077
|
id: "cursor",
|
|
20069
20078
|
label: "Cursor",
|
|
20070
|
-
hookLabel: "preToolUse hook",
|
|
20071
20079
|
setupCommand: "node9 setup cursor",
|
|
20072
|
-
|
|
20073
|
-
|
|
20074
|
-
|
|
20075
|
-
|
|
20076
|
-
)
|
|
20080
|
+
// MCP-only — no hook file (see note above).
|
|
20081
|
+
hookFormat: "flat",
|
|
20082
|
+
hookEvents: [],
|
|
20083
|
+
mcpFile: (h) => import_path39.default.join(h, ".cursor", "mcp.json"),
|
|
20084
|
+
present: (h) => exists(import_path39.default.join(h, ".cursor", "mcp.json"))
|
|
20077
20085
|
},
|
|
20078
20086
|
{
|
|
20079
20087
|
id: "hermes",
|
|
20080
20088
|
label: "Hermes Agent",
|
|
20081
|
-
hookLabel: "pre_tool_call hook",
|
|
20082
20089
|
setupCommand: "node9 setup hermes",
|
|
20083
|
-
|
|
20084
|
-
|
|
20090
|
+
hookFile: (h) => hermesConfigPath(h),
|
|
20091
|
+
hookFormat: "yaml",
|
|
20092
|
+
hookEvents: [ck("pre_tool_call"), lg("post_tool_call")],
|
|
20093
|
+
labelPad: 14,
|
|
20094
|
+
// 'post_tool_call' is wider than the default
|
|
20095
|
+
present: (h) => exists(hermesConfigPath(h))
|
|
20085
20096
|
}
|
|
20086
20097
|
];
|
|
20087
20098
|
function getAgentWiring(home = import_os34.default.homedir()) {
|
|
20088
20099
|
const detected = detectAgents(home);
|
|
20089
|
-
return AGENT_SPECS.map((spec) =>
|
|
20090
|
-
|
|
20091
|
-
|
|
20092
|
-
|
|
20093
|
-
|
|
20094
|
-
|
|
20095
|
-
|
|
20096
|
-
|
|
20097
|
-
|
|
20100
|
+
return AGENT_SPECS.map((spec) => {
|
|
20101
|
+
const root = spec.hookFile ? readHookRoot(spec.hookFile(home), spec.hookFormat) : "absent";
|
|
20102
|
+
const primary = spec.hookEvents[0];
|
|
20103
|
+
const rootPresent = root !== "absent" && root !== "invalid";
|
|
20104
|
+
const pad = spec.labelPad ?? DEFAULT_LABEL_PAD;
|
|
20105
|
+
const hooks = spec.hookEvents.map((ev) => ({
|
|
20106
|
+
label: hookLabelOf(ev, pad),
|
|
20107
|
+
wired: rootPresent && eventWired(root, ev, spec.hookFormat)
|
|
20108
|
+
}));
|
|
20109
|
+
let wireState;
|
|
20110
|
+
if (root === "absent") wireState = "absent";
|
|
20111
|
+
else if (root === "invalid") wireState = "invalid";
|
|
20112
|
+
else wireState = primary && eventWired(root, primary, spec.hookFormat) ? "wired" : "unwired";
|
|
20113
|
+
const mcp = spec.mcpFile ? readMcp(spec.mcpFile(home)) : null;
|
|
20114
|
+
const anyHookWired = hooks.some((h) => h.wired);
|
|
20115
|
+
return {
|
|
20116
|
+
id: spec.id,
|
|
20117
|
+
label: spec.label,
|
|
20118
|
+
setupCommand: spec.setupCommand,
|
|
20119
|
+
installed: detected[spec.id],
|
|
20120
|
+
present: spec.present(home),
|
|
20121
|
+
hooks,
|
|
20122
|
+
wireState,
|
|
20123
|
+
hookLabel: primary ? `${primary.key} hook` : "MCP proxy",
|
|
20124
|
+
settingsPath: spec.hookFile ? spec.hookFile(home) : spec.mcpFile ? spec.mcpFile(home) : "",
|
|
20125
|
+
configFormat: spec.hookFormat === "yaml" ? "YAML" : "JSON",
|
|
20126
|
+
mcpServers: mcp ? mcp.wrapped : null,
|
|
20127
|
+
mcpProtected: mcp ? mcp.present : false,
|
|
20128
|
+
isProtected: anyHookWired || (mcp?.present ?? false)
|
|
20129
|
+
};
|
|
20130
|
+
});
|
|
20098
20131
|
}
|
|
20099
20132
|
|
|
20100
20133
|
// src/cli/commands/doctor.ts
|
|
@@ -20184,12 +20217,13 @@ function registerDoctorCommand(program2, version2) {
|
|
|
20184
20217
|
section("Agent Hooks");
|
|
20185
20218
|
const notConfigured = [];
|
|
20186
20219
|
for (const a of getAgentWiring(homeDir2)) {
|
|
20187
|
-
|
|
20188
|
-
|
|
20189
|
-
|
|
20190
|
-
fail(`${a.label} \u2014 settings found but node9 hook missing`, `Run: ${a.setupCommand}`);
|
|
20220
|
+
const anyHookWired = a.hooks.some((h) => h.wired);
|
|
20221
|
+
if (a.isProtected) {
|
|
20222
|
+
pass(`${a.label} \u2014 ${anyHookWired ? `${a.hookLabel} active` : "MCP proxy active"}`);
|
|
20191
20223
|
} else if (a.wireState === "invalid") {
|
|
20192
20224
|
fail(`${a.label} \u2014 settings file is invalid JSON`, a.settingsPath);
|
|
20225
|
+
} else if (a.wireState === "unwired") {
|
|
20226
|
+
fail(`${a.label} \u2014 settings found but node9 hook missing`, `Run: ${a.setupCommand}`);
|
|
20193
20227
|
} else {
|
|
20194
20228
|
notConfigured.push(a.label);
|
|
20195
20229
|
}
|
|
@@ -21493,50 +21527,8 @@ var import_chalk15 = __toESM(require("chalk"));
|
|
|
21493
21527
|
var import_fs42 = __toESM(require("fs"));
|
|
21494
21528
|
var import_path43 = __toESM(require("path"));
|
|
21495
21529
|
var import_os38 = __toESM(require("os"));
|
|
21496
|
-
var yaml3 = __toESM(require("yaml"));
|
|
21497
21530
|
init_core();
|
|
21498
21531
|
init_daemon();
|
|
21499
|
-
init_setup();
|
|
21500
|
-
function readHermesHooks(configPath) {
|
|
21501
|
-
if (!import_fs42.default.existsSync(configPath)) return null;
|
|
21502
|
-
let raw;
|
|
21503
|
-
try {
|
|
21504
|
-
raw = import_fs42.default.readFileSync(configPath, "utf-8");
|
|
21505
|
-
} catch {
|
|
21506
|
-
return null;
|
|
21507
|
-
}
|
|
21508
|
-
try {
|
|
21509
|
-
const cfg = yaml3.parse(raw);
|
|
21510
|
-
const has = (event) => (cfg?.hooks?.[event] ?? []).some(
|
|
21511
|
-
(e) => typeof e?.command === "string" && isNode9Hook(e.command)
|
|
21512
|
-
);
|
|
21513
|
-
return { pre: has("pre_tool_call"), post: has("post_tool_call") };
|
|
21514
|
-
} catch {
|
|
21515
|
-
console.error(
|
|
21516
|
-
import_chalk15.default.yellow(
|
|
21517
|
-
` \u26A0\uFE0F Hermes config.yaml at ${configPath} is not valid YAML \u2014 showing as unwired.`
|
|
21518
|
-
)
|
|
21519
|
-
);
|
|
21520
|
-
return { pre: false, post: false };
|
|
21521
|
-
}
|
|
21522
|
-
}
|
|
21523
|
-
function readJson3(filePath) {
|
|
21524
|
-
try {
|
|
21525
|
-
if (import_fs42.default.existsSync(filePath)) return JSON.parse(import_fs42.default.readFileSync(filePath, "utf-8"));
|
|
21526
|
-
} catch {
|
|
21527
|
-
}
|
|
21528
|
-
return null;
|
|
21529
|
-
}
|
|
21530
|
-
function matchersHaveNode9Hook2(matchers) {
|
|
21531
|
-
return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
|
|
21532
|
-
}
|
|
21533
|
-
function flatHaveNode9Hook2(entries) {
|
|
21534
|
-
return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
|
|
21535
|
-
}
|
|
21536
|
-
function wrappedMcpServers(servers) {
|
|
21537
|
-
if (!servers) return [];
|
|
21538
|
-
return Object.entries(servers).filter(([, s]) => s.command === "node9" && Array.isArray(s.args) && s.args.length > 0).map(([name, s]) => `${name} \u2192 ${s.args.join(" ")}`);
|
|
21539
|
-
}
|
|
21540
21532
|
function printAgentSection(label, hookPairs, wrapped) {
|
|
21541
21533
|
console.log(import_chalk15.default.bold(` ${label}`));
|
|
21542
21534
|
for (const { name, present } of hookPairs) {
|
|
@@ -21603,102 +21595,23 @@ function registerStatusCommand(program2) {
|
|
|
21603
21595
|
` Sandbox: ${import_chalk15.default.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
|
|
21604
21596
|
);
|
|
21605
21597
|
}
|
|
21606
|
-
const
|
|
21607
|
-
|
|
21608
|
-
import_path43.default.join(homeDir2, ".claude", "settings.json")
|
|
21609
|
-
);
|
|
21610
|
-
const claudeConfig = readJson3(import_path43.default.join(homeDir2, ".claude.json"));
|
|
21611
|
-
const geminiSettings = readJson3(
|
|
21612
|
-
import_path43.default.join(homeDir2, ".gemini", "settings.json")
|
|
21613
|
-
);
|
|
21614
|
-
const cursorConfig = readJson3(import_path43.default.join(homeDir2, ".cursor", "mcp.json"));
|
|
21615
|
-
const antigravityHooks = readJson3(
|
|
21616
|
-
import_path43.default.join(homeDir2, ".gemini", "config", "hooks.json")
|
|
21617
|
-
);
|
|
21618
|
-
const antigravityMcp = readJson3(
|
|
21619
|
-
import_path43.default.join(homeDir2, ".gemini", "config", "mcp_config.json")
|
|
21620
|
-
);
|
|
21621
|
-
const antigravityPresent = antigravityHooks !== null || import_fs42.default.existsSync(import_path43.default.join(homeDir2, ".gemini", "antigravity-cli")) || import_fs42.default.existsSync(import_path43.default.join(homeDir2, ".gemini", "antigravity-ide"));
|
|
21622
|
-
const copilotHooks = readJson3(
|
|
21623
|
-
import_path43.default.join(homeDir2, ".copilot", "hooks", "node9.json")
|
|
21624
|
-
);
|
|
21625
|
-
const copilotMcp = readJson3(
|
|
21626
|
-
import_path43.default.join(homeDir2, ".copilot", "mcp-config.json")
|
|
21627
|
-
);
|
|
21628
|
-
const copilotPresent = import_fs42.default.existsSync(import_path43.default.join(homeDir2, ".copilot"));
|
|
21629
|
-
const hermesHooks = readHermesHooks(hermesConfigPath(homeDir2));
|
|
21630
|
-
const agentFound = claudeSettings || claudeConfig || geminiSettings || cursorConfig || antigravityPresent || copilotPresent || hermesHooks;
|
|
21631
|
-
if (agentFound) {
|
|
21598
|
+
const wiring = getAgentWiring(import_os38.default.homedir()).filter((a) => a.present);
|
|
21599
|
+
if (wiring.length > 0) {
|
|
21632
21600
|
console.log("");
|
|
21633
21601
|
console.log(import_chalk15.default.bold(" Agent Wiring:"));
|
|
21634
21602
|
console.log("");
|
|
21635
|
-
|
|
21636
|
-
|
|
21637
|
-
|
|
21638
|
-
|
|
21639
|
-
|
|
21640
|
-
|
|
21641
|
-
|
|
21642
|
-
|
|
21643
|
-
],
|
|
21644
|
-
wrappedMcpServers(claudeConfig?.mcpServers)
|
|
21645
|
-
);
|
|
21646
|
-
console.log("");
|
|
21647
|
-
}
|
|
21648
|
-
if (geminiSettings) {
|
|
21649
|
-
const beforeHook = matchersHaveNode9Hook2(geminiSettings.hooks?.BeforeTool);
|
|
21650
|
-
const afterHook = matchersHaveNode9Hook2(geminiSettings.hooks?.AfterTool);
|
|
21651
|
-
printAgentSection(
|
|
21652
|
-
"Gemini CLI",
|
|
21653
|
-
[
|
|
21654
|
-
{ name: "BeforeTool (node9 check)", present: beforeHook },
|
|
21655
|
-
{ name: "AfterTool (node9 log)", present: afterHook }
|
|
21656
|
-
],
|
|
21657
|
-
wrappedMcpServers(geminiSettings.mcpServers)
|
|
21658
|
-
);
|
|
21659
|
-
console.log("");
|
|
21660
|
-
}
|
|
21661
|
-
if (antigravityPresent) {
|
|
21662
|
-
const preHook = matchersHaveNode9Hook2(antigravityHooks?.hooks?.PreToolUse);
|
|
21663
|
-
const postHook = matchersHaveNode9Hook2(antigravityHooks?.hooks?.PostToolUse);
|
|
21664
|
-
printAgentSection(
|
|
21665
|
-
"Antigravity",
|
|
21666
|
-
[
|
|
21667
|
-
{ name: "PreToolUse (node9 check)", present: preHook },
|
|
21668
|
-
{ name: "PostToolUse (node9 log)", present: postHook }
|
|
21669
|
-
],
|
|
21670
|
-
wrappedMcpServers(antigravityMcp?.mcpServers)
|
|
21671
|
-
);
|
|
21672
|
-
console.log("");
|
|
21673
|
-
}
|
|
21674
|
-
if (copilotPresent) {
|
|
21675
|
-
const preHook = flatHaveNode9Hook2(copilotHooks?.hooks?.PreToolUse);
|
|
21676
|
-
const postHook = flatHaveNode9Hook2(copilotHooks?.hooks?.PostToolUse);
|
|
21677
|
-
const promptHook = flatHaveNode9Hook2(copilotHooks?.hooks?.UserPromptSubmit);
|
|
21678
|
-
printAgentSection(
|
|
21679
|
-
"GitHub Copilot",
|
|
21680
|
-
[
|
|
21681
|
-
{ name: "PreToolUse (node9 check)", present: preHook },
|
|
21682
|
-
{ name: "PostToolUse (node9 log)", present: postHook },
|
|
21683
|
-
{ name: "UserPromptSubmit (node9 check)", present: promptHook }
|
|
21684
|
-
],
|
|
21685
|
-
wrappedMcpServers(copilotMcp?.mcpServers)
|
|
21686
|
-
);
|
|
21687
|
-
console.log("");
|
|
21688
|
-
}
|
|
21689
|
-
if (cursorConfig) {
|
|
21690
|
-
printAgentSection("Cursor", [], wrappedMcpServers(cursorConfig.mcpServers));
|
|
21691
|
-
console.log("");
|
|
21692
|
-
}
|
|
21693
|
-
if (hermesHooks) {
|
|
21603
|
+
for (const a of wiring) {
|
|
21604
|
+
if (a.wireState === "invalid") {
|
|
21605
|
+
console.error(
|
|
21606
|
+
import_chalk15.default.yellow(
|
|
21607
|
+
` \u26A0\uFE0F ${a.label} config at ${a.settingsPath} is not valid ${a.configFormat} \u2014 showing as unwired.`
|
|
21608
|
+
)
|
|
21609
|
+
);
|
|
21610
|
+
}
|
|
21694
21611
|
printAgentSection(
|
|
21695
|
-
|
|
21696
|
-
|
|
21697
|
-
|
|
21698
|
-
{ name: "post_tool_call (node9 log)", present: hermesHooks.post }
|
|
21699
|
-
],
|
|
21700
|
-
null
|
|
21701
|
-
// Hermes has no MCP surface
|
|
21612
|
+
a.label,
|
|
21613
|
+
a.hooks.map((h) => ({ name: h.label, present: h.wired })),
|
|
21614
|
+
a.mcpServers
|
|
21702
21615
|
);
|
|
21703
21616
|
console.log("");
|
|
21704
21617
|
}
|
package/dist/cli.mjs
CHANGED
|
@@ -8136,7 +8136,7 @@ function binaryInPath(binary) {
|
|
|
8136
8136
|
return false;
|
|
8137
8137
|
}
|
|
8138
8138
|
function detectAgents(homeDir2 = os12.homedir()) {
|
|
8139
|
-
const
|
|
8139
|
+
const exists2 = (p) => {
|
|
8140
8140
|
try {
|
|
8141
8141
|
return fs13.existsSync(p);
|
|
8142
8142
|
} catch (err2) {
|
|
@@ -8150,7 +8150,7 @@ function detectAgents(homeDir2 = os12.homedir()) {
|
|
|
8150
8150
|
};
|
|
8151
8151
|
const desktopPath = claudeDesktopConfigPath(homeDir2);
|
|
8152
8152
|
return {
|
|
8153
|
-
claude:
|
|
8153
|
+
claude: exists2(path15.join(homeDir2, ".claude")) || exists2(path15.join(homeDir2, ".claude.json")),
|
|
8154
8154
|
// Antigravity (agy) shares the ~/.gemini root, so a bare
|
|
8155
8155
|
// `exists(~/.gemini)` would report the (EOL'd) Gemini CLI as
|
|
8156
8156
|
// installed on every agy machine — and `node9 init` would then
|
|
@@ -8160,35 +8160,35 @@ function detectAgents(homeDir2 = os12.homedir()) {
|
|
|
8160
8160
|
// creates ~/.gemini/settings.json on first run; agy does not touch
|
|
8161
8161
|
// it (it uses antigravity-cli/settings.json) — that file is the
|
|
8162
8162
|
// legacy-CLI discriminator.
|
|
8163
|
-
gemini:
|
|
8163
|
+
gemini: exists2(path15.join(homeDir2, ".gemini", "settings.json")) || binaryInPath("gemini"),
|
|
8164
8164
|
// agy creates ~/.gemini/antigravity-cli/ on first launch; the IDE
|
|
8165
8165
|
// creates antigravity-ide/. PATH fallback covers installed-but-
|
|
8166
8166
|
// never-launched (same class as opencode #186).
|
|
8167
|
-
antigravity:
|
|
8167
|
+
antigravity: exists2(path15.join(homeDir2, ".gemini", "antigravity-cli")) || exists2(path15.join(homeDir2, ".gemini", "antigravity-ide")) || binaryInPath("agy"),
|
|
8168
8168
|
// GitHub Copilot CLI creates ~/.copilot on first launch; PATH
|
|
8169
8169
|
// fallback covers installed-but-never-launched (same as opencode #186).
|
|
8170
|
-
copilot:
|
|
8171
|
-
cursor:
|
|
8172
|
-
codex:
|
|
8173
|
-
windsurf:
|
|
8174
|
-
vscode:
|
|
8175
|
-
claudeDesktop: desktopPath !== null &&
|
|
8170
|
+
copilot: exists2(path15.join(homeDir2, ".copilot")) || binaryInPath("copilot"),
|
|
8171
|
+
cursor: exists2(path15.join(homeDir2, ".cursor")),
|
|
8172
|
+
codex: exists2(path15.join(homeDir2, ".codex")),
|
|
8173
|
+
windsurf: exists2(path15.join(homeDir2, ".codeium", "windsurf")),
|
|
8174
|
+
vscode: exists2(path15.join(homeDir2, ".vscode")),
|
|
8175
|
+
claudeDesktop: desktopPath !== null && exists2(path15.dirname(desktopPath)),
|
|
8176
8176
|
// Opencode creates ~/.config/opencode lazily on first launch — fall back
|
|
8177
8177
|
// to a PATH lookup so installed-but-never-launched CLIs are still wired.
|
|
8178
|
-
opencode:
|
|
8178
|
+
opencode: exists2(path15.join(homeDir2, ".config", "opencode")) || binaryInPath("opencode"),
|
|
8179
8179
|
// Pi (https://pi.dev): config dir is ~/.pi/agent (CONFIG_DIR_NAME=".pi"
|
|
8180
8180
|
// + agentDir, verified against opensources/pi-main/packages/coding-agent/
|
|
8181
8181
|
// src/config.ts:449,475). The Bun-compiled binary path may create this
|
|
8182
8182
|
// dir lazily on first launch — same class of bug as opencode's #186
|
|
8183
8183
|
// (design R6) — so fall back to PATH lookup for installed-but-never-
|
|
8184
8184
|
// launched pi.
|
|
8185
|
-
pi:
|
|
8185
|
+
pi: exists2(path15.join(homeDir2, ".pi", "agent")) || binaryInPath("pi"),
|
|
8186
8186
|
// Hermes Agent (https://github.com/NousResearch/hermes-agent): home dir
|
|
8187
8187
|
// is $HERMES_HOME (default ~/.hermes) per hermes_constants.py:30. config.yaml
|
|
8188
8188
|
// appears after `hermes setup` has run; the directory alone exists from
|
|
8189
8189
|
// install time. PATH fallback covers the rare case where the user blew
|
|
8190
8190
|
// away ~/.hermes but kept the binary.
|
|
8191
|
-
hermes:
|
|
8191
|
+
hermes: exists2(hermesHomeDir(homeDir2)) || binaryInPath("hermes")
|
|
8192
8192
|
};
|
|
8193
8193
|
}
|
|
8194
8194
|
async function setupCursor() {
|
|
@@ -19955,118 +19955,151 @@ function matchersHaveNode9Hook(matchers) {
|
|
|
19955
19955
|
function flatHaveNode9Hook(entries) {
|
|
19956
19956
|
return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
|
|
19957
19957
|
}
|
|
19958
|
-
function
|
|
19959
|
-
|
|
19960
|
-
if (parsed === null) return "absent";
|
|
19961
|
-
if (parsed === "invalid") return "invalid";
|
|
19962
|
-
return read(parsed) ? "wired" : "unwired";
|
|
19963
|
-
}
|
|
19964
|
-
function hermesWire(home) {
|
|
19965
|
-
const configPath = hermesConfigPath(home);
|
|
19966
|
-
if (!fs38.existsSync(configPath)) return "absent";
|
|
19958
|
+
function readHookRoot(filePath, format) {
|
|
19959
|
+
if (!fs38.existsSync(filePath)) return "absent";
|
|
19967
19960
|
let raw;
|
|
19968
19961
|
try {
|
|
19969
|
-
raw = fs38.readFileSync(
|
|
19962
|
+
raw = fs38.readFileSync(filePath, "utf-8");
|
|
19970
19963
|
} catch {
|
|
19971
19964
|
return "absent";
|
|
19972
19965
|
}
|
|
19973
19966
|
try {
|
|
19974
|
-
const
|
|
19975
|
-
|
|
19976
|
-
(e) => typeof e?.command === "string" && isNode9Hook(e.command)
|
|
19977
|
-
);
|
|
19978
|
-
return pre ? "wired" : "unwired";
|
|
19967
|
+
const parsed = format === "yaml" ? yaml2.parse(raw) : JSON.parse(raw);
|
|
19968
|
+
return parsed?.hooks ?? {};
|
|
19979
19969
|
} catch {
|
|
19980
19970
|
return "invalid";
|
|
19981
19971
|
}
|
|
19982
19972
|
}
|
|
19973
|
+
function eventWired(root, ev, format) {
|
|
19974
|
+
const arr = root[ev.key];
|
|
19975
|
+
if (format === "matcher") return matchersHaveNode9Hook(arr);
|
|
19976
|
+
return flatHaveNode9Hook(arr);
|
|
19977
|
+
}
|
|
19978
|
+
function readMcp(filePath) {
|
|
19979
|
+
const parsed = readJson2(filePath);
|
|
19980
|
+
if (parsed === null || parsed === "invalid") return { wrapped: [], present: false };
|
|
19981
|
+
const servers = parsed.mcpServers ?? {};
|
|
19982
|
+
const entries = Object.entries(servers);
|
|
19983
|
+
const present = entries.some(([, s]) => s?.command === "node9");
|
|
19984
|
+
const wrapped = entries.filter(([, s]) => s?.command === "node9" && Array.isArray(s.args) && s.args.length > 0).map(([name, s]) => `${name} \u2192 ${s.args.join(" ")}`);
|
|
19985
|
+
return { wrapped, present };
|
|
19986
|
+
}
|
|
19987
|
+
var exists = (p) => {
|
|
19988
|
+
try {
|
|
19989
|
+
return fs38.existsSync(p);
|
|
19990
|
+
} catch {
|
|
19991
|
+
return false;
|
|
19992
|
+
}
|
|
19993
|
+
};
|
|
19994
|
+
var ck = (key) => ({ key, kind: "check" });
|
|
19995
|
+
var lg = (key) => ({ key, kind: "log" });
|
|
19996
|
+
var DEFAULT_LABEL_PAD = 11;
|
|
19997
|
+
var hookLabelOf = (ev, pad) => `${ev.key.padEnd(pad)} (node9 ${ev.kind})`;
|
|
19983
19998
|
var AGENT_SPECS = [
|
|
19984
19999
|
{
|
|
19985
20000
|
id: "claude",
|
|
19986
20001
|
label: "Claude Code",
|
|
19987
|
-
hookLabel: "PreToolUse hook",
|
|
19988
20002
|
setupCommand: "node9 setup claude",
|
|
19989
|
-
|
|
19990
|
-
|
|
19991
|
-
|
|
19992
|
-
|
|
19993
|
-
)
|
|
20003
|
+
hookFile: (h) => path39.join(h, ".claude", "settings.json"),
|
|
20004
|
+
hookFormat: "matcher",
|
|
20005
|
+
hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
|
|
20006
|
+
mcpFile: (h) => path39.join(h, ".claude.json"),
|
|
20007
|
+
present: (h) => exists(path39.join(h, ".claude", "settings.json")) || exists(path39.join(h, ".claude.json"))
|
|
19994
20008
|
},
|
|
19995
20009
|
{
|
|
19996
20010
|
id: "gemini",
|
|
19997
20011
|
label: "Gemini CLI",
|
|
19998
|
-
hookLabel: "BeforeTool hook",
|
|
19999
20012
|
setupCommand: "node9 setup gemini",
|
|
20000
|
-
|
|
20001
|
-
|
|
20002
|
-
|
|
20003
|
-
|
|
20004
|
-
)
|
|
20013
|
+
hookFile: (h) => path39.join(h, ".gemini", "settings.json"),
|
|
20014
|
+
hookFormat: "matcher",
|
|
20015
|
+
hookEvents: [ck("BeforeTool"), lg("AfterTool")],
|
|
20016
|
+
mcpFile: (h) => path39.join(h, ".gemini", "settings.json"),
|
|
20017
|
+
present: (h) => exists(path39.join(h, ".gemini", "settings.json"))
|
|
20005
20018
|
},
|
|
20006
20019
|
{
|
|
20007
20020
|
id: "codex",
|
|
20008
20021
|
label: "Codex",
|
|
20009
|
-
hookLabel: "PreToolUse hook",
|
|
20010
20022
|
setupCommand: "node9 setup codex",
|
|
20011
|
-
|
|
20012
|
-
|
|
20013
|
-
|
|
20014
|
-
|
|
20015
|
-
)
|
|
20023
|
+
hookFile: (h) => path39.join(h, ".codex", "hooks.json"),
|
|
20024
|
+
hookFormat: "matcher",
|
|
20025
|
+
hookEvents: [ck("PreToolUse"), ck("UserPromptSubmit")],
|
|
20026
|
+
present: (h) => exists(path39.join(h, ".codex"))
|
|
20016
20027
|
},
|
|
20017
20028
|
{
|
|
20018
20029
|
id: "antigravity",
|
|
20019
20030
|
label: "Antigravity",
|
|
20020
|
-
hookLabel: "PreToolUse hook",
|
|
20021
20031
|
setupCommand: "node9 setup antigravity",
|
|
20022
|
-
|
|
20023
|
-
|
|
20024
|
-
|
|
20025
|
-
|
|
20026
|
-
)
|
|
20032
|
+
hookFile: (h) => path39.join(h, ".gemini", "config", "hooks.json"),
|
|
20033
|
+
hookFormat: "matcher",
|
|
20034
|
+
hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
|
|
20035
|
+
mcpFile: (h) => path39.join(h, ".gemini", "config", "mcp_config.json"),
|
|
20036
|
+
present: (h) => exists(path39.join(h, ".gemini", "config", "hooks.json")) || exists(path39.join(h, ".gemini", "antigravity-cli")) || exists(path39.join(h, ".gemini", "antigravity-ide"))
|
|
20027
20037
|
},
|
|
20028
20038
|
{
|
|
20029
20039
|
id: "copilot",
|
|
20030
20040
|
label: "GitHub Copilot",
|
|
20031
|
-
hookLabel: "PreToolUse hook",
|
|
20032
20041
|
setupCommand: "node9 setup copilot",
|
|
20033
|
-
|
|
20034
|
-
|
|
20035
|
-
|
|
20036
|
-
|
|
20037
|
-
)
|
|
20042
|
+
hookFile: (h) => path39.join(h, ".copilot", "hooks", "node9.json"),
|
|
20043
|
+
hookFormat: "flat",
|
|
20044
|
+
hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
|
|
20045
|
+
mcpFile: (h) => path39.join(h, ".copilot", "mcp-config.json"),
|
|
20046
|
+
present: (h) => exists(path39.join(h, ".copilot"))
|
|
20038
20047
|
},
|
|
20039
20048
|
{
|
|
20040
20049
|
id: "cursor",
|
|
20041
20050
|
label: "Cursor",
|
|
20042
|
-
hookLabel: "preToolUse hook",
|
|
20043
20051
|
setupCommand: "node9 setup cursor",
|
|
20044
|
-
|
|
20045
|
-
|
|
20046
|
-
|
|
20047
|
-
|
|
20048
|
-
)
|
|
20052
|
+
// MCP-only — no hook file (see note above).
|
|
20053
|
+
hookFormat: "flat",
|
|
20054
|
+
hookEvents: [],
|
|
20055
|
+
mcpFile: (h) => path39.join(h, ".cursor", "mcp.json"),
|
|
20056
|
+
present: (h) => exists(path39.join(h, ".cursor", "mcp.json"))
|
|
20049
20057
|
},
|
|
20050
20058
|
{
|
|
20051
20059
|
id: "hermes",
|
|
20052
20060
|
label: "Hermes Agent",
|
|
20053
|
-
hookLabel: "pre_tool_call hook",
|
|
20054
20061
|
setupCommand: "node9 setup hermes",
|
|
20055
|
-
|
|
20056
|
-
|
|
20062
|
+
hookFile: (h) => hermesConfigPath(h),
|
|
20063
|
+
hookFormat: "yaml",
|
|
20064
|
+
hookEvents: [ck("pre_tool_call"), lg("post_tool_call")],
|
|
20065
|
+
labelPad: 14,
|
|
20066
|
+
// 'post_tool_call' is wider than the default
|
|
20067
|
+
present: (h) => exists(hermesConfigPath(h))
|
|
20057
20068
|
}
|
|
20058
20069
|
];
|
|
20059
20070
|
function getAgentWiring(home = os34.homedir()) {
|
|
20060
20071
|
const detected = detectAgents(home);
|
|
20061
|
-
return AGENT_SPECS.map((spec) =>
|
|
20062
|
-
|
|
20063
|
-
|
|
20064
|
-
|
|
20065
|
-
|
|
20066
|
-
|
|
20067
|
-
|
|
20068
|
-
|
|
20069
|
-
|
|
20072
|
+
return AGENT_SPECS.map((spec) => {
|
|
20073
|
+
const root = spec.hookFile ? readHookRoot(spec.hookFile(home), spec.hookFormat) : "absent";
|
|
20074
|
+
const primary = spec.hookEvents[0];
|
|
20075
|
+
const rootPresent = root !== "absent" && root !== "invalid";
|
|
20076
|
+
const pad = spec.labelPad ?? DEFAULT_LABEL_PAD;
|
|
20077
|
+
const hooks = spec.hookEvents.map((ev) => ({
|
|
20078
|
+
label: hookLabelOf(ev, pad),
|
|
20079
|
+
wired: rootPresent && eventWired(root, ev, spec.hookFormat)
|
|
20080
|
+
}));
|
|
20081
|
+
let wireState;
|
|
20082
|
+
if (root === "absent") wireState = "absent";
|
|
20083
|
+
else if (root === "invalid") wireState = "invalid";
|
|
20084
|
+
else wireState = primary && eventWired(root, primary, spec.hookFormat) ? "wired" : "unwired";
|
|
20085
|
+
const mcp = spec.mcpFile ? readMcp(spec.mcpFile(home)) : null;
|
|
20086
|
+
const anyHookWired = hooks.some((h) => h.wired);
|
|
20087
|
+
return {
|
|
20088
|
+
id: spec.id,
|
|
20089
|
+
label: spec.label,
|
|
20090
|
+
setupCommand: spec.setupCommand,
|
|
20091
|
+
installed: detected[spec.id],
|
|
20092
|
+
present: spec.present(home),
|
|
20093
|
+
hooks,
|
|
20094
|
+
wireState,
|
|
20095
|
+
hookLabel: primary ? `${primary.key} hook` : "MCP proxy",
|
|
20096
|
+
settingsPath: spec.hookFile ? spec.hookFile(home) : spec.mcpFile ? spec.mcpFile(home) : "",
|
|
20097
|
+
configFormat: spec.hookFormat === "yaml" ? "YAML" : "JSON",
|
|
20098
|
+
mcpServers: mcp ? mcp.wrapped : null,
|
|
20099
|
+
mcpProtected: mcp ? mcp.present : false,
|
|
20100
|
+
isProtected: anyHookWired || (mcp?.present ?? false)
|
|
20101
|
+
};
|
|
20102
|
+
});
|
|
20070
20103
|
}
|
|
20071
20104
|
|
|
20072
20105
|
// src/cli/commands/doctor.ts
|
|
@@ -20156,12 +20189,13 @@ function registerDoctorCommand(program2, version2) {
|
|
|
20156
20189
|
section("Agent Hooks");
|
|
20157
20190
|
const notConfigured = [];
|
|
20158
20191
|
for (const a of getAgentWiring(homeDir2)) {
|
|
20159
|
-
|
|
20160
|
-
|
|
20161
|
-
|
|
20162
|
-
fail(`${a.label} \u2014 settings found but node9 hook missing`, `Run: ${a.setupCommand}`);
|
|
20192
|
+
const anyHookWired = a.hooks.some((h) => h.wired);
|
|
20193
|
+
if (a.isProtected) {
|
|
20194
|
+
pass(`${a.label} \u2014 ${anyHookWired ? `${a.hookLabel} active` : "MCP proxy active"}`);
|
|
20163
20195
|
} else if (a.wireState === "invalid") {
|
|
20164
20196
|
fail(`${a.label} \u2014 settings file is invalid JSON`, a.settingsPath);
|
|
20197
|
+
} else if (a.wireState === "unwired") {
|
|
20198
|
+
fail(`${a.label} \u2014 settings found but node9 hook missing`, `Run: ${a.setupCommand}`);
|
|
20165
20199
|
} else {
|
|
20166
20200
|
notConfigured.push(a.label);
|
|
20167
20201
|
}
|
|
@@ -21463,52 +21497,10 @@ function registerDaemonCommand(program2) {
|
|
|
21463
21497
|
// src/cli/commands/status.ts
|
|
21464
21498
|
init_core();
|
|
21465
21499
|
init_daemon();
|
|
21466
|
-
init_setup();
|
|
21467
21500
|
import chalk15 from "chalk";
|
|
21468
21501
|
import fs42 from "fs";
|
|
21469
21502
|
import path43 from "path";
|
|
21470
21503
|
import os38 from "os";
|
|
21471
|
-
import * as yaml3 from "yaml";
|
|
21472
|
-
function readHermesHooks(configPath) {
|
|
21473
|
-
if (!fs42.existsSync(configPath)) return null;
|
|
21474
|
-
let raw;
|
|
21475
|
-
try {
|
|
21476
|
-
raw = fs42.readFileSync(configPath, "utf-8");
|
|
21477
|
-
} catch {
|
|
21478
|
-
return null;
|
|
21479
|
-
}
|
|
21480
|
-
try {
|
|
21481
|
-
const cfg = yaml3.parse(raw);
|
|
21482
|
-
const has = (event) => (cfg?.hooks?.[event] ?? []).some(
|
|
21483
|
-
(e) => typeof e?.command === "string" && isNode9Hook(e.command)
|
|
21484
|
-
);
|
|
21485
|
-
return { pre: has("pre_tool_call"), post: has("post_tool_call") };
|
|
21486
|
-
} catch {
|
|
21487
|
-
console.error(
|
|
21488
|
-
chalk15.yellow(
|
|
21489
|
-
` \u26A0\uFE0F Hermes config.yaml at ${configPath} is not valid YAML \u2014 showing as unwired.`
|
|
21490
|
-
)
|
|
21491
|
-
);
|
|
21492
|
-
return { pre: false, post: false };
|
|
21493
|
-
}
|
|
21494
|
-
}
|
|
21495
|
-
function readJson3(filePath) {
|
|
21496
|
-
try {
|
|
21497
|
-
if (fs42.existsSync(filePath)) return JSON.parse(fs42.readFileSync(filePath, "utf-8"));
|
|
21498
|
-
} catch {
|
|
21499
|
-
}
|
|
21500
|
-
return null;
|
|
21501
|
-
}
|
|
21502
|
-
function matchersHaveNode9Hook2(matchers) {
|
|
21503
|
-
return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
|
|
21504
|
-
}
|
|
21505
|
-
function flatHaveNode9Hook2(entries) {
|
|
21506
|
-
return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
|
|
21507
|
-
}
|
|
21508
|
-
function wrappedMcpServers(servers) {
|
|
21509
|
-
if (!servers) return [];
|
|
21510
|
-
return Object.entries(servers).filter(([, s]) => s.command === "node9" && Array.isArray(s.args) && s.args.length > 0).map(([name, s]) => `${name} \u2192 ${s.args.join(" ")}`);
|
|
21511
|
-
}
|
|
21512
21504
|
function printAgentSection(label, hookPairs, wrapped) {
|
|
21513
21505
|
console.log(chalk15.bold(` ${label}`));
|
|
21514
21506
|
for (const { name, present } of hookPairs) {
|
|
@@ -21575,102 +21567,23 @@ function registerStatusCommand(program2) {
|
|
|
21575
21567
|
` Sandbox: ${chalk15.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
|
|
21576
21568
|
);
|
|
21577
21569
|
}
|
|
21578
|
-
const
|
|
21579
|
-
|
|
21580
|
-
path43.join(homeDir2, ".claude", "settings.json")
|
|
21581
|
-
);
|
|
21582
|
-
const claudeConfig = readJson3(path43.join(homeDir2, ".claude.json"));
|
|
21583
|
-
const geminiSettings = readJson3(
|
|
21584
|
-
path43.join(homeDir2, ".gemini", "settings.json")
|
|
21585
|
-
);
|
|
21586
|
-
const cursorConfig = readJson3(path43.join(homeDir2, ".cursor", "mcp.json"));
|
|
21587
|
-
const antigravityHooks = readJson3(
|
|
21588
|
-
path43.join(homeDir2, ".gemini", "config", "hooks.json")
|
|
21589
|
-
);
|
|
21590
|
-
const antigravityMcp = readJson3(
|
|
21591
|
-
path43.join(homeDir2, ".gemini", "config", "mcp_config.json")
|
|
21592
|
-
);
|
|
21593
|
-
const antigravityPresent = antigravityHooks !== null || fs42.existsSync(path43.join(homeDir2, ".gemini", "antigravity-cli")) || fs42.existsSync(path43.join(homeDir2, ".gemini", "antigravity-ide"));
|
|
21594
|
-
const copilotHooks = readJson3(
|
|
21595
|
-
path43.join(homeDir2, ".copilot", "hooks", "node9.json")
|
|
21596
|
-
);
|
|
21597
|
-
const copilotMcp = readJson3(
|
|
21598
|
-
path43.join(homeDir2, ".copilot", "mcp-config.json")
|
|
21599
|
-
);
|
|
21600
|
-
const copilotPresent = fs42.existsSync(path43.join(homeDir2, ".copilot"));
|
|
21601
|
-
const hermesHooks = readHermesHooks(hermesConfigPath(homeDir2));
|
|
21602
|
-
const agentFound = claudeSettings || claudeConfig || geminiSettings || cursorConfig || antigravityPresent || copilotPresent || hermesHooks;
|
|
21603
|
-
if (agentFound) {
|
|
21570
|
+
const wiring = getAgentWiring(os38.homedir()).filter((a) => a.present);
|
|
21571
|
+
if (wiring.length > 0) {
|
|
21604
21572
|
console.log("");
|
|
21605
21573
|
console.log(chalk15.bold(" Agent Wiring:"));
|
|
21606
21574
|
console.log("");
|
|
21607
|
-
|
|
21608
|
-
|
|
21609
|
-
|
|
21610
|
-
|
|
21611
|
-
|
|
21612
|
-
|
|
21613
|
-
|
|
21614
|
-
|
|
21615
|
-
],
|
|
21616
|
-
wrappedMcpServers(claudeConfig?.mcpServers)
|
|
21617
|
-
);
|
|
21618
|
-
console.log("");
|
|
21619
|
-
}
|
|
21620
|
-
if (geminiSettings) {
|
|
21621
|
-
const beforeHook = matchersHaveNode9Hook2(geminiSettings.hooks?.BeforeTool);
|
|
21622
|
-
const afterHook = matchersHaveNode9Hook2(geminiSettings.hooks?.AfterTool);
|
|
21623
|
-
printAgentSection(
|
|
21624
|
-
"Gemini CLI",
|
|
21625
|
-
[
|
|
21626
|
-
{ name: "BeforeTool (node9 check)", present: beforeHook },
|
|
21627
|
-
{ name: "AfterTool (node9 log)", present: afterHook }
|
|
21628
|
-
],
|
|
21629
|
-
wrappedMcpServers(geminiSettings.mcpServers)
|
|
21630
|
-
);
|
|
21631
|
-
console.log("");
|
|
21632
|
-
}
|
|
21633
|
-
if (antigravityPresent) {
|
|
21634
|
-
const preHook = matchersHaveNode9Hook2(antigravityHooks?.hooks?.PreToolUse);
|
|
21635
|
-
const postHook = matchersHaveNode9Hook2(antigravityHooks?.hooks?.PostToolUse);
|
|
21636
|
-
printAgentSection(
|
|
21637
|
-
"Antigravity",
|
|
21638
|
-
[
|
|
21639
|
-
{ name: "PreToolUse (node9 check)", present: preHook },
|
|
21640
|
-
{ name: "PostToolUse (node9 log)", present: postHook }
|
|
21641
|
-
],
|
|
21642
|
-
wrappedMcpServers(antigravityMcp?.mcpServers)
|
|
21643
|
-
);
|
|
21644
|
-
console.log("");
|
|
21645
|
-
}
|
|
21646
|
-
if (copilotPresent) {
|
|
21647
|
-
const preHook = flatHaveNode9Hook2(copilotHooks?.hooks?.PreToolUse);
|
|
21648
|
-
const postHook = flatHaveNode9Hook2(copilotHooks?.hooks?.PostToolUse);
|
|
21649
|
-
const promptHook = flatHaveNode9Hook2(copilotHooks?.hooks?.UserPromptSubmit);
|
|
21650
|
-
printAgentSection(
|
|
21651
|
-
"GitHub Copilot",
|
|
21652
|
-
[
|
|
21653
|
-
{ name: "PreToolUse (node9 check)", present: preHook },
|
|
21654
|
-
{ name: "PostToolUse (node9 log)", present: postHook },
|
|
21655
|
-
{ name: "UserPromptSubmit (node9 check)", present: promptHook }
|
|
21656
|
-
],
|
|
21657
|
-
wrappedMcpServers(copilotMcp?.mcpServers)
|
|
21658
|
-
);
|
|
21659
|
-
console.log("");
|
|
21660
|
-
}
|
|
21661
|
-
if (cursorConfig) {
|
|
21662
|
-
printAgentSection("Cursor", [], wrappedMcpServers(cursorConfig.mcpServers));
|
|
21663
|
-
console.log("");
|
|
21664
|
-
}
|
|
21665
|
-
if (hermesHooks) {
|
|
21575
|
+
for (const a of wiring) {
|
|
21576
|
+
if (a.wireState === "invalid") {
|
|
21577
|
+
console.error(
|
|
21578
|
+
chalk15.yellow(
|
|
21579
|
+
` \u26A0\uFE0F ${a.label} config at ${a.settingsPath} is not valid ${a.configFormat} \u2014 showing as unwired.`
|
|
21580
|
+
)
|
|
21581
|
+
);
|
|
21582
|
+
}
|
|
21666
21583
|
printAgentSection(
|
|
21667
|
-
|
|
21668
|
-
|
|
21669
|
-
|
|
21670
|
-
{ name: "post_tool_call (node9 log)", present: hermesHooks.post }
|
|
21671
|
-
],
|
|
21672
|
-
null
|
|
21673
|
-
// Hermes has no MCP surface
|
|
21584
|
+
a.label,
|
|
21585
|
+
a.hooks.map((h) => ({ name: h.label, present: h.wired })),
|
|
21586
|
+
a.mcpServers
|
|
21674
21587
|
);
|
|
21675
21588
|
console.log("");
|
|
21676
21589
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node9/proxy",
|
|
3
|
-
"version": "1.35.
|
|
3
|
+
"version": "1.35.2",
|
|
4
4
|
"description": "The Sudo Command for AI Agents. Execution Security for Claude Code, Codex, Gemini, Cursor, Opencode, Pi, and any MCP server.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|