caveat-cli 0.14.0 → 0.14.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/caveat.js +0 -0
- package/dist/index.js +132 -55
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/caveat.js
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
@@ -6861,6 +6861,7 @@ import { copyFileSync, existsSync, mkdirSync, readFileSync as readFileSync2, wri
|
|
|
6861
6861
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
6862
6862
|
var EVENT_USER_PROMPT_SUBMIT = "UserPromptSubmit";
|
|
6863
6863
|
var EVENT_POST_TOOL_USE = "PostToolUse";
|
|
6864
|
+
var EVENT_POST_TOOL_USE_FAILURE = "PostToolUseFailure";
|
|
6864
6865
|
var EVENT_STOP = "Stop";
|
|
6865
6866
|
function quote(p) {
|
|
6866
6867
|
return p.includes(" ") ? `"${p}"` : p;
|
|
@@ -6892,6 +6893,15 @@ function removeHook(settings, event, command) {
|
|
|
6892
6893
|
settings.hooks[event] = filtered;
|
|
6893
6894
|
return true;
|
|
6894
6895
|
}
|
|
6896
|
+
function findExistingHookCommand(settings, event, command) {
|
|
6897
|
+
const list = settings.hooks?.[event];
|
|
6898
|
+
if (!list) return void 0;
|
|
6899
|
+
for (const entry of list) {
|
|
6900
|
+
const found = entry.hooks?.find((h) => isSameHookCommand(h.command, command));
|
|
6901
|
+
if (found) return found.command;
|
|
6902
|
+
}
|
|
6903
|
+
return void 0;
|
|
6904
|
+
}
|
|
6895
6905
|
function isSameHookCommand(actual, expected) {
|
|
6896
6906
|
return actual === expected || actual.endsWith(` ${expected}`);
|
|
6897
6907
|
}
|
|
@@ -6971,21 +6981,31 @@ function installClaudeIntegration(opts) {
|
|
|
6971
6981
|
const usCmd = hookCommand(opts.nodePath, opts.cliScriptPath, "user-prompt-submit");
|
|
6972
6982
|
const ptCmd = hookCommand(opts.nodePath, opts.cliScriptPath, "post-tool-use");
|
|
6973
6983
|
const stopCmd = hookCommand(opts.nodePath, opts.cliScriptPath, "stop");
|
|
6984
|
+
const ptInstallCmd = findExistingHookCommand(settings, EVENT_POST_TOOL_USE, ptCmd) ?? ptCmd;
|
|
6974
6985
|
const userPromptSubmit = upsertHook(settings, EVENT_USER_PROMPT_SUBMIT, usCmd);
|
|
6975
|
-
const postToolUse = upsertHook(settings, EVENT_POST_TOOL_USE,
|
|
6986
|
+
const postToolUse = upsertHook(settings, EVENT_POST_TOOL_USE, ptInstallCmd);
|
|
6987
|
+
const postToolUseFailure = upsertHook(
|
|
6988
|
+
settings,
|
|
6989
|
+
EVENT_POST_TOOL_USE_FAILURE,
|
|
6990
|
+
ptInstallCmd
|
|
6991
|
+
);
|
|
6976
6992
|
const stop = upsertHook(settings, EVENT_STOP, stopCmd);
|
|
6977
6993
|
let backupPath;
|
|
6978
|
-
const anyAdded = userPromptSubmit === "added" || postToolUse === "added" || stop === "added";
|
|
6994
|
+
const anyAdded = userPromptSubmit === "added" || postToolUse === "added" || postToolUseFailure === "added" || stop === "added";
|
|
6979
6995
|
if (!opts.dryRun && anyAdded) {
|
|
6980
6996
|
const backup = writeSettings(settingsPath, settings);
|
|
6981
6997
|
if (backup) backupPath = backup;
|
|
6982
6998
|
} else if (opts.dryRun) {
|
|
6983
6999
|
opts.logger.info(
|
|
6984
|
-
`[dry-run] would ${userPromptSubmit === "added" ? "add" : "keep"} UserPromptSubmit, ${postToolUse === "added" ? "add" : "keep"} PostToolUse, ${stop === "added" ? "add" : "keep"} Stop hook in ${settingsPath}`
|
|
7000
|
+
`[dry-run] would ${userPromptSubmit === "added" ? "add" : "keep"} UserPromptSubmit, ${postToolUse === "added" ? "add" : "keep"} PostToolUse, ${postToolUseFailure === "added" ? "add" : "keep"} PostToolUseFailure, ${stop === "added" ? "add" : "keep"} Stop hook in ${settingsPath}`
|
|
6985
7001
|
);
|
|
6986
7002
|
}
|
|
6987
7003
|
const mcp = opts.skipMcpRegistration ? { action: "skipped", detail: "skipped by caller" } : registerMcp(opts.nodePath, opts.cliScriptPath, opts.dryRun, opts.logger);
|
|
6988
|
-
return {
|
|
7004
|
+
return {
|
|
7005
|
+
mcp,
|
|
7006
|
+
hooks: { userPromptSubmit, postToolUse, postToolUseFailure, stop },
|
|
7007
|
+
backupPath
|
|
7008
|
+
};
|
|
6989
7009
|
}
|
|
6990
7010
|
function uninstallClaudeIntegration(opts) {
|
|
6991
7011
|
const settingsPath = join3(opts.claudeDir, "settings.json");
|
|
@@ -6995,9 +7015,10 @@ function uninstallClaudeIntegration(opts) {
|
|
|
6995
7015
|
const stopCmd = hookCommand(opts.nodePath, opts.cliScriptPath, "stop");
|
|
6996
7016
|
const removedUs = removeHook(settings, EVENT_USER_PROMPT_SUBMIT, usCmd);
|
|
6997
7017
|
const removedPt = removeHook(settings, EVENT_POST_TOOL_USE, ptCmd);
|
|
7018
|
+
const removedPtf = removeHook(settings, EVENT_POST_TOOL_USE_FAILURE, ptCmd);
|
|
6998
7019
|
const removedStop = removeHook(settings, EVENT_STOP, stopCmd);
|
|
6999
7020
|
let backupPath;
|
|
7000
|
-
if (!opts.dryRun && (removedUs || removedPt || removedStop)) {
|
|
7021
|
+
if (!opts.dryRun && (removedUs || removedPt || removedPtf || removedStop)) {
|
|
7001
7022
|
const backup = writeSettings(settingsPath, settings);
|
|
7002
7023
|
if (backup) backupPath = backup;
|
|
7003
7024
|
}
|
|
@@ -7007,6 +7028,7 @@ function uninstallClaudeIntegration(opts) {
|
|
|
7007
7028
|
hooks: {
|
|
7008
7029
|
userPromptSubmit: removedUs ? "added" : "unchanged",
|
|
7009
7030
|
postToolUse: removedPt ? "added" : "unchanged",
|
|
7031
|
+
postToolUseFailure: removedPtf ? "added" : "unchanged",
|
|
7010
7032
|
stop: removedStop ? "added" : "unchanged"
|
|
7011
7033
|
},
|
|
7012
7034
|
backupPath
|
|
@@ -7110,6 +7132,12 @@ function runUninstall(ctx, opts) {
|
|
|
7110
7132
|
ctx.logger.info(
|
|
7111
7133
|
`UserPromptSubmit hook: ${result.hooks.userPromptSubmit === "added" ? "removed" : "not present"}`
|
|
7112
7134
|
);
|
|
7135
|
+
ctx.logger.info(
|
|
7136
|
+
`PostToolUse hook: ${result.hooks.postToolUse === "added" ? "removed" : "not present"}`
|
|
7137
|
+
);
|
|
7138
|
+
ctx.logger.info(
|
|
7139
|
+
`PostToolUseFailure hook: ${result.hooks.postToolUseFailure === "added" ? "removed" : "not present"}`
|
|
7140
|
+
);
|
|
7113
7141
|
ctx.logger.info(
|
|
7114
7142
|
`Stop hook: ${result.hooks.stop === "added" ? "removed" : "not present"}`
|
|
7115
7143
|
);
|
|
@@ -7125,6 +7153,10 @@ function reportInstallResult(ctx, result, dryRun) {
|
|
|
7125
7153
|
ctx.logger.info(
|
|
7126
7154
|
`${prefix}UserPromptSubmit hook: ${result.hooks.userPromptSubmit}`
|
|
7127
7155
|
);
|
|
7156
|
+
ctx.logger.info(`${prefix}PostToolUse hook: ${result.hooks.postToolUse}`);
|
|
7157
|
+
ctx.logger.info(
|
|
7158
|
+
`${prefix}PostToolUseFailure hook: ${result.hooks.postToolUseFailure}`
|
|
7159
|
+
);
|
|
7128
7160
|
ctx.logger.info(`${prefix}Stop hook: ${result.hooks.stop}`);
|
|
7129
7161
|
if (result.backupPath) {
|
|
7130
7162
|
ctx.logger.info(`settings.json backed up: ${result.backupPath}`);
|
|
@@ -21935,11 +21967,13 @@ function extractToolResponseText(response) {
|
|
|
21935
21967
|
return "";
|
|
21936
21968
|
}
|
|
21937
21969
|
function isToolError(payload) {
|
|
21970
|
+
if (payload.hook_event_name === "PostToolUseFailure") return true;
|
|
21938
21971
|
const resp = payload.tool_response ?? payload.toolResponse;
|
|
21939
21972
|
if (resp !== null && typeof resp === "object" && !Array.isArray(resp)) {
|
|
21940
21973
|
if (resp.is_error === true) return true;
|
|
21941
21974
|
}
|
|
21942
21975
|
if (payload.is_error === true) return true;
|
|
21976
|
+
if (typeof payload.error === "string" && payload.error.length > 0) return true;
|
|
21943
21977
|
return false;
|
|
21944
21978
|
}
|
|
21945
21979
|
function spawnWorker(job) {
|
|
@@ -22163,7 +22197,9 @@ async function runHook(name, arg) {
|
|
|
22163
22197
|
}
|
|
22164
22198
|
if (name === "post-tool-use") {
|
|
22165
22199
|
if (!isToolError(payload)) process.exit(0);
|
|
22166
|
-
const errText = extractToolResponseText(
|
|
22200
|
+
const errText = extractToolResponseText(
|
|
22201
|
+
payload.tool_response ?? payload.toolResponse ?? payload.error ?? payload
|
|
22202
|
+
);
|
|
22167
22203
|
if (errText) {
|
|
22168
22204
|
spawnWorker({ sessionId, searchText: errText });
|
|
22169
22205
|
}
|
|
@@ -22500,9 +22536,11 @@ function extractToolResponseText2(response) {
|
|
|
22500
22536
|
function numericExitCode(v) {
|
|
22501
22537
|
return typeof v === "number" && Number.isInteger(v) ? v : null;
|
|
22502
22538
|
}
|
|
22503
|
-
function
|
|
22504
|
-
const
|
|
22505
|
-
|
|
22539
|
+
function processExitCodeFromText(text) {
|
|
22540
|
+
const m = /Process exited with code\s+(-?\d+)/.exec(text);
|
|
22541
|
+
return m ? Number(m[1]) : null;
|
|
22542
|
+
}
|
|
22543
|
+
function transcriptToolOutput(transcriptPath, toolUseId) {
|
|
22506
22544
|
if (!transcriptPath || !toolUseId || !existsSync7(transcriptPath)) return null;
|
|
22507
22545
|
let raw = "";
|
|
22508
22546
|
try {
|
|
@@ -22523,12 +22561,32 @@ function transcriptExitCode(payload) {
|
|
|
22523
22561
|
if (payloadObj === null || typeof payloadObj !== "object") continue;
|
|
22524
22562
|
const p = payloadObj;
|
|
22525
22563
|
if (p.type !== "function_call_output" || p.call_id !== toolUseId) continue;
|
|
22526
|
-
|
|
22527
|
-
const m = /Process exited with code\s+(-?\d+)/.exec(output);
|
|
22528
|
-
if (m) return Number(m[1]);
|
|
22564
|
+
return typeof p.output === "string" ? p.output : "";
|
|
22529
22565
|
}
|
|
22530
22566
|
return null;
|
|
22531
22567
|
}
|
|
22568
|
+
function transcriptExitCode(payload) {
|
|
22569
|
+
const transcriptPath = typeof payload.transcript_path === "string" ? payload.transcript_path : "";
|
|
22570
|
+
const toolUseId = typeof payload.tool_use_id === "string" ? payload.tool_use_id : "";
|
|
22571
|
+
const output = transcriptToolOutput(transcriptPath, toolUseId);
|
|
22572
|
+
return output === null ? null : processExitCodeFromText(output);
|
|
22573
|
+
}
|
|
22574
|
+
function isShellLikeTool(payload) {
|
|
22575
|
+
const name = typeof payload.tool_name === "string" ? payload.tool_name : "";
|
|
22576
|
+
if (name === "Bash" || name === "exec_command") return true;
|
|
22577
|
+
const input = payload.tool_input;
|
|
22578
|
+
if (input !== null && typeof input === "object" && !Array.isArray(input)) {
|
|
22579
|
+
const r = input;
|
|
22580
|
+
return typeof r.command === "string" || typeof r.cmd === "string";
|
|
22581
|
+
}
|
|
22582
|
+
return false;
|
|
22583
|
+
}
|
|
22584
|
+
function toolInputText(input) {
|
|
22585
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) return "";
|
|
22586
|
+
const r = input;
|
|
22587
|
+
const command = r.command ?? r.cmd;
|
|
22588
|
+
return typeof command === "string" ? command : "";
|
|
22589
|
+
}
|
|
22532
22590
|
function isCodexToolError(payload) {
|
|
22533
22591
|
if (payload.is_error === true) return true;
|
|
22534
22592
|
const topExit = numericExitCode(payload.exit_code ?? payload.exitCode);
|
|
@@ -22540,10 +22598,37 @@ function isCodexToolError(payload) {
|
|
|
22540
22598
|
const exit2 = numericExitCode(r.exit_code ?? r.exitCode);
|
|
22541
22599
|
if (exit2 !== null) return exit2 !== 0;
|
|
22542
22600
|
}
|
|
22601
|
+
const responseExit = processExitCodeFromText(extractToolResponseText2(resp));
|
|
22602
|
+
if (responseExit !== null) return responseExit !== 0;
|
|
22543
22603
|
const transcriptExit = transcriptExitCode(payload);
|
|
22544
22604
|
if (transcriptExit !== null) return transcriptExit !== 0;
|
|
22545
22605
|
return false;
|
|
22546
22606
|
}
|
|
22607
|
+
function buildCodexPostToolUseWorkerJob(payload) {
|
|
22608
|
+
const sessionId = codexSessionId(payload);
|
|
22609
|
+
if (!sessionId) return null;
|
|
22610
|
+
const transcriptPath = typeof payload.transcript_path === "string" ? payload.transcript_path : void 0;
|
|
22611
|
+
const toolUseId = typeof payload.tool_use_id === "string" ? payload.tool_use_id : void 0;
|
|
22612
|
+
const responseText = extractToolResponseText2(payload.tool_response ?? payload.toolResponse);
|
|
22613
|
+
const inputText = toolInputText(payload.tool_input);
|
|
22614
|
+
const transcriptOutput = transcriptPath && toolUseId ? transcriptToolOutput(transcriptPath, toolUseId) : null;
|
|
22615
|
+
const searchText = [inputText, responseText, transcriptOutput ?? ""].map((s) => s.trim()).filter(Boolean).join("\n");
|
|
22616
|
+
const knownError = isCodexToolError(payload);
|
|
22617
|
+
if (knownError) {
|
|
22618
|
+
return { sessionId, searchText, knownError: true, transcriptPath, toolUseId };
|
|
22619
|
+
}
|
|
22620
|
+
if (transcriptPath && toolUseId && isShellLikeTool(payload) && searchText) {
|
|
22621
|
+
return {
|
|
22622
|
+
sessionId,
|
|
22623
|
+
searchText,
|
|
22624
|
+
knownError: false,
|
|
22625
|
+
allowSymptomOnly: true,
|
|
22626
|
+
transcriptPath,
|
|
22627
|
+
toolUseId
|
|
22628
|
+
};
|
|
22629
|
+
}
|
|
22630
|
+
return null;
|
|
22631
|
+
}
|
|
22547
22632
|
function codexContextOutput(text, eventName = "UserPromptSubmit") {
|
|
22548
22633
|
return JSON.stringify({
|
|
22549
22634
|
hookSpecificOutput: {
|
|
@@ -22567,37 +22652,39 @@ function drainForSession2(sessionId, eventName = "UserPromptSubmit") {
|
|
|
22567
22652
|
`);
|
|
22568
22653
|
}
|
|
22569
22654
|
}
|
|
22570
|
-
function
|
|
22571
|
-
const
|
|
22572
|
-
|
|
22573
|
-
|
|
22574
|
-
|
|
22575
|
-
|
|
22576
|
-
writeFileSync5(workFile, JSON.stringify(job), "utf-8");
|
|
22577
|
-
} catch (err) {
|
|
22578
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
22579
|
-
process.stderr.write(`[caveat:codex-hook] worker writefile error: ${msg}
|
|
22580
|
-
`);
|
|
22581
|
-
return;
|
|
22655
|
+
async function waitForTranscriptOutput(transcriptPath, toolUseId) {
|
|
22656
|
+
const deadline = Date.now() + 2e3;
|
|
22657
|
+
while (Date.now() <= deadline) {
|
|
22658
|
+
const output = transcriptToolOutput(transcriptPath, toolUseId);
|
|
22659
|
+
if (output !== null) return output;
|
|
22660
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
22582
22661
|
}
|
|
22583
|
-
|
|
22584
|
-
|
|
22585
|
-
|
|
22586
|
-
|
|
22587
|
-
|
|
22588
|
-
|
|
22589
|
-
|
|
22590
|
-
);
|
|
22591
|
-
|
|
22592
|
-
|
|
22593
|
-
|
|
22594
|
-
|
|
22595
|
-
|
|
22596
|
-
|
|
22597
|
-
|
|
22598
|
-
} catch {
|
|
22662
|
+
return null;
|
|
22663
|
+
}
|
|
22664
|
+
async function processCodexWorkerJob(job, opts = { waitForTranscript: true }) {
|
|
22665
|
+
if (!job.searchText || !job.sessionId) return;
|
|
22666
|
+
let searchText = job.searchText;
|
|
22667
|
+
let knownError = job.knownError === true;
|
|
22668
|
+
if (opts.waitForTranscript && job.transcriptPath && job.toolUseId) {
|
|
22669
|
+
const transcriptOutput = await waitForTranscriptOutput(job.transcriptPath, job.toolUseId);
|
|
22670
|
+
if (transcriptOutput) {
|
|
22671
|
+
const exit2 = processExitCodeFromText(transcriptOutput);
|
|
22672
|
+
if (exit2 !== null) {
|
|
22673
|
+
if (exit2 === 0) return;
|
|
22674
|
+
knownError = true;
|
|
22675
|
+
}
|
|
22676
|
+
searchText = [searchText, transcriptOutput].filter(Boolean).join("\n");
|
|
22599
22677
|
}
|
|
22600
22678
|
}
|
|
22679
|
+
if (!knownError && job.allowSymptomOnly !== true) return;
|
|
22680
|
+
const hits = searchCaveatsFromTextSafely2(searchText);
|
|
22681
|
+
if (hits.length === 0) return;
|
|
22682
|
+
const ctx = buildContextSafely2();
|
|
22683
|
+
if (!ctx) return;
|
|
22684
|
+
try {
|
|
22685
|
+
appendPendingReminder(ctx.caveatHome, job.sessionId, toolErrorReminderText(hits));
|
|
22686
|
+
} catch {
|
|
22687
|
+
}
|
|
22601
22688
|
}
|
|
22602
22689
|
async function runCodexWorker(workFile) {
|
|
22603
22690
|
let raw;
|
|
@@ -22616,15 +22703,7 @@ async function runCodexWorker(workFile) {
|
|
|
22616
22703
|
} catch {
|
|
22617
22704
|
process.exit(0);
|
|
22618
22705
|
}
|
|
22619
|
-
|
|
22620
|
-
const hits = searchCaveatsFromTextSafely2(job.searchText);
|
|
22621
|
-
if (hits.length === 0) process.exit(0);
|
|
22622
|
-
const ctx = buildContextSafely2();
|
|
22623
|
-
if (!ctx) process.exit(0);
|
|
22624
|
-
try {
|
|
22625
|
-
appendPendingReminder(ctx.caveatHome, job.sessionId, toolErrorReminderText(hits));
|
|
22626
|
-
} catch {
|
|
22627
|
-
}
|
|
22706
|
+
await processCodexWorkerJob(job);
|
|
22628
22707
|
process.exit(0);
|
|
22629
22708
|
}
|
|
22630
22709
|
function runDiagnostics(codexHome = process.env.CODEX_HOME ?? join10(homedir3(), ".codex")) {
|
|
@@ -22669,8 +22748,8 @@ async function runCodexHook(name, arg) {
|
|
|
22669
22748
|
}
|
|
22670
22749
|
const payload = parsePayload2(raw);
|
|
22671
22750
|
const sessionId = codexSessionId(payload);
|
|
22672
|
-
if (sessionId) drainForSession2(sessionId);
|
|
22673
|
-
else process.stderr.write("[caveat:codex-hook] missing session_id; pending drain disabled\n");
|
|
22751
|
+
if (sessionId && name === "user-prompt-submit") drainForSession2(sessionId);
|
|
22752
|
+
else if (!sessionId) process.stderr.write("[caveat:codex-hook] missing session_id; pending drain disabled\n");
|
|
22674
22753
|
if (name === "user-prompt-submit") {
|
|
22675
22754
|
const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
|
|
22676
22755
|
const hits = searchCaveatsFromTextSafely2(prompt);
|
|
@@ -22681,10 +22760,8 @@ async function runCodexHook(name, arg) {
|
|
|
22681
22760
|
process.exit(0);
|
|
22682
22761
|
}
|
|
22683
22762
|
if (name === "post-tool-use") {
|
|
22684
|
-
|
|
22685
|
-
if (
|
|
22686
|
-
const errText = extractToolResponseText2(payload.tool_response ?? payload.toolResponse ?? payload);
|
|
22687
|
-
if (errText) spawnCodexWorker({ sessionId, searchText: errText });
|
|
22763
|
+
const job = buildCodexPostToolUseWorkerJob(payload);
|
|
22764
|
+
if (job) await processCodexWorkerJob(job, { waitForTranscript: false });
|
|
22688
22765
|
process.exit(0);
|
|
22689
22766
|
}
|
|
22690
22767
|
if (name === "stop") {
|