@rulemetric/cli 0.5.3 → 0.6.0
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/bin/dev.js +7 -0
- package/dist/base-command.js +4 -4
- package/dist/{chunk-7DRLX3DN.js → chunk-3POYC5NA.js} +3 -3
- package/dist/{chunk-TJ2M5QVL.js → chunk-3QH73QOQ.js} +2 -2
- package/dist/{chunk-CXWY5XKD.js → chunk-4LHEN2FS.js} +8 -8
- package/dist/{chunk-CROYS75W.js → chunk-7K4ILWS7.js} +3 -3
- package/dist/{chunk-TDTHAOXO.js → chunk-DR5WLIOU.js} +2 -2
- package/dist/{chunk-ANOWI23I.js → chunk-HWGRTIXN.js} +98 -105
- package/dist/chunk-HWGRTIXN.js.map +7 -0
- package/dist/{chunk-T76YQCKX.js → chunk-IATQXFJA.js} +2 -2
- package/dist/{chunk-346TGPVR.js → chunk-JCYCAQ6U.js} +126 -33
- package/dist/chunk-JCYCAQ6U.js.map +7 -0
- package/dist/{chunk-AUUHXCJU.js → chunk-JEJ3J5J6.js} +2 -2
- package/dist/{chunk-OAQG5WBV.js → chunk-LAKZUE35.js} +2 -2
- package/dist/{chunk-JNFIYBFL.js → chunk-N2X35ITW.js} +7 -1
- package/dist/{chunk-JNFIYBFL.js.map → chunk-N2X35ITW.js.map} +2 -2
- package/dist/{chunk-CCSR73HF.js → chunk-N5A2IQAK.js} +2 -2
- package/dist/{chunk-VCG4Q53B.js → chunk-OTOWLUOJ.js} +2 -2
- package/dist/{chunk-FTJRMUID.js → chunk-OVGP4OGK.js} +25 -1
- package/dist/chunk-OVGP4OGK.js.map +7 -0
- package/dist/{chunk-OKCFYPIQ.js → chunk-RCMXTLQF.js} +2 -2
- package/dist/{chunk-CGUV6TUA.js → chunk-RUCSRN7F.js} +2 -2
- package/dist/{chunk-4UJATBTG.js → chunk-SLL3OBKN.js} +4 -4
- package/dist/{chunk-CB43W53J.js → chunk-SZ7VDCD6.js} +3 -3
- package/dist/{chunk-N52AHNBH.js → chunk-W4MZXKZ2.js} +6 -2
- package/dist/chunk-W4MZXKZ2.js.map +7 -0
- package/dist/{chunk-KHJUQFIK.js → chunk-YSEQWCN4.js} +2 -2
- package/dist/{chunk-CWFVBGX2.js → chunk-ZRLYHBPN.js} +4 -2
- package/dist/chunk-ZRLYHBPN.js.map +7 -0
- package/dist/{chunk-ER5DPPHB.js → chunk-ZSGKRCKD.js} +6 -3
- package/dist/chunk-ZSGKRCKD.js.map +7 -0
- package/dist/commands/antigravity/backfill.js +5 -5
- package/dist/commands/auth/create-key.js +4 -4
- package/dist/commands/auth/login.js +19 -5
- package/dist/commands/auth/login.js.map +2 -2
- package/dist/commands/auth/logout.js +4 -4
- package/dist/commands/auth/revoke-key.js +4 -4
- package/dist/commands/auth/status.js +4 -4
- package/dist/commands/catalog/index.js +4 -4
- package/dist/commands/catalog/recommend.js +4 -4
- package/dist/commands/convert.js +6 -5
- package/dist/commands/convert.js.map +2 -2
- package/dist/commands/converters.js +4 -4
- package/dist/commands/crons/list.js +4 -4
- package/dist/commands/crons/run.js +4 -4
- package/dist/commands/dashboard.js +4 -4
- package/dist/commands/eval-targets/create.js +4 -4
- package/dist/commands/eval-targets/list.js +4 -4
- package/dist/commands/evals/agent.js +12 -12
- package/dist/commands/evals/benchmark.js +5 -5
- package/dist/commands/evals/compare.js +4 -4
- package/dist/commands/evals/create.js +4 -4
- package/dist/commands/evals/optimize-description.js +4 -4
- package/dist/commands/evals/optimize.js +4 -4
- package/dist/commands/evals/run.js +4 -4
- package/dist/commands/gateway/ensure.js +5 -5
- package/dist/commands/hooks/install.js +88 -55
- package/dist/commands/hooks/install.js.map +2 -2
- package/dist/commands/hooks/run.js +9 -7
- package/dist/commands/hooks/run.js.map +2 -2
- package/dist/commands/hooks/uninstall.js +57 -11
- package/dist/commands/hooks/uninstall.js.map +2 -2
- package/dist/commands/import.js +4 -4
- package/dist/commands/instructions/create.js +4 -4
- package/dist/commands/instructions/delete.js +4 -4
- package/dist/commands/instructions/fork.js +4 -4
- package/dist/commands/instructions/get.js +4 -4
- package/dist/commands/instructions/list.js +4 -4
- package/dist/commands/instructions/promote.js +4 -4
- package/dist/commands/instructions/pull.js +4 -4
- package/dist/commands/instructions/upstream.js +4 -4
- package/dist/commands/instructions/versions.js +4 -4
- package/dist/commands/launcher.js +4 -4
- package/dist/commands/org/current.js +9 -9
- package/dist/commands/org/list.js +6 -6
- package/dist/commands/org/switch.js +7 -7
- package/dist/commands/packs/add-instruction.js +4 -4
- package/dist/commands/packs/apply.js +4 -4
- package/dist/commands/packs/create.js +4 -4
- package/dist/commands/packs/fork.js +4 -4
- package/dist/commands/packs/get.js +4 -4
- package/dist/commands/packs/list.js +4 -4
- package/dist/commands/packs/remove-instruction.js +4 -4
- package/dist/commands/proxy/env.js +5 -5
- package/dist/commands/proxy/logs.js +4 -4
- package/dist/commands/proxy/restart.js +4 -4
- package/dist/commands/proxy/setup.js +4 -4
- package/dist/commands/proxy/start.js +4 -4
- package/dist/commands/proxy/status.js +5 -5
- package/dist/commands/proxy/stop.js +4 -4
- package/dist/commands/research/backfill.js +5 -5
- package/dist/commands/research/tail-transcript.js +5 -5
- package/dist/commands/service/install.js +12 -6
- package/dist/commands/service/install.js.map +2 -2
- package/dist/commands/service/status.js +4 -4
- package/dist/commands/service/uninstall.js +4 -4
- package/dist/commands/sessions/analyze.js +4 -4
- package/dist/commands/sessions/end.js +4 -4
- package/dist/commands/sessions/import.js +4 -4
- package/dist/commands/sessions/list.js +4 -4
- package/dist/commands/sessions/start.js +6 -6
- package/dist/commands/setup.js +10 -10
- package/dist/commands/setup.js.map +2 -2
- package/dist/commands/skills/export-approved.js +4 -4
- package/dist/commands/skills/install.js +4 -4
- package/dist/commands/skills/list.js +4 -4
- package/dist/commands/skills/publish.js +4 -4
- package/dist/commands/skills/search.js +4 -4
- package/dist/commands/skills/update.js +4 -4
- package/dist/commands/suggestions/accept.js +4 -4
- package/dist/commands/suggestions/dismiss.js +4 -4
- package/dist/commands/suggestions/list.js +4 -4
- package/dist/commands/suggestions/refresh.js +4 -4
- package/dist/dashboard/Dashboard.js +6 -4
- package/dist/dashboard/Dashboard.js.map +1 -1
- package/dist/dashboard/data.js +3 -1
- package/dist/dashboard/launcher/LauncherOverlay.js +5 -3
- package/dist/dashboard/launcher/api.js +3 -1
- package/dist/dashboard/launcher/useLaunchJobs.js +4 -2
- package/dist/lib/active-org-refresh.js +3 -3
- package/dist/lib/agent-loop.js +9 -9
- package/dist/lib/antigravity-transcript.js +2 -2
- package/dist/lib/api-client.js +3 -1
- package/dist/lib/eval-benchmark.js +2 -2
- package/dist/lib/eval-conversation-handler.js +2 -2
- package/dist/lib/gateway-lifecycle.js +7 -3
- package/dist/lib/handlers/cron-suggest-instructions.js +2 -2
- package/dist/lib/handlers/process-conversation.js +3 -3
- package/dist/lib/handlers/process-eval.js +2 -2
- package/dist/lib/handlers/process-insights.js +2 -2
- package/dist/lib/handlers/process-launch.js +2 -2
- package/dist/lib/hooks-config.js +7 -1
- package/dist/lib/manual-tasks.js +2 -2
- package/dist/lib/research-client.js +2 -2
- package/dist/lib/setup-steps.js +8 -4
- package/dist/lib/statusline-shim.js +3 -1
- package/dist/lib/terminal-target-heartbeat.js +2 -2
- package/oclif.manifest.json +2 -2
- package/package.json +6 -6
- package/dist/chunk-346TGPVR.js.map +0 -7
- package/dist/chunk-ANOWI23I.js.map +0 -7
- package/dist/chunk-CWFVBGX2.js.map +0 -7
- package/dist/chunk-ER5DPPHB.js.map +0 -7
- package/dist/chunk-FTJRMUID.js.map +0 -7
- package/dist/chunk-N52AHNBH.js.map +0 -7
- /package/dist/{chunk-7DRLX3DN.js.map → chunk-3POYC5NA.js.map} +0 -0
- /package/dist/{chunk-TJ2M5QVL.js.map → chunk-3QH73QOQ.js.map} +0 -0
- /package/dist/{chunk-CXWY5XKD.js.map → chunk-4LHEN2FS.js.map} +0 -0
- /package/dist/{chunk-CROYS75W.js.map → chunk-7K4ILWS7.js.map} +0 -0
- /package/dist/{chunk-TDTHAOXO.js.map → chunk-DR5WLIOU.js.map} +0 -0
- /package/dist/{chunk-T76YQCKX.js.map → chunk-IATQXFJA.js.map} +0 -0
- /package/dist/{chunk-AUUHXCJU.js.map → chunk-JEJ3J5J6.js.map} +0 -0
- /package/dist/{chunk-OAQG5WBV.js.map → chunk-LAKZUE35.js.map} +0 -0
- /package/dist/{chunk-CCSR73HF.js.map → chunk-N5A2IQAK.js.map} +0 -0
- /package/dist/{chunk-VCG4Q53B.js.map → chunk-OTOWLUOJ.js.map} +0 -0
- /package/dist/{chunk-OKCFYPIQ.js.map → chunk-RCMXTLQF.js.map} +0 -0
- /package/dist/{chunk-CGUV6TUA.js.map → chunk-RUCSRN7F.js.map} +0 -0
- /package/dist/{chunk-4UJATBTG.js.map → chunk-SLL3OBKN.js.map} +0 -0
- /package/dist/{chunk-CB43W53J.js.map → chunk-SZ7VDCD6.js.map} +0 -0
- /package/dist/{chunk-KHJUQFIK.js.map → chunk-YSEQWCN4.js.map} +0 -0
package/bin/dev.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node --import tsx --no-warnings=ExperimentalWarning
|
|
2
2
|
import {execute} from '@oclif/core'
|
|
3
|
+
|
|
4
|
+
// Dev entry point: default to the LOCAL API so a repo checkout talks to the
|
|
5
|
+
// dev server unless the developer overrides RULEMETRIC_API_URL. The production
|
|
6
|
+
// default (https://rulemetric.com) lives in api-client.ts and applies to the
|
|
7
|
+
// globally-installed CLI (bin/run.js), which end users hit on a fresh install.
|
|
8
|
+
process.env.RULEMETRIC_API_URL ||= 'http://localhost:3000'
|
|
9
|
+
|
|
3
10
|
await execute({development: true, dir: import.meta.url})
|
package/dist/base-command.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BaseCommand
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-SZ7VDCD6.js";
|
|
4
4
|
import "./chunk-R56PH7YE.js";
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-CWFVBGX2.js";
|
|
7
|
-
import "./chunk-W2YERO7E.js";
|
|
5
|
+
import "./chunk-SLL3OBKN.js";
|
|
8
6
|
import "./chunk-EKJ2CABV.js";
|
|
7
|
+
import "./chunk-ZRLYHBPN.js";
|
|
8
|
+
import "./chunk-W2YERO7E.js";
|
|
9
9
|
import "./chunk-NSBPE2FW.js";
|
|
10
10
|
export {
|
|
11
11
|
BaseCommand
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
processConversationJob
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-DR5WLIOU.js";
|
|
4
4
|
import {
|
|
5
5
|
apiPatch
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ZRLYHBPN.js";
|
|
7
7
|
|
|
8
8
|
// src/lib/handlers/process-conversation.ts
|
|
9
9
|
async function processConversation(payload, helpers) {
|
|
@@ -20,4 +20,4 @@ async function processConversation(payload, helpers) {
|
|
|
20
20
|
export {
|
|
21
21
|
processConversation
|
|
22
22
|
};
|
|
23
|
-
//# sourceMappingURL=chunk-
|
|
23
|
+
//# sourceMappingURL=chunk-3POYC5NA.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
apiGet,
|
|
3
3
|
apiPost
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ZRLYHBPN.js";
|
|
5
5
|
|
|
6
6
|
// src/lib/eval-benchmark.ts
|
|
7
7
|
function computeStats(values) {
|
|
@@ -89,4 +89,4 @@ export {
|
|
|
89
89
|
aggregateBenchmark,
|
|
90
90
|
postBenchmark
|
|
91
91
|
};
|
|
92
|
-
//# sourceMappingURL=chunk-
|
|
92
|
+
//# sourceMappingURL=chunk-3QH73QOQ.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
startTerminalTargetHeartbeat
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-JEJ3J5J6.js";
|
|
4
4
|
import {
|
|
5
5
|
processLaunch
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-OTOWLUOJ.js";
|
|
7
7
|
import {
|
|
8
8
|
processRunCleanup
|
|
9
9
|
} from "./chunk-WLFX4PYR.js";
|
|
@@ -18,19 +18,19 @@ import {
|
|
|
18
18
|
} from "./chunk-RQ2TMLKG.js";
|
|
19
19
|
import {
|
|
20
20
|
cronSuggestInstructions
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-IATQXFJA.js";
|
|
22
22
|
import {
|
|
23
23
|
processAnnouncement
|
|
24
24
|
} from "./chunk-BCIUSUEV.js";
|
|
25
25
|
import {
|
|
26
26
|
processConversation
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-3POYC5NA.js";
|
|
28
28
|
import {
|
|
29
29
|
processEval
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-RCMXTLQF.js";
|
|
31
31
|
import {
|
|
32
32
|
processInsights
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-N5A2IQAK.js";
|
|
34
34
|
import {
|
|
35
35
|
isPermanentJobError
|
|
36
36
|
} from "./chunk-DGHWRQXL.js";
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
ApiError,
|
|
39
39
|
apiGet,
|
|
40
40
|
apiPost
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-ZRLYHBPN.js";
|
|
42
42
|
|
|
43
43
|
// src/lib/agent-loop.ts
|
|
44
44
|
var HANDLERS = {
|
|
@@ -267,4 +267,4 @@ export {
|
|
|
267
267
|
pollForWork,
|
|
268
268
|
runAgentLoop
|
|
269
269
|
};
|
|
270
|
-
//# sourceMappingURL=chunk-
|
|
270
|
+
//# sourceMappingURL=chunk-4LHEN2FS.js.map
|
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
} from "./chunk-DTFXCHHI.js";
|
|
4
4
|
import {
|
|
5
5
|
useLaunchJobs
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-YSEQWCN4.js";
|
|
7
7
|
import {
|
|
8
8
|
cancelLaunchJob,
|
|
9
9
|
createLaunchJob,
|
|
10
10
|
listLaunchers
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-W4MZXKZ2.js";
|
|
12
12
|
import {
|
|
13
13
|
ActiveJobsList
|
|
14
14
|
} from "./chunk-QD6X4L4L.js";
|
|
@@ -158,4 +158,4 @@ function LauncherOverlay({
|
|
|
158
158
|
export {
|
|
159
159
|
LauncherOverlay
|
|
160
160
|
};
|
|
161
|
-
//# sourceMappingURL=chunk-
|
|
161
|
+
//# sourceMappingURL=chunk-7K4ILWS7.js.map
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import {
|
|
11
11
|
apiGet,
|
|
12
12
|
apiPost
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-ZRLYHBPN.js";
|
|
14
14
|
|
|
15
15
|
// src/lib/eval-conversation-handler.ts
|
|
16
16
|
async function processConversationJob(jobConfig, evalTargetId, logger) {
|
|
@@ -217,4 +217,4 @@ async function handleAnalyzeAndPropose(target, evalCases, messages, userFeedback
|
|
|
217
217
|
export {
|
|
218
218
|
processConversationJob
|
|
219
219
|
};
|
|
220
|
-
//# sourceMappingURL=chunk-
|
|
220
|
+
//# sourceMappingURL=chunk-DR5WLIOU.js.map
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// src/lib/hooks-config.ts
|
|
2
2
|
import { execSync } from "node:child_process";
|
|
3
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, copyFileSync } from "node:fs";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
|
-
import { join } from "node:path";
|
|
5
|
+
import { join, dirname } from "node:path";
|
|
6
6
|
function detectInstalledTools(projectPath) {
|
|
7
7
|
const tools = ["claude_code"];
|
|
8
8
|
if (existsSync(join(projectPath, ".cursor"))) {
|
|
@@ -16,31 +16,19 @@ function detectInstalledTools(projectPath) {
|
|
|
16
16
|
}
|
|
17
17
|
return tools;
|
|
18
18
|
}
|
|
19
|
-
function generateCursorHooksConfig() {
|
|
19
|
+
function generateCursorHooksConfig(binDir) {
|
|
20
|
+
const prefix = binDir ? `PATH="${binDir}:$PATH" ` : "";
|
|
21
|
+
const cmd = (name) => ({ command: `${prefix}rulemetric hooks run ${name}` });
|
|
20
22
|
return {
|
|
21
23
|
version: 1,
|
|
22
24
|
hooks: {
|
|
23
|
-
sessionStart: [
|
|
24
|
-
|
|
25
|
-
],
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
],
|
|
29
|
-
|
|
30
|
-
{ command: "rulemetric hooks run post-tool-use" }
|
|
31
|
-
],
|
|
32
|
-
beforeShellExecution: [
|
|
33
|
-
{ command: "rulemetric hooks run post-tool-use" }
|
|
34
|
-
],
|
|
35
|
-
afterShellExecution: [
|
|
36
|
-
{ command: "rulemetric hooks run post-tool-use" }
|
|
37
|
-
],
|
|
38
|
-
beforeMCPExecution: [
|
|
39
|
-
{ command: "rulemetric hooks run post-tool-use" }
|
|
40
|
-
],
|
|
41
|
-
afterMCPExecution: [
|
|
42
|
-
{ command: "rulemetric hooks run post-tool-use" }
|
|
43
|
-
],
|
|
25
|
+
sessionStart: [cmd("session-start")],
|
|
26
|
+
beforeSubmitPrompt: [cmd("user-prompt")],
|
|
27
|
+
afterFileEdit: [cmd("post-tool-use")],
|
|
28
|
+
beforeShellExecution: [cmd("post-tool-use")],
|
|
29
|
+
afterShellExecution: [cmd("post-tool-use")],
|
|
30
|
+
beforeMCPExecution: [cmd("post-tool-use")],
|
|
31
|
+
afterMCPExecution: [cmd("post-tool-use")],
|
|
44
32
|
// `afterAgentResponse` carries the full assistant response text +
|
|
45
33
|
// token usage. Verified empirically via a probe hook on
|
|
46
34
|
// 2026-05-12 — Cursor passes `text`, `input_tokens`,
|
|
@@ -48,45 +36,34 @@ function generateCursorHooksConfig() {
|
|
|
48
36
|
// is the cleanest path to per-turn assistant content for Cursor
|
|
49
37
|
// (chat traffic itself bypasses the proxy via HTTP/2 — see
|
|
50
38
|
// docs/audits/2026-05-11-cursor-capture-architecture.md).
|
|
51
|
-
afterAgentResponse: [
|
|
52
|
-
{ command: "rulemetric hooks run assistant-response" }
|
|
53
|
-
],
|
|
39
|
+
afterAgentResponse: [cmd("assistant-response")],
|
|
54
40
|
// `stop` fires when the agent loop ends for a single turn (status:
|
|
55
41
|
// completed|aborted|error). Per-turn, NOT per-session — route it to
|
|
56
42
|
// post-tool-use, not session-end. The real session boundary is
|
|
57
43
|
// `sessionEnd` below.
|
|
58
|
-
stop: [
|
|
59
|
-
|
|
60
|
-
],
|
|
61
|
-
sessionEnd: [
|
|
62
|
-
{ command: "rulemetric hooks run session-end" }
|
|
63
|
-
]
|
|
44
|
+
stop: [cmd("post-tool-use")],
|
|
45
|
+
sessionEnd: [cmd("session-end")]
|
|
64
46
|
}
|
|
65
47
|
};
|
|
66
48
|
}
|
|
67
|
-
function generateCopilotHooksConfig() {
|
|
49
|
+
function generateCopilotHooksConfig(binDir) {
|
|
50
|
+
const prefix = binDir ? `PATH="${binDir}:$PATH" ` : "";
|
|
51
|
+
const cmd = (name) => ({ type: "command", bash: `${prefix}rulemetric hooks run ${name}` });
|
|
68
52
|
return {
|
|
69
53
|
version: 1,
|
|
70
54
|
hooks: {
|
|
71
|
-
sessionStart: [
|
|
72
|
-
|
|
73
|
-
],
|
|
74
|
-
|
|
75
|
-
{ type: "command", bash: "rulemetric hooks run user-prompt" }
|
|
76
|
-
],
|
|
77
|
-
postToolUse: [
|
|
78
|
-
{ type: "command", bash: "rulemetric hooks run post-tool-use" }
|
|
79
|
-
],
|
|
80
|
-
sessionEnd: [
|
|
81
|
-
{ type: "command", bash: "rulemetric hooks run session-end" }
|
|
82
|
-
]
|
|
55
|
+
sessionStart: [cmd("session-start")],
|
|
56
|
+
userPromptSubmitted: [cmd("user-prompt")],
|
|
57
|
+
postToolUse: [cmd("post-tool-use")],
|
|
58
|
+
sessionEnd: [cmd("session-end")]
|
|
83
59
|
}
|
|
84
60
|
};
|
|
85
61
|
}
|
|
86
|
-
function generateClaudeCodeHooksConfig() {
|
|
62
|
+
function generateClaudeCodeHooksConfig(binDir) {
|
|
63
|
+
const prefix = binDir ? `PATH="${binDir}:$PATH" ` : "";
|
|
87
64
|
const mk = (name) => ({
|
|
88
65
|
type: "command",
|
|
89
|
-
command:
|
|
66
|
+
command: `${prefix}rulemetric hooks run ${name}`
|
|
90
67
|
});
|
|
91
68
|
return {
|
|
92
69
|
SessionStart: [{ hooks: [mk("session-start")] }],
|
|
@@ -96,8 +73,42 @@ function generateClaudeCodeHooksConfig() {
|
|
|
96
73
|
SessionEnd: [{ hooks: [mk("session-end")] }]
|
|
97
74
|
};
|
|
98
75
|
}
|
|
99
|
-
function
|
|
100
|
-
|
|
76
|
+
function readClaudeSettings(configPath) {
|
|
77
|
+
if (!existsSync(configPath)) return { settings: {}, recoveredBackup: null };
|
|
78
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
79
|
+
try {
|
|
80
|
+
return { settings: JSON.parse(raw), recoveredBackup: null };
|
|
81
|
+
} catch {
|
|
82
|
+
const recoveredBackup = `${configPath}.corrupt.bak`;
|
|
83
|
+
writeFileSync(recoveredBackup, raw);
|
|
84
|
+
return { settings: {}, recoveredBackup };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function writeClaudeSettingsWithBackup(configPath, settings) {
|
|
88
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
89
|
+
let backupPath = null;
|
|
90
|
+
if (existsSync(configPath)) {
|
|
91
|
+
backupPath = `${configPath}.bak`;
|
|
92
|
+
copyFileSync(configPath, backupPath);
|
|
93
|
+
}
|
|
94
|
+
writeFileSync(configPath, JSON.stringify(settings, null, 2) + "\n");
|
|
95
|
+
return { backupPath };
|
|
96
|
+
}
|
|
97
|
+
function readMergeableJson(configPath) {
|
|
98
|
+
if (!existsSync(configPath)) return {};
|
|
99
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
100
|
+
try {
|
|
101
|
+
return JSON.parse(raw);
|
|
102
|
+
} catch {
|
|
103
|
+
try {
|
|
104
|
+
writeFileSync(`${configPath}.corrupt.bak`, raw);
|
|
105
|
+
} catch {
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function mergeClaudeCodeHooks(settings, binDir) {
|
|
111
|
+
const generated = generateClaudeCodeHooksConfig(binDir);
|
|
101
112
|
const hooks = settings.hooks ?? {};
|
|
102
113
|
for (const [event, blocks] of Object.entries(generated)) {
|
|
103
114
|
const current = hooks[event] ?? [];
|
|
@@ -110,6 +121,15 @@ function mergeClaudeCodeHooks(settings) {
|
|
|
110
121
|
}
|
|
111
122
|
settings.hooks = hooks;
|
|
112
123
|
}
|
|
124
|
+
function hasRulemetricClaudeHooks(settings) {
|
|
125
|
+
if (!settings.hooks || typeof settings.hooks !== "object") return false;
|
|
126
|
+
const hooks = settings.hooks;
|
|
127
|
+
return Object.values(hooks).some(
|
|
128
|
+
(blocks) => Array.isArray(blocks) && blocks.some(
|
|
129
|
+
(b) => b?.hooks?.some((h) => typeof h?.command === "string" && h.command.includes("rulemetric"))
|
|
130
|
+
)
|
|
131
|
+
);
|
|
132
|
+
}
|
|
113
133
|
function removeClaudeCodeHooks(settings) {
|
|
114
134
|
if (!settings.hooks || typeof settings.hooks !== "object") return false;
|
|
115
135
|
const hooks = settings.hooks;
|
|
@@ -130,20 +150,15 @@ function removeClaudeCodeHooks(settings) {
|
|
|
130
150
|
if (Object.keys(hooks).length === 0) delete settings.hooks;
|
|
131
151
|
return removed > 0;
|
|
132
152
|
}
|
|
133
|
-
function writeCursorHooksToDir(cursorDir) {
|
|
153
|
+
function writeCursorHooksToDir(cursorDir, binDir) {
|
|
134
154
|
if (!existsSync(cursorDir)) {
|
|
135
155
|
return null;
|
|
136
156
|
}
|
|
137
157
|
const configPath = join(cursorDir, "hooks.json");
|
|
138
|
-
const generated = generateCursorHooksConfig();
|
|
139
|
-
|
|
140
|
-
if (
|
|
141
|
-
|
|
142
|
-
existing = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
143
|
-
} catch {
|
|
144
|
-
existing = {};
|
|
145
|
-
}
|
|
146
|
-
}
|
|
158
|
+
const generated = generateCursorHooksConfig(binDir);
|
|
159
|
+
const parsed = readMergeableJson(configPath);
|
|
160
|
+
if (parsed === null) return null;
|
|
161
|
+
const existing = parsed;
|
|
147
162
|
const merged = { ...existing.hooks ?? {} };
|
|
148
163
|
for (const [event, hooks] of Object.entries(generated.hooks)) {
|
|
149
164
|
const current = merged[event] ?? [];
|
|
@@ -161,13 +176,13 @@ function writeCursorHooksToDir(cursorDir) {
|
|
|
161
176
|
writeFileSync(configPath, JSON.stringify(result, null, 2) + "\n");
|
|
162
177
|
return configPath;
|
|
163
178
|
}
|
|
164
|
-
function writeCursorHooks(projectPath) {
|
|
165
|
-
return writeCursorHooksToDir(join(projectPath, ".cursor"));
|
|
179
|
+
function writeCursorHooks(projectPath, binDir) {
|
|
180
|
+
return writeCursorHooksToDir(join(projectPath, ".cursor"), binDir);
|
|
166
181
|
}
|
|
167
|
-
function writeCursorUserHooks() {
|
|
168
|
-
return writeCursorHooksToDir(join(homedir(), ".cursor"));
|
|
182
|
+
function writeCursorUserHooks(binDir) {
|
|
183
|
+
return writeCursorHooksToDir(join(homedir(), ".cursor"), binDir);
|
|
169
184
|
}
|
|
170
|
-
function writeCopilotHooks(projectPath) {
|
|
185
|
+
function writeCopilotHooks(projectPath, binDir) {
|
|
171
186
|
const githubDir = join(projectPath, ".github");
|
|
172
187
|
if (!existsSync(githubDir)) {
|
|
173
188
|
return null;
|
|
@@ -175,7 +190,7 @@ function writeCopilotHooks(projectPath) {
|
|
|
175
190
|
const hooksDir = join(githubDir, "hooks");
|
|
176
191
|
mkdirSync(hooksDir, { recursive: true });
|
|
177
192
|
const configPath = join(hooksDir, "rulemetric.json");
|
|
178
|
-
const config = generateCopilotHooksConfig();
|
|
193
|
+
const config = generateCopilotHooksConfig(binDir);
|
|
179
194
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
180
195
|
return configPath;
|
|
181
196
|
}
|
|
@@ -224,13 +239,8 @@ function writeCursorProxyConfig(projectPath, gatewayPort, certPath) {
|
|
|
224
239
|
const cursorDir = join(projectPath, ".cursor");
|
|
225
240
|
if (!existsSync(cursorDir)) return false;
|
|
226
241
|
const settingsPath = join(cursorDir, "settings.json");
|
|
227
|
-
|
|
228
|
-
if (
|
|
229
|
-
try {
|
|
230
|
-
settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
231
|
-
} catch {
|
|
232
|
-
}
|
|
233
|
-
}
|
|
242
|
+
const settings = readMergeableJson(settingsPath);
|
|
243
|
+
if (settings === null) return false;
|
|
234
244
|
settings["http.proxy"] = `http://localhost:${gatewayPort}`;
|
|
235
245
|
settings["http.proxyStrictSSL"] = existsSync(certPath);
|
|
236
246
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
@@ -238,15 +248,10 @@ function writeCursorProxyConfig(projectPath, gatewayPort, certPath) {
|
|
|
238
248
|
}
|
|
239
249
|
function writeVSCodeProxyConfig(projectPath, gatewayPort, certPath) {
|
|
240
250
|
const vscodeDir = join(projectPath, ".vscode");
|
|
241
|
-
|
|
251
|
+
if (!existsSync(vscodeDir)) return false;
|
|
242
252
|
const settingsPath = join(vscodeDir, "settings.json");
|
|
243
|
-
|
|
244
|
-
if (
|
|
245
|
-
try {
|
|
246
|
-
settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
247
|
-
} catch {
|
|
248
|
-
}
|
|
249
|
-
}
|
|
253
|
+
const settings = readMergeableJson(settingsPath);
|
|
254
|
+
if (settings === null) return false;
|
|
250
255
|
settings["http.proxy"] = `http://localhost:${gatewayPort}`;
|
|
251
256
|
settings["http.proxyStrictSSL"] = existsSync(certPath);
|
|
252
257
|
const copilotAdvanced = settings["github.copilot.advanced"] ?? {};
|
|
@@ -310,6 +315,7 @@ function installMacOSProxyEnv(gatewayPort, certPath) {
|
|
|
310
315
|
const agentsDir = join(homedir(), "Library", "LaunchAgents");
|
|
311
316
|
mkdirSync(agentsDir, { recursive: true });
|
|
312
317
|
const certLine = existsSync(certPath) ? `launchctl setenv NODE_EXTRA_CA_CERTS ${certPath};` : "";
|
|
318
|
+
const bootCmd = `if nc -z -w 1 127.0.0.1 ${gatewayPort} 2>/dev/null; then launchctl setenv HTTPS_PROXY ${proxyUrl};${certLine} else launchctl unsetenv HTTPS_PROXY; launchctl unsetenv NODE_EXTRA_CA_CERTS; fi`;
|
|
313
319
|
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
314
320
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
315
321
|
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
@@ -321,7 +327,7 @@ function installMacOSProxyEnv(gatewayPort, certPath) {
|
|
|
321
327
|
<array>
|
|
322
328
|
<string>/bin/sh</string>
|
|
323
329
|
<string>-c</string>
|
|
324
|
-
<string
|
|
330
|
+
<string>${bootCmd}</string>
|
|
325
331
|
</array>
|
|
326
332
|
<key>RunAtLoad</key>
|
|
327
333
|
<true/>
|
|
@@ -358,13 +364,8 @@ function getVSCodeUserSettingsPath() {
|
|
|
358
364
|
function writeVSCodeUserProxyConfig(gatewayPort) {
|
|
359
365
|
const settingsPath = getVSCodeUserSettingsPath();
|
|
360
366
|
if (!existsSync(join(settingsPath, ".."))) return false;
|
|
361
|
-
|
|
362
|
-
if (
|
|
363
|
-
try {
|
|
364
|
-
settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
365
|
-
} catch {
|
|
366
|
-
}
|
|
367
|
-
}
|
|
367
|
+
const settings = readMergeableJson(settingsPath);
|
|
368
|
+
if (settings === null) return false;
|
|
368
369
|
if (settings["http.proxy"] && !settings["http.proxy"].includes("localhost")) {
|
|
369
370
|
return false;
|
|
370
371
|
}
|
|
@@ -401,13 +402,8 @@ function getCursorUserSettingsPath() {
|
|
|
401
402
|
function writeCursorUserProxyConfig(gatewayPort) {
|
|
402
403
|
const settingsPath = getCursorUserSettingsPath();
|
|
403
404
|
if (!existsSync(join(settingsPath, ".."))) return false;
|
|
404
|
-
|
|
405
|
-
if (
|
|
406
|
-
try {
|
|
407
|
-
settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
408
|
-
} catch {
|
|
409
|
-
}
|
|
410
|
-
}
|
|
405
|
+
const settings = readMergeableJson(settingsPath);
|
|
406
|
+
if (settings === null) return false;
|
|
411
407
|
if (settings["http.proxy"] && !settings["http.proxy"].includes("localhost")) {
|
|
412
408
|
return false;
|
|
413
409
|
}
|
|
@@ -461,14 +457,8 @@ function generateAntigravityHooksConfig() {
|
|
|
461
457
|
}
|
|
462
458
|
function writeAntigravityHooksToDir(dir) {
|
|
463
459
|
const configPath = join(dir, "hooks.json");
|
|
464
|
-
|
|
465
|
-
if (
|
|
466
|
-
try {
|
|
467
|
-
existing = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
468
|
-
} catch {
|
|
469
|
-
existing = {};
|
|
470
|
-
}
|
|
471
|
-
}
|
|
460
|
+
const existing = readMergeableJson(configPath);
|
|
461
|
+
if (existing === null) return null;
|
|
472
462
|
const generated = generateAntigravityHooksConfig();
|
|
473
463
|
const merged = { ...existing, rulemetric: generated.rulemetric };
|
|
474
464
|
mkdirSync(dir, { recursive: true });
|
|
@@ -516,7 +506,10 @@ export {
|
|
|
516
506
|
generateCursorHooksConfig,
|
|
517
507
|
generateCopilotHooksConfig,
|
|
518
508
|
generateClaudeCodeHooksConfig,
|
|
509
|
+
readClaudeSettings,
|
|
510
|
+
writeClaudeSettingsWithBackup,
|
|
519
511
|
mergeClaudeCodeHooks,
|
|
512
|
+
hasRulemetricClaudeHooks,
|
|
520
513
|
removeClaudeCodeHooks,
|
|
521
514
|
writeCursorHooks,
|
|
522
515
|
writeCursorUserHooks,
|
|
@@ -539,4 +532,4 @@ export {
|
|
|
539
532
|
removeAntigravityHooks,
|
|
540
533
|
removeAntigravityUserHooks
|
|
541
534
|
};
|
|
542
|
-
//# sourceMappingURL=chunk-
|
|
535
|
+
//# sourceMappingURL=chunk-HWGRTIXN.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/lib/hooks-config.ts"],
|
|
4
|
+
"sourcesContent": ["import { execSync } from 'node:child_process';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, copyFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join, dirname } from 'node:path';\n\nexport type DetectedTool = 'claude_code' | 'cursor' | 'copilot_agent' | 'antigravity';\n\n/**\n * Detect which AI coding tools are installed in the project by checking\n * for their config directories.\n */\nexport function detectInstalledTools(projectPath: string): DetectedTool[] {\n const tools: DetectedTool[] = ['claude_code'];\n\n if (existsSync(join(projectPath, '.cursor'))) {\n tools.push('cursor');\n }\n\n if (existsSync(join(projectPath, '.github'))) {\n tools.push('copilot_agent');\n }\n\n // Antigravity's workspace config lives at `.agents/`. The user-level\n // hooks file at ~/.gemini/config/hooks.json is installed unconditionally\n // by writeAntigravityUserHooks() so we don't gate on that here.\n if (existsSync(join(projectPath, '.agents'))) {\n tools.push('antigravity');\n }\n\n return tools;\n}\n\n/**\n * Generate Cursor hooks config object.\n *\n * Cursor uses event names like beforeSubmitPrompt, afterFileEdit, etc.\n * Each event maps to an array of hook objects with a `command` field.\n */\nexport function generateCursorHooksConfig(binDir?: string): object {\n // Event coverage matches Cursor 1.7+ hook API (cursor.com/docs/hooks).\n // `sessionStart` is required so the registry file is written with\n // conversation_id before any tool runs; without it the proxy-based linker\n // path would have nothing to map and we'd drop captures. `sessionEnd`\n // emits the session-end signal once per chat conversation (vs `stop`\n // which fires per-turn \u2014 those go through post-tool-use).\n //\n // binDir: prepended to PATH like the Claude Code hooks \u2014 Cursor is a GUI app\n // launched with launchd's minimal PATH (no nvm/pnpm-global bin), so a bare\n // `rulemetric` silently fails with command-not-found.\n const prefix = binDir ? `PATH=\"${binDir}:$PATH\" ` : '';\n const cmd = (name: string) => ({ command: `${prefix}rulemetric hooks run ${name}` });\n return {\n version: 1,\n hooks: {\n sessionStart: [cmd('session-start')],\n beforeSubmitPrompt: [cmd('user-prompt')],\n afterFileEdit: [cmd('post-tool-use')],\n beforeShellExecution: [cmd('post-tool-use')],\n afterShellExecution: [cmd('post-tool-use')],\n beforeMCPExecution: [cmd('post-tool-use')],\n afterMCPExecution: [cmd('post-tool-use')],\n // `afterAgentResponse` carries the full assistant response text +\n // token usage. Verified empirically via a probe hook on\n // 2026-05-12 \u2014 Cursor passes `text`, `input_tokens`,\n // `output_tokens`, `cache_read_tokens`, `cache_write_tokens`. This\n // is the cleanest path to per-turn assistant content for Cursor\n // (chat traffic itself bypasses the proxy via HTTP/2 \u2014 see\n // docs/audits/2026-05-11-cursor-capture-architecture.md).\n afterAgentResponse: [cmd('assistant-response')],\n // `stop` fires when the agent loop ends for a single turn (status:\n // completed|aborted|error). Per-turn, NOT per-session \u2014 route it to\n // post-tool-use, not session-end. The real session boundary is\n // `sessionEnd` below.\n stop: [cmd('post-tool-use')],\n sessionEnd: [cmd('session-end')],\n },\n };\n}\n\n/**\n * Generate Copilot Agent hooks config object.\n *\n * Copilot uses event names like sessionStart, userPromptSubmitted, etc.\n * Each event maps to an array of hook objects with `type` and `bash` fields.\n */\nexport function generateCopilotHooksConfig(binDir?: string): object {\n // binDir prepended to PATH \u2014 Copilot Agent runs in the same GUI/minimal-PATH\n // environment class as Cursor, so a bare `rulemetric` silently fails.\n const prefix = binDir ? `PATH=\"${binDir}:$PATH\" ` : '';\n const cmd = (name: string) => ({ type: 'command', bash: `${prefix}rulemetric hooks run ${name}` });\n return {\n version: 1,\n hooks: {\n sessionStart: [cmd('session-start')],\n userPromptSubmitted: [cmd('user-prompt')],\n postToolUse: [cmd('post-tool-use')],\n sessionEnd: [cmd('session-end')],\n },\n };\n}\n\ninterface ClaudeHookEntry {\n type: 'command';\n command: string;\n}\n\ninterface ClaudeHookBlock {\n matcher?: string;\n hooks: ClaudeHookEntry[];\n}\n\n/** The `settings.hooks` map RuleMetric installs into a project's `.claude/settings.json`. */\nexport type ClaudeCodeHooksConfig = Record<\n 'SessionStart' | 'UserPromptSubmit' | 'PostToolUse' | 'Stop' | 'SessionEnd',\n ClaudeHookBlock[]\n>;\n\n/**\n * Generate the Claude Code hooks map for `.claude/settings.json`.\n *\n * Claude Code keys hook blocks by native event name, each block being\n * `{ matcher?, hooks: [{ type: 'command', command }] }`. Unlike Antigravity\n * these carry NO `RULEMETRIC_HOOK_TOOL` prefix \u2014 `_normalize.sh` defaults to\n * the `claude_code` branch (session_id + transcript_path payload shape).\n *\n * This is the hooks-first, zero-proxy capture path: SessionEnd runs\n * `session-end.sh`, which reimports the whole transcript via\n * `POST /api/sessions/:id/reimport` \u2014 no mitmproxy / CA trust / gateway needed.\n * The rendered system prompt is still proxy-only, so instruction-effectiveness\n * linking is handled separately (Phase 2 synthetic snapshot).\n */\nexport function generateClaudeCodeHooksConfig(binDir?: string): ClaudeCodeHooksConfig {\n // The literal `rulemetric` is always present: it's the marker that install.ts\n // \u00A71 and mergeClaudeCodeHooks use to dedup/strip our hooks (a resolved\n // node+run.js path omits 'rulemetric' in a dev checkout, which would silently\n // break that dedup).\n //\n // `binDir`, when provided, is prepended to PATH so the command resolves even\n // in Claude Code's hook shell \u2014 which runs `/bin/sh -c` with a minimal PATH\n // that does NOT include nvm/pnpm global bin dirs. Without it, a fresh-install\n // user hits `rulemetric: command not found` on every hook, so capture (and\n // the session-start gateway auto-start that keeps the proxy alive) silently\n // fails. Callers resolve the real bin dir at install time and pass it here.\n const prefix = binDir ? `PATH=\"${binDir}:$PATH\" ` : '';\n const mk = (name: string): ClaudeHookEntry => ({\n type: 'command',\n command: `${prefix}rulemetric hooks run ${name}`,\n });\n return {\n SessionStart: [{ hooks: [mk('session-start')] }],\n UserPromptSubmit: [{ hooks: [mk('user-prompt')] }],\n PostToolUse: [{ matcher: '.*', hooks: [mk('post-tool-use')] }],\n Stop: [{ hooks: [mk('assistant-response')] }],\n SessionEnd: [{ hooks: [mk('session-end')] }],\n };\n}\n\n/**\n * Read a Claude Code `settings.json` for merging, WITHOUT ever crashing the\n * install on a fresh machine. A missing file yields `{}`. A malformed file\n * (hand-edited JSONC, a stray trailing comma, a half-written file) is NOT\n * allowed to throw \u2014 that used to hard-fail `hooks install` with a raw\n * SyntaxError and leave the user with NO capture. Instead we back the\n * unparseable file up (so nothing is silently lost) and proceed on a fresh\n * object so capture still installs.\n *\n * Returns the parsed settings plus `recoveredBackup`: the path the corrupt\n * original was copied to, or `null` when the file was absent/valid.\n */\nexport function readClaudeSettings(configPath: string): {\n settings: Record<string, unknown>;\n recoveredBackup: string | null;\n} {\n if (!existsSync(configPath)) return { settings: {}, recoveredBackup: null };\n const raw = readFileSync(configPath, 'utf-8');\n try {\n return { settings: JSON.parse(raw) as Record<string, unknown>, recoveredBackup: null };\n } catch {\n const recoveredBackup = `${configPath}.corrupt.bak`;\n writeFileSync(recoveredBackup, raw);\n return { settings: {}, recoveredBackup };\n }\n}\n\n/**\n * Write a Claude Code `settings.json`, backing up any prior file first so the\n * change is reversible: the `.bak` holds the previous contents for the user to\n * restore manually if a merge ever goes wrong. Creates the parent dir as needed.\n * Returns `backupPath` (the `.bak` written) or `null` when there was no\n * pre-existing file to back up.\n */\nexport function writeClaudeSettingsWithBackup(\n configPath: string,\n settings: Record<string, unknown>,\n): { backupPath: string | null } {\n mkdirSync(dirname(configPath), { recursive: true });\n let backupPath: string | null = null;\n if (existsSync(configPath)) {\n backupPath = `${configPath}.bak`;\n copyFileSync(configPath, backupPath);\n }\n writeFileSync(configPath, JSON.stringify(settings, null, 2) + '\\n');\n return { backupPath };\n}\n\n/**\n * Read a JSON config file for a safe in-place merge. Returns `{}` when the file\n * is absent, the parsed object when it's valid, and `null` when it EXISTS but is\n * unparseable \u2014 in which case the original is first backed up to `.corrupt.bak`.\n * Callers MUST treat `null` as \"skip, do not write\": editor settings.json files\n * are JSONC (comments + trailing commas are legal and common), so a strict\n * JSON.parse throws on valid user files; blindly resetting to `{}` and writing\n * would clobber the user's ENTIRE editor config down to our few keys. Backing up\n * + skipping is the non-destructive floor (mirrors readClaudeSettings).\n */\nfunction readMergeableJson(configPath: string): Record<string, unknown> | null {\n if (!existsSync(configPath)) return {};\n const raw = readFileSync(configPath, 'utf-8');\n try {\n return JSON.parse(raw) as Record<string, unknown>;\n } catch {\n try {\n writeFileSync(`${configPath}.corrupt.bak`, raw);\n } catch {\n /* best-effort backup */\n }\n return null;\n }\n}\n\n/**\n * Merge RuleMetric's Claude Code hooks into an in-memory `.claude/settings.json`\n * object. Claude Code hooks share the same settings.json that install.ts reads,\n * strips (\u00A71), and writes (\u00A75), so this mutates `settings.hooks` in place rather\n * than doing its own file I/O. Idempotent + non-destructive: per event it skips\n * adding when a rulemetric command is already present and otherwise appends,\n * preserving the user's own hooks.\n */\nexport function mergeClaudeCodeHooks(settings: Record<string, unknown>, binDir?: string): void {\n const generated = generateClaudeCodeHooksConfig(binDir);\n const hooks = (settings.hooks ?? {}) as Record<string, ClaudeHookBlock[]>;\n for (const [event, blocks] of Object.entries(generated)) {\n const current = (hooks[event] ?? []) as ClaudeHookBlock[];\n const alreadyPresent = current.some((b) =>\n b?.hooks?.some((h) => typeof h?.command === 'string' && h.command.includes('rulemetric')),\n );\n if (!alreadyPresent) {\n hooks[event] = [...current, ...blocks];\n }\n }\n settings.hooks = hooks;\n}\n\n/**\n * True iff RuleMetric's Claude Code session hooks are present in a settings\n * object \u2014 i.e. capture is actually wired up. This is the honest readiness\n * signal for the onboarding hooks step: capture works when the hooks exist,\n * NOT merely because a proxy env var was written (which greened even when the\n * proxy pointed at a dead port \u2014 the fresh-laptop ConnectionRefused footgun).\n */\nexport function hasRulemetricClaudeHooks(settings: Record<string, unknown>): boolean {\n if (!settings.hooks || typeof settings.hooks !== 'object') return false;\n const hooks = settings.hooks as Record<string, ClaudeHookBlock[]>;\n return Object.values(hooks).some(\n (blocks) =>\n Array.isArray(blocks) &&\n blocks.some((b) =>\n b?.hooks?.some((h) => typeof h?.command === 'string' && h.command.includes('rulemetric')),\n ),\n );\n}\n\n/**\n * Strip RuleMetric's Claude Code hooks from an in-memory settings object while\n * preserving the user's own hook entries. Mirrors install.ts \u00A71 but is reusable\n * for uninstall. Removes emptied event arrays and deletes `settings.hooks` if it\n * ends up empty. Returns true iff anything was removed.\n */\nexport function removeClaudeCodeHooks(settings: Record<string, unknown>): boolean {\n if (!settings.hooks || typeof settings.hooks !== 'object') return false;\n const hooks = settings.hooks as Record<string, ClaudeHookBlock[]>;\n let removed = 0;\n for (const event of Object.keys(hooks)) {\n const entries = hooks[event];\n if (!Array.isArray(entries)) continue;\n const kept = entries.filter((block) => {\n const isRulemetric = block?.hooks?.some(\n (h) => typeof h?.command === 'string' && h.command.includes('rulemetric'),\n );\n if (isRulemetric) removed += 1;\n return !isRulemetric;\n });\n if (kept.length === 0) delete hooks[event];\n else hooks[event] = kept;\n }\n if (Object.keys(hooks).length === 0) delete settings.hooks;\n return removed > 0;\n}\n\n/**\n * Merge rulemetric hooks into a Cursor `hooks.json` at the given directory.\n *\n * Used by both the project (`<repo>/.cursor/`) and user (`~/.cursor/`) writers.\n * Returns the config file path, or null if the cursor dir doesn't exist.\n */\nfunction writeCursorHooksToDir(cursorDir: string, binDir?: string): string | null {\n if (!existsSync(cursorDir)) {\n return null;\n }\n\n const configPath = join(cursorDir, 'hooks.json');\n const generated = generateCursorHooksConfig(binDir) as {\n version: number;\n hooks: Record<string, Array<{ command: string }>>;\n };\n\n const parsed = readMergeableJson(configPath);\n if (parsed === null) return null; // unparseable \u2014 backed up, don't drop sibling hooks\n const existing = parsed as { version?: number; hooks?: Record<string, unknown[]> };\n\n const merged: Record<string, unknown[]> = { ...(existing.hooks ?? {}) };\n\n for (const [event, hooks] of Object.entries(generated.hooks)) {\n const current = merged[event] ?? [];\n const hasRulemetric = current.some(\n (h: unknown) =>\n typeof h === 'object' &&\n h !== null &&\n 'command' in h &&\n typeof (h as { command: string }).command === 'string' &&\n (h as { command: string }).command.includes('rulemetric'),\n );\n if (!hasRulemetric) {\n merged[event] = [...current, ...hooks];\n }\n }\n\n const result = {\n version: existing.version ?? generated.version,\n hooks: merged,\n };\n\n writeFileSync(configPath, JSON.stringify(result, null, 2) + '\\n');\n return configPath;\n}\n\n/**\n * Write Cursor hooks config to `<projectPath>/.cursor/hooks.json`.\n * Merges with existing config \u2014 appends rulemetric hooks without duplicating.\n * Returns the config file path, or null if `.cursor/` doesn't exist.\n */\nexport function writeCursorHooks(projectPath: string, binDir?: string): string | null {\n return writeCursorHooksToDir(join(projectPath, '.cursor'), binDir);\n}\n\n/**\n * Write Cursor hooks config to user-level `~/.cursor/hooks.json` so capture\n * works for every Cursor conversation without per-project setup.\n *\n * Returns the config file path, or null if `~/.cursor/` doesn't exist\n * (i.e., Cursor has never been launched on this machine).\n */\nexport function writeCursorUserHooks(binDir?: string): string | null {\n return writeCursorHooksToDir(join(homedir(), '.cursor'), binDir);\n}\n\n/**\n * Write Copilot Agent hooks config to `.github/hooks/rulemetric.json`.\n * Creates the hooks directory if needed.\n * Returns the config file path, or null if `.github/` doesn't exist.\n */\nexport function writeCopilotHooks(projectPath: string, binDir?: string): string | null {\n const githubDir = join(projectPath, '.github');\n if (!existsSync(githubDir)) {\n return null;\n }\n\n const hooksDir = join(githubDir, 'hooks');\n mkdirSync(hooksDir, { recursive: true });\n\n const configPath = join(hooksDir, 'rulemetric.json');\n const config = generateCopilotHooksConfig(binDir);\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n');\n return configPath;\n}\n\n/**\n * Strip rulemetric hooks from a Cursor `hooks.json` at the given path.\n * Preserves other hooks and removes empty event arrays.\n * Returns true if changes were made.\n */\nfunction removeCursorHooksFromFile(configPath: string): boolean {\n if (!existsSync(configPath)) {\n return false;\n }\n\n let config: { version?: number; hooks?: Record<string, unknown[]> };\n try {\n config = JSON.parse(readFileSync(configPath, 'utf-8'));\n } catch {\n return false;\n }\n\n if (!config.hooks) {\n return false;\n }\n\n let changed = false;\n for (const [event, hooks] of Object.entries(config.hooks)) {\n const filtered = hooks.filter((h: unknown) => {\n if (typeof h !== 'object' || h === null || !('command' in h)) return true;\n const cmd = (h as { command: string }).command;\n if (typeof cmd === 'string' && cmd.includes('rulemetric')) {\n changed = true;\n return false;\n }\n return true;\n });\n\n if (filtered.length === 0) {\n delete config.hooks[event];\n } else {\n config.hooks[event] = filtered;\n }\n }\n\n if (changed) {\n writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n');\n }\n\n return changed;\n}\n\n/**\n * Remove rulemetric hooks from `<projectPath>/.cursor/hooks.json`.\n */\nexport function removeCursorHooks(projectPath: string): boolean {\n return removeCursorHooksFromFile(join(projectPath, '.cursor', 'hooks.json'));\n}\n\n/**\n * Remove rulemetric hooks from user-level `~/.cursor/hooks.json`.\n */\nexport function removeCursorUserHooks(): boolean {\n return removeCursorHooksFromFile(join(homedir(), '.cursor', 'hooks.json'));\n}\n\n/**\n * Write Cursor proxy config to `.cursor/settings.json`.\n * Sets `http.proxy` and `http.proxyStrictSSL` while preserving existing settings.\n * Returns true if `.cursor/` exists and config was written, false otherwise.\n */\nexport function writeCursorProxyConfig(projectPath: string, gatewayPort: number, certPath: string): boolean {\n const cursorDir = join(projectPath, '.cursor');\n if (!existsSync(cursorDir)) return false;\n\n const settingsPath = join(cursorDir, 'settings.json');\n const settings = readMergeableJson(settingsPath);\n if (settings === null) return false; // unparseable JSONC \u2014 backed up, don't clobber\n\n settings['http.proxy'] = `http://localhost:${gatewayPort}`;\n settings['http.proxyStrictSSL'] = existsSync(certPath);\n writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\\n');\n return true;\n}\n\n/**\n * Write VS Code proxy config to `.vscode/settings.json` for Copilot capture.\n * Sets `http.proxy`, `http.proxyStrictSSL`, and Copilot-specific override\n * while preserving existing settings.\n * Returns true if config was written, false if `.vscode/` doesn't exist.\n */\nexport function writeVSCodeProxyConfig(projectPath: string, gatewayPort: number, certPath: string): boolean {\n const vscodeDir = join(projectPath, '.vscode');\n // Only touch projects that already use VS Code \u2014 do NOT manufacture a\n // `.vscode/` in every repo (matches writeCursorProxyConfig's contract and this\n // function's own docstring). Creating it pollutes Claude-Code-only repos with\n // an untracked, proxy-pinning settings.json.\n if (!existsSync(vscodeDir)) return false;\n\n const settingsPath = join(vscodeDir, 'settings.json');\n const settings = readMergeableJson(settingsPath);\n if (settings === null) return false; // unparseable JSONC \u2014 backed up, don't clobber\n\n settings['http.proxy'] = `http://localhost:${gatewayPort}`;\n settings['http.proxyStrictSSL'] = existsSync(certPath);\n\n // Copilot-specific override for surgical proxy targeting\n const copilotAdvanced = (settings['github.copilot.advanced'] ?? {}) as Record<string, unknown>;\n copilotAdvanced['debug.overrideProxyUrl'] = `http://localhost:${gatewayPort}`;\n settings['github.copilot.advanced'] = copilotAdvanced;\n\n writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\\n');\n return true;\n}\n\n/**\n * Remove VS Code proxy config from `.vscode/settings.json`.\n * Removes `http.proxy`, `http.proxyStrictSSL`, and Copilot proxy override.\n * Returns true if changes were made.\n */\nexport function removeVSCodeProxyConfig(projectPath: string): boolean {\n const settingsPath = join(projectPath, '.vscode', 'settings.json');\n if (!existsSync(settingsPath)) return false;\n\n let settings: Record<string, unknown>;\n try { settings = JSON.parse(readFileSync(settingsPath, 'utf-8')); } catch { return false; }\n\n let changed = false;\n\n if (settings['http.proxy']) {\n delete settings['http.proxy'];\n delete settings['http.proxyStrictSSL'];\n changed = true;\n }\n\n const copilotAdvanced = settings['github.copilot.advanced'] as Record<string, unknown> | undefined;\n if (copilotAdvanced?.['debug.overrideProxyUrl']) {\n delete copilotAdvanced['debug.overrideProxyUrl'];\n if (Object.keys(copilotAdvanced).length === 0) {\n delete settings['github.copilot.advanced'];\n }\n changed = true;\n }\n\n if (changed) {\n writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\\n');\n }\n\n return changed;\n}\n\n/**\n * Remove Copilot Agent hooks by deleting `.github/hooks/rulemetric.json`.\n * Returns true if the file was removed.\n */\nexport function removeCopilotHooks(projectPath: string): boolean {\n const configPath = join(projectPath, '.github', 'hooks', 'rulemetric.json');\n if (!existsSync(configPath)) {\n return false;\n }\n\n unlinkSync(configPath);\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// macOS LaunchAgent for GUI app env vars (HTTPS_PROXY, NODE_EXTRA_CA_CERTS)\n// ---------------------------------------------------------------------------\n\nconst LAUNCH_AGENT_LABEL = 'com.rulemetric.env';\n\nfunction getLaunchAgentPath(): string {\n return join(homedir(), 'Library', 'LaunchAgents', `${LAUNCH_AGENT_LABEL}.plist`);\n}\n\n/**\n * Set HTTPS_PROXY and NODE_EXTRA_CA_CERTS for all GUI apps on macOS.\n *\n * Two layers:\n * 1. `launchctl setenv` \u2014 immediate effect for newly launched apps\n * 2. LaunchAgent plist \u2014 persists across reboots\n *\n * This ensures VS Code / Copilot / Cursor route LLM traffic through the\n * gateway even when launched from Dock or Spotlight (not a terminal).\n *\n * References:\n * - anthropics/claude-code#30318 (same pattern for Claude Desktop)\n * - Snyk IDE docs recommend launchctl setenv for macOS\n * - Apple QA1067 (archived) \u2014 original env var mechanism removed in 10.10\n */\nexport function installMacOSProxyEnv(gatewayPort: number, certPath: string): boolean {\n if (process.platform !== 'darwin') return false;\n\n const proxyUrl = `http://localhost:${gatewayPort}`;\n\n // Layer 1: immediate effect via launchctl setenv\n try {\n execSync(`launchctl setenv HTTPS_PROXY ${proxyUrl}`, { stdio: 'pipe' });\n if (existsSync(certPath)) {\n execSync(`launchctl setenv NODE_EXTRA_CA_CERTS ${certPath}`, { stdio: 'pipe' });\n }\n } catch {\n return false;\n }\n\n // Layer 2: LaunchAgent plist for persistence across reboots\n const plistPath = getLaunchAgentPath();\n const agentsDir = join(homedir(), 'Library', 'LaunchAgents');\n mkdirSync(agentsDir, { recursive: true });\n\n const certLine = existsSync(certPath)\n ? `launchctl setenv NODE_EXTRA_CA_CERTS ${certPath};`\n : '';\n\n // LIVENESS-GUARDED boot command: at every login this re-pins HTTPS_PROXY\n // machine-wide, but the gateway on :8787 is not a supervised service on the\n // end-user path \u2014 it dies on logout and nothing restarts it. Pinning a dead\n // port breaks HTTPS for every GUI app. So probe :8787 first and only setenv\n // when the gateway actually answers; otherwise clear it (the session-start\n // hook will re-pin once Claude Code brings the gateway back up).\n const bootCmd =\n `if nc -z -w 1 127.0.0.1 ${gatewayPort} 2>/dev/null; then ` +\n `launchctl setenv HTTPS_PROXY ${proxyUrl};${certLine} ` +\n `else launchctl unsetenv HTTPS_PROXY; launchctl unsetenv NODE_EXTRA_CA_CERTS; fi`;\n\n const plist = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\"\n \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${LAUNCH_AGENT_LABEL}</string>\n <key>ProgramArguments</key>\n <array>\n <string>/bin/sh</string>\n <string>-c</string>\n <string>${bootCmd}</string>\n </array>\n <key>RunAtLoad</key>\n <true/>\n</dict>\n</plist>\n`;\n\n writeFileSync(plistPath, plist);\n return true;\n}\n\n/**\n * Remove the RuleMetric LaunchAgent and unset env vars.\n */\nexport function uninstallMacOSProxyEnv(): boolean {\n if (process.platform !== 'darwin') return false;\n\n let changed = false;\n\n // Unset env vars for current session\n try {\n execSync('launchctl unsetenv HTTPS_PROXY', { stdio: 'pipe' });\n execSync('launchctl unsetenv NODE_EXTRA_CA_CERTS', { stdio: 'pipe' });\n } catch { /* may not be set */ }\n\n // Remove LaunchAgent plist\n const plistPath = getLaunchAgentPath();\n if (existsSync(plistPath)) {\n unlinkSync(plistPath);\n changed = true;\n }\n\n return changed;\n}\n\n// ---------------------------------------------------------------------------\n// VS Code user-level proxy settings\n// ---------------------------------------------------------------------------\n\nfunction getVSCodeUserSettingsPath(): string {\n if (process.platform === 'darwin') {\n return join(homedir(), 'Library', 'Application Support', 'Code', 'User', 'settings.json');\n }\n if (process.platform === 'win32') {\n return join(process.env.APPDATA ?? '', 'Code', 'User', 'settings.json');\n }\n return join(homedir(), '.config', 'Code', 'User', 'settings.json');\n}\n\n/**\n * Set http.proxy in VS Code user settings (not workspace).\n *\n * Workspace-level http.proxy is silently ignored because VS Code restricts\n * it to APPLICATION scope (microsoft/vscode#236932). User-level settings\n * are the only reliable way to configure http.proxy.\n */\nexport function writeVSCodeUserProxyConfig(gatewayPort: number): boolean {\n const settingsPath = getVSCodeUserSettingsPath();\n if (!existsSync(join(settingsPath, '..'))) return false;\n\n const settings = readMergeableJson(settingsPath);\n if (settings === null) return false; // unparseable JSONC \u2014 backed up, don't clobber user config\n\n // Only set if not already configured by the user\n if (settings['http.proxy'] && !(settings['http.proxy'] as string).includes('localhost')) {\n return false; // user has a custom proxy, don't overwrite\n }\n\n settings['http.proxy'] = `http://localhost:${gatewayPort}`;\n settings['http.proxyStrictSSL'] = false;\n\n writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\\n');\n return true;\n}\n\n/**\n * Remove RuleMetric proxy config from VS Code user settings.\n */\nexport function removeVSCodeUserProxyConfig(): boolean {\n const settingsPath = getVSCodeUserSettingsPath();\n if (!existsSync(settingsPath)) return false;\n\n let settings: Record<string, unknown>;\n try { settings = JSON.parse(readFileSync(settingsPath, 'utf-8')); } catch { return false; }\n\n const proxy = settings['http.proxy'] as string | undefined;\n if (!proxy?.includes('localhost')) return false;\n\n delete settings['http.proxy'];\n delete settings['http.proxyStrictSSL'];\n writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\\n');\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Cursor user-level proxy settings\n// ---------------------------------------------------------------------------\n//\n// Cursor is a VS Code fork and uses the same settings.json shape, but reads\n// from `Cursor/User/settings.json` instead of `Code/User/settings.json`. The\n// `.cursor/settings.json` (workspace) path can't reach Cursor's Electron main\n// process for the same reason VS Code's workspace path can't reach VS Code's\n// \u2014 the http.proxy setting is restricted to APPLICATION scope and only the\n// user-level file is honored.\n\nfunction getCursorUserSettingsPath(): string {\n if (process.platform === 'darwin') {\n return join(homedir(), 'Library', 'Application Support', 'Cursor', 'User', 'settings.json');\n }\n if (process.platform === 'win32') {\n return join(process.env.APPDATA ?? '', 'Cursor', 'User', 'settings.json');\n }\n return join(homedir(), '.config', 'Cursor', 'User', 'settings.json');\n}\n\n/**\n * Set http.proxy in Cursor user settings (not workspace). Mirrors\n * `writeVSCodeUserProxyConfig` \u2014 workspace-level http.proxy is silently\n * ignored due to APPLICATION-scope restriction (Cursor inherits this from\n * VS Code, see microsoft/vscode#236932).\n *\n * Returns true if config was written, false if Cursor isn't installed\n * (User dir absent) or the user already has a non-localhost proxy set.\n */\nexport function writeCursorUserProxyConfig(gatewayPort: number): boolean {\n const settingsPath = getCursorUserSettingsPath();\n if (!existsSync(join(settingsPath, '..'))) return false;\n\n const settings = readMergeableJson(settingsPath);\n if (settings === null) return false; // unparseable JSONC \u2014 backed up, don't clobber user config\n\n if (settings['http.proxy'] && !(settings['http.proxy'] as string).includes('localhost')) {\n return false;\n }\n\n settings['http.proxy'] = `http://localhost:${gatewayPort}`;\n settings['http.proxyStrictSSL'] = false;\n\n writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\\n');\n return true;\n}\n\n/**\n * Remove RuleMetric proxy config from Cursor user settings.\n */\nexport function removeCursorUserProxyConfig(): boolean {\n const settingsPath = getCursorUserSettingsPath();\n if (!existsSync(settingsPath)) return false;\n\n let settings: Record<string, unknown>;\n try { settings = JSON.parse(readFileSync(settingsPath, 'utf-8')); } catch { return false; }\n\n const proxy = settings['http.proxy'] as string | undefined;\n if (!proxy?.includes('localhost')) return false;\n\n delete settings['http.proxy'];\n delete settings['http.proxyStrictSSL'];\n writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\\n');\n return true;\n}\n\n// \u2500\u2500\u2500 Antigravity (Google's Gemini-based agentic IDE) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Antigravity hooks live in `.agents/hooks.json` (workspace) and\n// `~/.gemini/config/hooks.json` (user-level). Events follow the\n// SessionStart / UserPromptSubmit / PreToolUse / PostToolUse / Stop /\n// SessionEnd vocabulary. Each hook command sets `RULEMETRIC_HOOK_TOOL=\n// antigravity` so `_normalize.sh` routes through the Antigravity branch.\n\ninterface AntigravityHookEntry {\n type: 'command';\n command: string;\n timeout?: number;\n}\n\ninterface AntigravityHookBlock {\n matcher?: string;\n hooks: AntigravityHookEntry[];\n}\n\ninterface AntigravityRuleMetricBlock {\n enabled: boolean;\n SessionStart: AntigravityHookBlock[];\n UserPromptSubmit: AntigravityHookBlock[];\n PreToolUse: AntigravityHookBlock[];\n PostToolUse: AntigravityHookBlock[];\n Stop: AntigravityHookBlock[];\n SessionEnd: AntigravityHookBlock[];\n}\n\nexport type AntigravityHooksConfig = { rulemetric: AntigravityRuleMetricBlock };\n\n/** Resolve the binary string to invoke for a hook command. */\nfunction getRulemetricCommand(): string {\n // Prefer the local Node binary + bin/run.js so the hook works even when\n // `rulemetric` isn't on PATH for the GUI Antigravity process.\n const nodeBin = process.execPath;\n // bin/run.js lives next to dist/ in the installed CLI; resolve from\n // import.meta isn't available in a portable way here, so we use the\n // process argv1 if it's pointing at the CLI entry, else fall back to\n // the bare command name. In practice this function is called inside\n // `rulemetric hooks install`, where process.argv[1] is bin/run.js.\n const cliEntry = process.argv[1];\n if (cliEntry && cliEntry.endsWith('run.js')) {\n return `${nodeBin} ${cliEntry}`;\n }\n return 'rulemetric';\n}\n\nexport function generateAntigravityHooksConfig(): AntigravityHooksConfig {\n const cmd = getRulemetricCommand();\n const mk = (name: string): AntigravityHookEntry => ({\n type: 'command',\n command: `RULEMETRIC_HOOK_TOOL=antigravity ${cmd} hooks run ${name}`,\n timeout: 30,\n });\n return {\n rulemetric: {\n enabled: true,\n SessionStart: [{ hooks: [mk('session-start')] }],\n UserPromptSubmit: [{ hooks: [mk('user-prompt')] }],\n PreToolUse: [{ matcher: '.*', hooks: [mk('post-tool-use')] }],\n PostToolUse: [{ matcher: '.*', hooks: [mk('post-tool-use')] }],\n Stop: [{ hooks: [mk('assistant-response')] }],\n SessionEnd: [{ hooks: [mk('session-end')] }],\n },\n };\n}\n\nfunction writeAntigravityHooksToDir(dir: string): string | null {\n const configPath = join(dir, 'hooks.json');\n const existing = readMergeableJson(configPath);\n if (existing === null) return null; // unparseable \u2014 backed up, don't drop sibling blocks\n const generated = generateAntigravityHooksConfig();\n // Antigravity keys hook blocks by name; RuleMetric owns its own block\n // and never touches sibling blocks the user may have added.\n const merged = { ...existing, rulemetric: generated.rulemetric };\n mkdirSync(dir, { recursive: true });\n writeFileSync(configPath, JSON.stringify(merged, null, 2) + '\\n');\n return configPath;\n}\n\nexport function writeAntigravityHooks(projectPath: string): string | null {\n return writeAntigravityHooksToDir(join(projectPath, '.agents'));\n}\n\nexport function writeAntigravityUserHooks(): string | null {\n const configDir = join(homedir(), '.gemini', 'config');\n return writeAntigravityHooksToDir(configDir);\n}\n\nfunction removeAntigravityHooksFromFile(configPath: string): boolean {\n if (!existsSync(configPath)) return false;\n let existing: Record<string, unknown>;\n try { existing = JSON.parse(readFileSync(configPath, 'utf-8')); } catch { return false; }\n if (!('rulemetric' in existing)) return false;\n delete existing.rulemetric;\n if (Object.keys(existing).length === 0) {\n // No other blocks \u2014 remove the file entirely.\n try { unlinkSync(configPath); } catch { /* ignore */ }\n } else {\n writeFileSync(configPath, JSON.stringify(existing, null, 2) + '\\n');\n }\n return true;\n}\n\nexport function removeAntigravityHooks(projectPath: string): boolean {\n return removeAntigravityHooksFromFile(join(projectPath, '.agents', 'hooks.json'));\n}\n\nexport function removeAntigravityUserHooks(): boolean {\n const modern = removeAntigravityHooksFromFile(join(homedir(), '.gemini', 'config', 'hooks.json'));\n const legacy = removeAntigravityHooksFromFile(join(homedir(), '.gemini', 'hooks.json'));\n return modern || legacy;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
import {
|
|
15
15
|
apiGet,
|
|
16
16
|
apiPost
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-ZRLYHBPN.js";
|
|
18
18
|
|
|
19
19
|
// src/lib/handlers/cron-suggest-instructions.ts
|
|
20
20
|
import { readFileSync } from "node:fs";
|
|
@@ -164,4 +164,4 @@ var cronSuggestInstructions = async (_payload, helpers) => {
|
|
|
164
164
|
export {
|
|
165
165
|
cronSuggestInstructions
|
|
166
166
|
};
|
|
167
|
-
//# sourceMappingURL=chunk-
|
|
167
|
+
//# sourceMappingURL=chunk-IATQXFJA.js.map
|