caveat-cli 0.17.2 → 0.17.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-TRIQ5WFY.js → chunk-ZBHCOA6R.js} +128 -108
- package/dist/chunk-ZBHCOA6R.js.map +1 -0
- package/dist/index.js +319 -539
- package/dist/index.js.map +1 -1
- package/dist/{server-FUWTRVRZ.js → server-GBVHOF67.js} +2 -2
- package/package.json +12 -12
- package/dist/chunk-TRIQ5WFY.js.map +0 -1
- /package/dist/{server-FUWTRVRZ.js.map → server-GBVHOF67.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -38,14 +38,18 @@ import {
|
|
|
38
38
|
get,
|
|
39
39
|
hasAnyStruggleSignal,
|
|
40
40
|
initOwnSync,
|
|
41
|
+
isPrivateOwnerStat,
|
|
42
|
+
isWindows,
|
|
41
43
|
listRecent,
|
|
42
44
|
listStale,
|
|
43
45
|
loadConfig,
|
|
44
46
|
logHookQueryMiss,
|
|
45
47
|
markHit,
|
|
46
48
|
maybeSweepPendingDirs,
|
|
49
|
+
nodeExecutableNames,
|
|
47
50
|
observeRuntimeError,
|
|
48
51
|
openDb,
|
|
52
|
+
powershellCallPrefix,
|
|
49
53
|
prewarmSealedKeys,
|
|
50
54
|
publishOwn,
|
|
51
55
|
readCodexSessionSignals,
|
|
@@ -72,7 +76,7 @@ import {
|
|
|
72
76
|
userPromptSubmitReminderText,
|
|
73
77
|
writeDigestMarker,
|
|
74
78
|
writeUserConfigPatch
|
|
75
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-ZBHCOA6R.js";
|
|
76
80
|
|
|
77
81
|
// ../../node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/codegen/code.js
|
|
78
82
|
var require_code = __commonJS({
|
|
@@ -6885,25 +6889,67 @@ var stdoutLogger = {
|
|
|
6885
6889
|
|
|
6886
6890
|
// src/commands/init.ts
|
|
6887
6891
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
6888
|
-
import { existsSync as
|
|
6889
|
-
import { dirname as
|
|
6892
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync, renameSync, rmdirSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
6893
|
+
import { dirname as dirname3, join as join8 } from "node:path";
|
|
6890
6894
|
|
|
6891
6895
|
// src/claudeInstall.ts
|
|
6892
6896
|
import { spawnSync } from "node:child_process";
|
|
6893
|
-
import {
|
|
6894
|
-
import {
|
|
6897
|
+
import { constants, existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
6898
|
+
import { join as join3 } from "node:path";
|
|
6899
|
+
|
|
6900
|
+
// src/installShared.ts
|
|
6901
|
+
import { accessSync, copyFileSync, existsSync, mkdirSync, realpathSync, statSync, writeFileSync } from "node:fs";
|
|
6902
|
+
import { dirname as dirname2, isAbsolute } from "node:path";
|
|
6903
|
+
function quoteIfSpaces(p) {
|
|
6904
|
+
return p.includes(" ") ? `"${p}"` : p;
|
|
6905
|
+
}
|
|
6906
|
+
function commandTokens(command) {
|
|
6907
|
+
const tokens = [];
|
|
6908
|
+
const pattern = /"([^"]*)"|([^\s"]+)/g;
|
|
6909
|
+
let end = 0;
|
|
6910
|
+
let match;
|
|
6911
|
+
while ((match = pattern.exec(command)) !== null) {
|
|
6912
|
+
if (command.slice(end, match.index).trim()) return null;
|
|
6913
|
+
tokens.push(match[1] ?? match[2]);
|
|
6914
|
+
end = pattern.lastIndex;
|
|
6915
|
+
}
|
|
6916
|
+
return command.slice(end).trim() ? null : tokens;
|
|
6917
|
+
}
|
|
6918
|
+
function isCanonicalAsset(path, expectedPath, mode) {
|
|
6919
|
+
if (typeof path !== "string" || !isAbsolute(path) || !isAbsolute(expectedPath)) return false;
|
|
6920
|
+
try {
|
|
6921
|
+
accessSync(path, mode);
|
|
6922
|
+
return statSync(path).isFile() && realpathSync(path) === realpathSync(expectedPath);
|
|
6923
|
+
} catch {
|
|
6924
|
+
return false;
|
|
6925
|
+
}
|
|
6926
|
+
}
|
|
6927
|
+
function writeFileWithBackup(path, text) {
|
|
6928
|
+
const dir = dirname2(path);
|
|
6929
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
6930
|
+
let backupPath = "";
|
|
6931
|
+
if (existsSync(path)) {
|
|
6932
|
+
backupPath = `${path}.caveat-backup-${Date.now()}`;
|
|
6933
|
+
copyFileSync(path, backupPath);
|
|
6934
|
+
}
|
|
6935
|
+
writeFileSync(path, text, "utf-8");
|
|
6936
|
+
return backupPath;
|
|
6937
|
+
}
|
|
6938
|
+
function writeJsonWithBackup(path, value) {
|
|
6939
|
+
return writeFileWithBackup(path, `${JSON.stringify(value, null, 2)}
|
|
6940
|
+
`);
|
|
6941
|
+
}
|
|
6942
|
+
|
|
6943
|
+
// src/claudeInstall.ts
|
|
6895
6944
|
var EVENT_USER_PROMPT_SUBMIT = "UserPromptSubmit";
|
|
6896
6945
|
var EVENT_POST_TOOL_USE = "PostToolUse";
|
|
6897
6946
|
var EVENT_POST_TOOL_USE_FAILURE = "PostToolUseFailure";
|
|
6898
6947
|
var EVENT_STOP = "Stop";
|
|
6899
|
-
function quote(p) {
|
|
6900
|
-
return p.includes(" ") ? `"${p}"` : p;
|
|
6901
|
-
}
|
|
6902
6948
|
function mcpArgs(cliScriptPath) {
|
|
6903
6949
|
return ["--disable-warning=ExperimentalWarning", cliScriptPath, "mcp-server"];
|
|
6904
6950
|
}
|
|
6905
6951
|
function hookCommand(nodePath, cliScriptPath, event) {
|
|
6906
|
-
return `${
|
|
6952
|
+
return `${quoteIfSpaces(nodePath)} ${quoteIfSpaces(cliScriptPath)} hook ${event}`;
|
|
6907
6953
|
}
|
|
6908
6954
|
function upsertHook(settings, event, command) {
|
|
6909
6955
|
settings.hooks ??= {};
|
|
@@ -6951,27 +6997,6 @@ function isCaveatClaudeHookCommand(actual, event) {
|
|
|
6951
6997
|
const lower = actual.toLowerCase();
|
|
6952
6998
|
return !isEnvPrefixedCommand(actual) && lower.includes("caveat") && actual.includes(`hook ${event}`);
|
|
6953
6999
|
}
|
|
6954
|
-
function commandTokens(command) {
|
|
6955
|
-
const tokens = [];
|
|
6956
|
-
const pattern = /"([^"]*)"|([^\s"]+)/g;
|
|
6957
|
-
let end = 0;
|
|
6958
|
-
let match;
|
|
6959
|
-
while ((match = pattern.exec(command)) !== null) {
|
|
6960
|
-
if (command.slice(end, match.index).trim()) return null;
|
|
6961
|
-
tokens.push(match[1] ?? match[2]);
|
|
6962
|
-
end = pattern.lastIndex;
|
|
6963
|
-
}
|
|
6964
|
-
return command.slice(end).trim() ? null : tokens;
|
|
6965
|
-
}
|
|
6966
|
-
function isCanonicalAsset(path, expectedPath, mode) {
|
|
6967
|
-
if (typeof path !== "string" || !isAbsolute(path) || !isAbsolute(expectedPath)) return false;
|
|
6968
|
-
try {
|
|
6969
|
-
accessSync(path, mode);
|
|
6970
|
-
return statSync(path).isFile() && realpathSync(path) === realpathSync(expectedPath);
|
|
6971
|
-
} catch {
|
|
6972
|
-
return false;
|
|
6973
|
-
}
|
|
6974
|
-
}
|
|
6975
7000
|
function isCanonicalCaveatClaudeHookCommand(actual, event, nodePath, cliScriptPath) {
|
|
6976
7001
|
const tokens = commandTokens(actual);
|
|
6977
7002
|
return tokens?.length === 4 && isCanonicalAsset(tokens[0], nodePath, constants.X_OK) && isCanonicalAsset(tokens[1], cliScriptPath, constants.R_OK) && tokens[2] === "hook" && tokens[3] === event;
|
|
@@ -6982,19 +7007,11 @@ function isCaveatClaudeMcpRegistration(value, nodePath, cliScriptPath) {
|
|
|
6982
7007
|
return Object.keys(server).length === 4 && ["args", "command", "env", "type"].every((key) => Object.hasOwn(server, key)) && server.type === "stdio" && isCanonicalAsset(server.command, nodePath, constants.X_OK) && Array.isArray(server.args) && server.args.length === 3 && server.args[0] === "--disable-warning=ExperimentalWarning" && isCanonicalAsset(server.args[1], cliScriptPath, constants.R_OK) && server.args[2] === "mcp-server" && server.env !== null && typeof server.env === "object" && !Array.isArray(server.env) && Object.keys(server.env).length === 0;
|
|
6983
7008
|
}
|
|
6984
7009
|
function readSettings(path) {
|
|
6985
|
-
if (!
|
|
7010
|
+
if (!existsSync2(path)) return {};
|
|
6986
7011
|
return JSON.parse(readFileSync2(path, "utf-8"));
|
|
6987
7012
|
}
|
|
6988
7013
|
function writeSettings(path, settings) {
|
|
6989
|
-
|
|
6990
|
-
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
6991
|
-
let backupPath = "";
|
|
6992
|
-
if (existsSync(path)) {
|
|
6993
|
-
backupPath = `${path}.caveat-backup-${Date.now()}`;
|
|
6994
|
-
copyFileSync(path, backupPath);
|
|
6995
|
-
}
|
|
6996
|
-
writeFileSync(path, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
6997
|
-
return backupPath;
|
|
7014
|
+
return writeJsonWithBackup(path, settings);
|
|
6998
7015
|
}
|
|
6999
7016
|
var CLAUDE_BIN = "claude";
|
|
7000
7017
|
function shellQuote(s) {
|
|
@@ -7112,51 +7129,36 @@ function uninstallClaudeIntegration(opts) {
|
|
|
7112
7129
|
}
|
|
7113
7130
|
|
|
7114
7131
|
// src/codexHookInstall.ts
|
|
7115
|
-
import {
|
|
7116
|
-
import {
|
|
7132
|
+
import { constants as constants2, existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
7133
|
+
import { join as join4 } from "node:path";
|
|
7117
7134
|
import { parse as parseToml } from "smol-toml";
|
|
7118
|
-
function quote2(p) {
|
|
7119
|
-
return p.includes(" ") ? `"${p}"` : p;
|
|
7120
|
-
}
|
|
7121
7135
|
function hookCommand2(nodePath, cliScriptPath, event, platform = process.platform) {
|
|
7122
|
-
|
|
7123
|
-
return `${prefix}${quote2(nodePath)} ${quote2(cliScriptPath)} codex-hook ${event}`;
|
|
7136
|
+
return `${powershellCallPrefix(platform)}${quoteIfSpaces(nodePath)} ${quoteIfSpaces(cliScriptPath)} codex-hook ${event}`;
|
|
7124
7137
|
}
|
|
7125
7138
|
function eventCommandFragment(event) {
|
|
7126
7139
|
return `codex-hook ${event}`;
|
|
7127
7140
|
}
|
|
7128
7141
|
function isSameHookCommand2(actual, expected) {
|
|
7129
|
-
|
|
7142
|
+
const normalizedTokens = (command) => {
|
|
7143
|
+
const parsed = commandTokens(command);
|
|
7144
|
+
return parsed?.[0] === "&" ? parsed.slice(1) : parsed;
|
|
7145
|
+
};
|
|
7146
|
+
const actualTokens = normalizedTokens(actual);
|
|
7147
|
+
const expectedTokens = normalizedTokens(expected);
|
|
7148
|
+
return actualTokens !== null && expectedTokens !== null && actualTokens.length === expectedTokens.length && actualTokens.every((token, index) => token === expectedTokens[index]);
|
|
7130
7149
|
}
|
|
7131
7150
|
function isCaveatCodexHookCommand(actual, event) {
|
|
7132
7151
|
const lower = actual.toLowerCase();
|
|
7133
7152
|
return lower.includes("caveat") && actual.includes(eventCommandFragment(event));
|
|
7134
7153
|
}
|
|
7135
|
-
function commandTokens2(command) {
|
|
7136
|
-
const tokens = [];
|
|
7137
|
-
const pattern = /"([^"]*)"|([^\s"]+)/g;
|
|
7138
|
-
let end = 0;
|
|
7139
|
-
let match;
|
|
7140
|
-
while ((match = pattern.exec(command)) !== null) {
|
|
7141
|
-
if (command.slice(end, match.index).trim()) return null;
|
|
7142
|
-
tokens.push(match[1] ?? match[2]);
|
|
7143
|
-
end = pattern.lastIndex;
|
|
7144
|
-
}
|
|
7145
|
-
return command.slice(end).trim() ? null : tokens;
|
|
7146
|
-
}
|
|
7147
|
-
function isCanonicalAsset2(path, expectedPath, mode) {
|
|
7148
|
-
if (typeof path !== "string" || !isAbsolute2(path) || !isAbsolute2(expectedPath)) return false;
|
|
7149
|
-
try {
|
|
7150
|
-
accessSync2(path, mode);
|
|
7151
|
-
return statSync2(path).isFile() && realpathSync2(path) === realpathSync2(expectedPath);
|
|
7152
|
-
} catch {
|
|
7153
|
-
return false;
|
|
7154
|
-
}
|
|
7155
|
-
}
|
|
7156
7154
|
function isCanonicalCaveatCodexHookCommand(actual, event, nodePath, cliScriptPath) {
|
|
7157
|
-
const parsed =
|
|
7155
|
+
const parsed = commandTokens(actual);
|
|
7158
7156
|
const tokens = parsed?.[0] === "&" ? parsed.slice(1) : parsed;
|
|
7159
|
-
return tokens?.length === 4 &&
|
|
7157
|
+
return tokens?.length === 4 && isCanonicalAsset(tokens[0], nodePath, constants2.X_OK) && isCanonicalAsset(tokens[1], cliScriptPath, constants2.R_OK) && tokens[2] === "codex-hook" && tokens[3] === event;
|
|
7158
|
+
}
|
|
7159
|
+
function isCanonicalCaveatCodexHookEntry(value, event, nodePath, cliScriptPath) {
|
|
7160
|
+
if (!isPlainRecord(value) || typeof value.type !== "string" || typeof value.command !== "string") return false;
|
|
7161
|
+
return value.type === "command" && isCanonicalCaveatCodexHookCommand(value.command, event, nodePath, cliScriptPath) && value.timeout === 5 && value.timeoutSec === void 0 && value.async === false && value.statusMessage === null;
|
|
7160
7162
|
}
|
|
7161
7163
|
function hasCaveatHook(hooksJson, event, fragment) {
|
|
7162
7164
|
return hooksJson.hooks?.[event]?.some(
|
|
@@ -7164,30 +7166,20 @@ function hasCaveatHook(hooksJson, event, fragment) {
|
|
|
7164
7166
|
) ?? false;
|
|
7165
7167
|
}
|
|
7166
7168
|
function readHooks(path) {
|
|
7167
|
-
if (!
|
|
7169
|
+
if (!existsSync3(path)) return {};
|
|
7168
7170
|
return JSON.parse(readFileSync3(path, "utf-8"));
|
|
7169
7171
|
}
|
|
7170
|
-
function writeJsonWithBackup(path, value) {
|
|
7171
|
-
const dir = dirname3(path);
|
|
7172
|
-
if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true });
|
|
7173
|
-
let backupPath = "";
|
|
7174
|
-
if (existsSync2(path)) {
|
|
7175
|
-
backupPath = `${path}.caveat-backup-${Date.now()}`;
|
|
7176
|
-
copyFileSync2(path, backupPath);
|
|
7177
|
-
}
|
|
7178
|
-
writeFileSync2(path, `${JSON.stringify(value, null, 2)}
|
|
7179
|
-
`, "utf-8");
|
|
7180
|
-
return backupPath;
|
|
7181
|
-
}
|
|
7182
7172
|
function upsertHook2(hooksJson, event, command, subcommand) {
|
|
7183
7173
|
hooksJson.hooks ??= {};
|
|
7184
7174
|
const list = hooksJson.hooks[event] ??= [];
|
|
7185
7175
|
for (const entry of list) {
|
|
7186
7176
|
for (const hook2 of entry.hooks ?? []) {
|
|
7187
|
-
if (isSameHookCommand2(hook2.command, command)) return "unchanged";
|
|
7188
|
-
if (isCaveatCodexHookCommand(hook2.command, subcommand)) {
|
|
7177
|
+
if (isSameHookCommand2(hook2.command, command) && hook2.type === "command" && hook2.timeout === 5 && hook2.timeoutSec === void 0 && hook2.async === false && hook2.statusMessage === null) return "unchanged";
|
|
7178
|
+
if (isSameHookCommand2(hook2.command, command) || isCaveatCodexHookCommand(hook2.command, subcommand)) {
|
|
7189
7179
|
hook2.command = command;
|
|
7190
|
-
hook2.
|
|
7180
|
+
hook2.type = "command";
|
|
7181
|
+
hook2.timeout = 5;
|
|
7182
|
+
delete hook2.timeoutSec;
|
|
7191
7183
|
hook2.async = false;
|
|
7192
7184
|
hook2.statusMessage = null;
|
|
7193
7185
|
return "added";
|
|
@@ -7199,7 +7191,7 @@ function upsertHook2(hooksJson, event, command, subcommand) {
|
|
|
7199
7191
|
{
|
|
7200
7192
|
type: "command",
|
|
7201
7193
|
command,
|
|
7202
|
-
|
|
7194
|
+
timeout: 5,
|
|
7203
7195
|
async: false,
|
|
7204
7196
|
statusMessage: null
|
|
7205
7197
|
}
|
|
@@ -7398,21 +7390,10 @@ function maskTomlStringsAndComments(raw) {
|
|
|
7398
7390
|
}
|
|
7399
7391
|
return output;
|
|
7400
7392
|
}
|
|
7401
|
-
function writeConfigWithBackup(path, text) {
|
|
7402
|
-
const dir = dirname3(path);
|
|
7403
|
-
if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true });
|
|
7404
|
-
let backupPath = "";
|
|
7405
|
-
if (existsSync2(path)) {
|
|
7406
|
-
backupPath = `${path}.caveat-backup-${Date.now()}`;
|
|
7407
|
-
copyFileSync2(path, backupPath);
|
|
7408
|
-
}
|
|
7409
|
-
writeFileSync2(path, text, "utf-8");
|
|
7410
|
-
return backupPath;
|
|
7411
|
-
}
|
|
7412
7393
|
function installCodexHooks(opts) {
|
|
7413
7394
|
const hooksPath = join4(opts.codexHome, "hooks.json");
|
|
7414
7395
|
const configPath = join4(opts.codexHome, "config.toml");
|
|
7415
|
-
const rawConfig =
|
|
7396
|
+
const rawConfig = existsSync3(configPath) ? readFileSync3(configPath, "utf-8") : "";
|
|
7416
7397
|
const enabled = enableCodexHooksFeature(rawConfig);
|
|
7417
7398
|
if (enabled.status === "blocked") {
|
|
7418
7399
|
return {
|
|
@@ -7452,7 +7433,7 @@ function installCodexHooks(opts) {
|
|
|
7452
7433
|
if (backup) backupPath = backup;
|
|
7453
7434
|
}
|
|
7454
7435
|
if (enabled.changed) {
|
|
7455
|
-
const backup =
|
|
7436
|
+
const backup = writeFileWithBackup(configPath, enabled.text);
|
|
7456
7437
|
if (backup) configBackupPath = backup;
|
|
7457
7438
|
}
|
|
7458
7439
|
}
|
|
@@ -7513,11 +7494,19 @@ function detectCodexHookInstallation(codexHome) {
|
|
|
7513
7494
|
postToolUse: hasCaveatHook(hooksJson, "PostToolUse", eventCommandFragment("post-tool-use")),
|
|
7514
7495
|
stop: hasCaveatHook(hooksJson, "Stop", eventCommandFragment("stop"))
|
|
7515
7496
|
};
|
|
7497
|
+
const hasLegacyTimeoutSec = (event, fragment) => hooksJson.hooks?.[event]?.some((entry) => entry.hooks?.some((hook2) => hook2.command.includes(fragment) && hook2.timeoutSec !== void 0)) ?? false;
|
|
7498
|
+
const legacyTimeoutSec = {
|
|
7499
|
+
userPromptSubmit: hasLegacyTimeoutSec("UserPromptSubmit", eventCommandFragment("user-prompt-submit")),
|
|
7500
|
+
postToolUse: hasLegacyTimeoutSec("PostToolUse", eventCommandFragment("post-tool-use")),
|
|
7501
|
+
stop: hasLegacyTimeoutSec("Stop", eventCommandFragment("stop"))
|
|
7502
|
+
};
|
|
7516
7503
|
const count = Object.values(hooks).filter(Boolean).length;
|
|
7504
|
+
const hasLegacy = Object.values(legacyTimeoutSec).some(Boolean);
|
|
7517
7505
|
return {
|
|
7518
|
-
installation: count === 0 ? "not-installed" : count === 3 ? "installed" : "partial",
|
|
7506
|
+
installation: count === 0 ? "not-installed" : count === 3 && !hasLegacy ? "installed" : "partial",
|
|
7519
7507
|
hooksPath,
|
|
7520
|
-
hooks
|
|
7508
|
+
hooks,
|
|
7509
|
+
legacyTimeoutSec
|
|
7521
7510
|
};
|
|
7522
7511
|
}
|
|
7523
7512
|
|
|
@@ -7568,9 +7557,9 @@ function askOnce(question) {
|
|
|
7568
7557
|
}
|
|
7569
7558
|
|
|
7570
7559
|
// src/nodePath.ts
|
|
7571
|
-
import { existsSync as
|
|
7560
|
+
import { existsSync as existsSync4, realpathSync as realpathSync2 } from "node:fs";
|
|
7572
7561
|
import { delimiter, join as join5 } from "node:path";
|
|
7573
|
-
var defaultRealpath = (path) =>
|
|
7562
|
+
var defaultRealpath = (path) => realpathSync2.native(path);
|
|
7574
7563
|
function safeRealpath(path, realpath = defaultRealpath) {
|
|
7575
7564
|
try {
|
|
7576
7565
|
return realpath(path);
|
|
@@ -7582,12 +7571,12 @@ function resolveHookNodePath({
|
|
|
7582
7571
|
env = process.env,
|
|
7583
7572
|
execPath = process.execPath,
|
|
7584
7573
|
platform = process.platform,
|
|
7585
|
-
exists =
|
|
7574
|
+
exists = existsSync4,
|
|
7586
7575
|
realpath = defaultRealpath
|
|
7587
7576
|
} = {}) {
|
|
7588
7577
|
const execRealpath = safeRealpath(execPath, realpath);
|
|
7589
7578
|
const pathEnv = env.PATH ?? env.Path ?? "";
|
|
7590
|
-
const names = platform
|
|
7579
|
+
const names = nodeExecutableNames(platform);
|
|
7591
7580
|
for (const dir of pathEnv.split(delimiter).filter(Boolean)) {
|
|
7592
7581
|
for (const name of names) {
|
|
7593
7582
|
const candidate = join5(dir, name);
|
|
@@ -7602,12 +7591,12 @@ function resolveHookNodePath({
|
|
|
7602
7591
|
}
|
|
7603
7592
|
|
|
7604
7593
|
// src/commands/publish.ts
|
|
7605
|
-
import { existsSync as
|
|
7594
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
7606
7595
|
import { join as join7 } from "node:path";
|
|
7607
7596
|
|
|
7608
7597
|
// src/commands/codexSidecarAdvisory.ts
|
|
7609
7598
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
7610
|
-
import { mkdtempSync, readFileSync as readFileSync4, rmSync, writeFileSync as
|
|
7599
|
+
import { mkdtempSync, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
7611
7600
|
import { tmpdir } from "node:os";
|
|
7612
7601
|
import { join as join6 } from "node:path";
|
|
7613
7602
|
var DEFAULT_HOOK_SIDECAR_TIMEOUT_MS = 12e4;
|
|
@@ -7670,7 +7659,7 @@ function runCodexSidecarAdvisory(input) {
|
|
|
7670
7659
|
args.push("--save-result", resultFile);
|
|
7671
7660
|
if (input.additionalContext) {
|
|
7672
7661
|
const additionalContextFile = join6(resultDir, "hook-signal.json");
|
|
7673
|
-
|
|
7662
|
+
writeFileSync2(additionalContextFile, JSON.stringify({ context: [input.additionalContext] }) + "\n", { encoding: "utf-8", mode: 384, flag: "wx" });
|
|
7674
7663
|
args.push("--additional-context-file", additionalContextFile);
|
|
7675
7664
|
}
|
|
7676
7665
|
const nodeCli = process.env.CAVEAT_CODEX_SIDECAR_NODE_CLI;
|
|
@@ -7765,7 +7754,7 @@ async function runPublish(ctx, opts, dependencies = {}) {
|
|
|
7765
7754
|
const isTty = dependencies.isTty ?? (() => Boolean(process.stdin.isTTY));
|
|
7766
7755
|
const confirm = dependencies.confirm ?? askOnce;
|
|
7767
7756
|
const projectRoot = process.cwd();
|
|
7768
|
-
const hasCodexSidecarConfig = dependencies.hasCodexSidecarConfig ?? ((root) =>
|
|
7757
|
+
const hasCodexSidecarConfig = dependencies.hasCodexSidecarConfig ?? ((root) => existsSync5(join7(root, ".codex-sidecar.yml")));
|
|
7769
7758
|
const publishAdvisory = hasCodexSidecarConfig(projectRoot) ? (changes) => {
|
|
7770
7759
|
try {
|
|
7771
7760
|
return dependencies.runCodexSidecarAdvisory?.(changes, projectRoot) ?? formatCodexSidecarAdvisory(runCodexSidecarAdvisory({
|
|
@@ -7824,22 +7813,22 @@ async function runInit(ctx, opts = { skipClaude: false, dryRun: false }, depende
|
|
|
7824
7813
|
let codexHookState = "not-installed";
|
|
7825
7814
|
ensureUserConfig(ctx.userConfigPath);
|
|
7826
7815
|
ctx.logger.info(`user config: ${ctx.userConfigPath}`);
|
|
7827
|
-
if (!
|
|
7828
|
-
|
|
7829
|
-
|
|
7816
|
+
if (!existsSync6(ctx.paths.knowledgeRepo)) {
|
|
7817
|
+
mkdirSync2(ctx.paths.knowledgeRepo, { recursive: true });
|
|
7818
|
+
mkdirSync2(ctx.paths.entriesDir, { recursive: true });
|
|
7830
7819
|
ctx.logger.info(`knowledge repo scaffolded: ${ctx.paths.knowledgeRepo}`);
|
|
7831
7820
|
} else {
|
|
7832
7821
|
ctx.logger.info(`knowledge repo: ${ctx.paths.knowledgeRepo}`);
|
|
7833
7822
|
}
|
|
7834
7823
|
migrateLegacyCommunityDir(ctx);
|
|
7835
7824
|
const gitignorePath = join8(ctx.paths.knowledgeRepo, ".gitignore");
|
|
7836
|
-
if (!
|
|
7837
|
-
|
|
7825
|
+
if (!existsSync6(gitignorePath)) {
|
|
7826
|
+
writeFileSync3(gitignorePath, KNOWLEDGE_GITIGNORE, "utf-8");
|
|
7838
7827
|
ctx.logger.info(`.gitignore created: ${gitignorePath}`);
|
|
7839
7828
|
}
|
|
7840
7829
|
if (!opts.dryRun) {
|
|
7841
|
-
const dbDir =
|
|
7842
|
-
if (!
|
|
7830
|
+
const dbDir = dirname3(ctx.paths.dbPath);
|
|
7831
|
+
if (!existsSync6(dbDir)) mkdirSync2(dbDir, { recursive: true });
|
|
7843
7832
|
const keyProvider = createKeyserverKeyProvider({ caveatHome: ctx.caveatHome });
|
|
7844
7833
|
const failures = await prewarmSealedKeys({ paths: ctx.paths, keyProvider });
|
|
7845
7834
|
for (const failure of failures) {
|
|
@@ -8017,7 +8006,7 @@ function reportEnvironmentSummary(ctx, publishTarget, codexHookState, dryRun) {
|
|
|
8017
8006
|
const prefix = dryRun ? "[dry-run] would have " : "";
|
|
8018
8007
|
const isRepo = gitOutput(["-C", ctx.paths.knowledgeRepo, "rev-parse", "--is-inside-work-tree"]) === "true";
|
|
8019
8008
|
const remote = isRepo ? gitOutput(["-C", ctx.paths.knowledgeRepo, "config", "--get", "remote.origin.url"]) : null;
|
|
8020
|
-
const communityCount =
|
|
8009
|
+
const communityCount = existsSync6(ctx.paths.communityDir) ? readdirSync(ctx.paths.communityDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).length : 0;
|
|
8021
8010
|
ctx.logger.info(`${prefix}environment summary:`);
|
|
8022
8011
|
ctx.logger.info(` own git: ${isRepo ? "repository" : "not initialized"}`);
|
|
8023
8012
|
ctx.logger.info(` private remote: ${remote || "not configured"}`);
|
|
@@ -8032,14 +8021,14 @@ function migrateLegacyCommunityDir(ctx) {
|
|
|
8032
8021
|
const legacy = join8(ctx.paths.knowledgeRepo, "community");
|
|
8033
8022
|
const current = ctx.paths.communityDir;
|
|
8034
8023
|
if (legacy === current) return;
|
|
8035
|
-
if (!
|
|
8036
|
-
if (
|
|
8024
|
+
if (!existsSync6(legacy)) return;
|
|
8025
|
+
if (existsSync6(current)) {
|
|
8037
8026
|
ctx.logger.warn(
|
|
8038
8027
|
`legacy community dir still exists at ${legacy} \u2014 remove manually (new location in use)`
|
|
8039
8028
|
);
|
|
8040
8029
|
return;
|
|
8041
8030
|
}
|
|
8042
|
-
|
|
8031
|
+
mkdirSync2(current, { recursive: true });
|
|
8043
8032
|
for (const entry of readdirSync(legacy, { withFileTypes: true })) {
|
|
8044
8033
|
if (!entry.isDirectory()) continue;
|
|
8045
8034
|
renameSync(join8(legacy, entry.name), join8(current, entry.name));
|
|
@@ -8104,11 +8093,11 @@ function reportInstallResult(ctx, result, dryRun) {
|
|
|
8104
8093
|
}
|
|
8105
8094
|
|
|
8106
8095
|
// src/commands/indexCmd.ts
|
|
8107
|
-
import { existsSync as
|
|
8108
|
-
import { dirname as
|
|
8096
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync3 } from "node:fs";
|
|
8097
|
+
import { dirname as dirname4 } from "node:path";
|
|
8109
8098
|
async function runIndex(ctx, opts) {
|
|
8110
|
-
const dbDir =
|
|
8111
|
-
if (!
|
|
8099
|
+
const dbDir = dirname4(ctx.paths.dbPath);
|
|
8100
|
+
if (!existsSync7(dbDir)) mkdirSync3(dbDir, { recursive: true });
|
|
8112
8101
|
const keyProvider = createKeyserverKeyProvider({ caveatHome: ctx.caveatHome });
|
|
8113
8102
|
const failures = await prewarmSealedKeys({ paths: ctx.paths, keyProvider });
|
|
8114
8103
|
for (const failure of failures) {
|
|
@@ -8292,7 +8281,7 @@ function runStats(ctx) {
|
|
|
8292
8281
|
|
|
8293
8282
|
// src/commands/serve.ts
|
|
8294
8283
|
async function runServe(opts) {
|
|
8295
|
-
const { startServer } = await import("./server-
|
|
8284
|
+
const { startServer } = await import("./server-GBVHOF67.js");
|
|
8296
8285
|
const { port, host } = startServer({ port: opts.port });
|
|
8297
8286
|
process.stdout.write(`[caveat] web portal: http://${host}:${port}/
|
|
8298
8287
|
`);
|
|
@@ -32532,12 +32521,12 @@ function handleListRecent(ctx, args) {
|
|
|
32532
32521
|
}
|
|
32533
32522
|
|
|
32534
32523
|
// ../mcp/dist/tools/pull.js
|
|
32535
|
-
import { existsSync as
|
|
32524
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
32536
32525
|
var pullInputShape = {};
|
|
32537
32526
|
async function handlePull(ctx, _args = {}) {
|
|
32538
32527
|
const pulled = [];
|
|
32539
32528
|
const indexed = [];
|
|
32540
|
-
if (
|
|
32529
|
+
if (existsSync8(ctx.paths.communityDir)) {
|
|
32541
32530
|
const results = await communityPull({
|
|
32542
32531
|
communityDir: ctx.paths.communityDir,
|
|
32543
32532
|
logger: ctx.logger
|
|
@@ -32693,28 +32682,28 @@ async function runMcpServer() {
|
|
|
32693
32682
|
import { spawn as spawn2 } from "node:child_process";
|
|
32694
32683
|
import {
|
|
32695
32684
|
chmodSync,
|
|
32696
|
-
existsSync as
|
|
32685
|
+
existsSync as existsSync11,
|
|
32697
32686
|
lstatSync,
|
|
32698
32687
|
mkdtempSync as mkdtempSync2,
|
|
32699
32688
|
mkdirSync as mkdirSync5,
|
|
32700
32689
|
readdirSync as readdirSync2,
|
|
32701
|
-
readFileSync as
|
|
32702
|
-
realpathSync as
|
|
32690
|
+
readFileSync as readFileSync6,
|
|
32691
|
+
realpathSync as realpathSync3,
|
|
32703
32692
|
rmSync as rmSync2,
|
|
32704
32693
|
writeFileSync as writeFileSync5
|
|
32705
32694
|
} from "node:fs";
|
|
32706
32695
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
32707
|
-
import { basename, dirname as
|
|
32708
|
-
import {
|
|
32696
|
+
import { basename, dirname as dirname5, join as join12 } from "node:path";
|
|
32697
|
+
import { randomBytes } from "node:crypto";
|
|
32709
32698
|
|
|
32710
32699
|
// src/autoReindexTrigger.ts
|
|
32711
32700
|
import { spawn } from "node:child_process";
|
|
32712
|
-
import { existsSync as
|
|
32701
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
32713
32702
|
import { join as join10 } from "node:path";
|
|
32714
32703
|
function maybeTriggerAutoReindex(ctx) {
|
|
32715
32704
|
if (process.env.CAVEAT_INDEX_AUTOSYNC === "off") return;
|
|
32716
|
-
if (!
|
|
32717
|
-
if (
|
|
32705
|
+
if (!existsSync9(ctx.paths.dbPath)) return;
|
|
32706
|
+
if (existsSync9(join10(ctx.caveatHome, "index", ".reindex-lock"))) return;
|
|
32718
32707
|
const current = computeEntriesDigest(ctx.paths);
|
|
32719
32708
|
const marker = readDigestMarker(ctx.caveatHome);
|
|
32720
32709
|
if (marker?.digest === current.digest && marker.fileCount === current.fileCount) return;
|
|
@@ -32739,18 +32728,22 @@ function maybeTriggerAutoSync(ctx, debounceMs = AUTO_SYNC_DEBOUNCE_MS) {
|
|
|
32739
32728
|
});
|
|
32740
32729
|
}
|
|
32741
32730
|
|
|
32742
|
-
// src/
|
|
32743
|
-
|
|
32744
|
-
|
|
32745
|
-
|
|
32746
|
-
|
|
32747
|
-
|
|
32748
|
-
|
|
32731
|
+
// src/hookShared.ts
|
|
32732
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
32733
|
+
import { join as join11 } from "node:path";
|
|
32734
|
+
import { createHash } from "node:crypto";
|
|
32735
|
+
var MAX_CONTEXT_BLOCKS = 3;
|
|
32736
|
+
var STOP_REMINDER_PREFIX = "[caveat] \u3053\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u3067\u5916\u90E8\u4ED5\u69D8\u306E\u7F60\u306B\u5F53\u305F\u3063\u305F\u53EF\u80FD\u6027\u3092\u793A\u3059\u30B7\u30B0\u30CA\u30EB:";
|
|
32737
|
+
function hookSilentLogger(host) {
|
|
32738
|
+
return {
|
|
32739
|
+
info: () => {
|
|
32740
|
+
},
|
|
32741
|
+
warn: () => {
|
|
32742
|
+
},
|
|
32743
|
+
error: (m) => process.stderr.write(`[${host.stderrTag}] ${m}
|
|
32749
32744
|
`)
|
|
32750
|
-
};
|
|
32751
|
-
|
|
32752
|
-
var CLAUDE_STOP_REMINDER_PREFIX = "[caveat] \u3053\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u3067\u5916\u90E8\u4ED5\u69D8\u306E\u7F60\u306B\u5F53\u305F\u3063\u305F\u53EF\u80FD\u6027\u3092\u793A\u3059\u30B7\u30B0\u30CA\u30EB:";
|
|
32753
|
-
var CLAUDE_STOP_STATE_DIR = "claude-stop-state";
|
|
32745
|
+
};
|
|
32746
|
+
}
|
|
32754
32747
|
async function readStdin() {
|
|
32755
32748
|
const chunks = [];
|
|
32756
32749
|
for await (const chunk of process.stdin) {
|
|
@@ -32758,34 +32751,32 @@ async function readStdin() {
|
|
|
32758
32751
|
}
|
|
32759
32752
|
return Buffer.concat(chunks).toString("utf-8");
|
|
32760
32753
|
}
|
|
32761
|
-
function
|
|
32754
|
+
function errorMessage4(err) {
|
|
32755
|
+
return err instanceof Error ? err.message : String(err);
|
|
32756
|
+
}
|
|
32757
|
+
function reportHookError(host, phase, err) {
|
|
32758
|
+
observeRuntimeError(host.errorCode, { version: CAVEAT_VERSION });
|
|
32759
|
+
process.stderr.write(`[${host.stderrTag}] ${phase}: ${errorMessage4(err)}
|
|
32760
|
+
`);
|
|
32761
|
+
}
|
|
32762
|
+
function parsePayload(host, raw) {
|
|
32762
32763
|
if (!raw) return {};
|
|
32763
32764
|
try {
|
|
32764
32765
|
return JSON.parse(raw);
|
|
32765
32766
|
} catch (err) {
|
|
32766
|
-
|
|
32767
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
32768
|
-
process.stderr.write(`[caveat:hook] json parse error: ${msg}
|
|
32769
|
-
`);
|
|
32767
|
+
reportHookError(host, "json parse error", err);
|
|
32770
32768
|
return {};
|
|
32771
32769
|
}
|
|
32772
32770
|
}
|
|
32773
|
-
function
|
|
32774
|
-
const v = payload.session_id ?? payload.sessionId;
|
|
32775
|
-
return typeof v === "string" && v.length > 0 ? v : "_unknown";
|
|
32776
|
-
}
|
|
32777
|
-
function buildContextSafely() {
|
|
32771
|
+
function buildContextSafely(host) {
|
|
32778
32772
|
try {
|
|
32779
|
-
return buildContext(
|
|
32773
|
+
return buildContext(hookSilentLogger(host));
|
|
32780
32774
|
} catch (err) {
|
|
32781
|
-
|
|
32782
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
32783
|
-
process.stderr.write(`[caveat:hook] context error: ${msg}
|
|
32784
|
-
`);
|
|
32775
|
+
reportHookError(host, "context error", err);
|
|
32785
32776
|
return null;
|
|
32786
32777
|
}
|
|
32787
32778
|
}
|
|
32788
|
-
function searchCaveatsSafely(input) {
|
|
32779
|
+
function searchCaveatsSafely(host, input) {
|
|
32789
32780
|
const inputs = Array.isArray(input) ? input : [input];
|
|
32790
32781
|
const queryForLog = inputs.map((item) => item.surface === "user_prompt" ? item.topicText || item.failureText : item.failureText).filter(Boolean).join("\n");
|
|
32791
32782
|
if (inputs.length === 0 || inputs.every((item) => !item.topicText && !item.failureText)) return [];
|
|
@@ -32793,8 +32784,8 @@ function searchCaveatsSafely(input) {
|
|
|
32793
32784
|
let caveatHome;
|
|
32794
32785
|
let hits;
|
|
32795
32786
|
try {
|
|
32796
|
-
const ctx = buildContextSafely();
|
|
32797
|
-
if (!ctx || !
|
|
32787
|
+
const ctx = buildContextSafely(host);
|
|
32788
|
+
if (!ctx || !existsSync10(ctx.paths.dbPath)) return [];
|
|
32798
32789
|
caveatHome = ctx.caveatHome;
|
|
32799
32790
|
db = openDb({ path: ctx.paths.dbPath });
|
|
32800
32791
|
const searchOptions = {
|
|
@@ -32802,29 +32793,20 @@ function searchCaveatsSafely(input) {
|
|
|
32802
32793
|
};
|
|
32803
32794
|
hits = inputs.length === 1 ? findCaveatsForHook(db, inputs[0], searchOptions) : findCaveatsForHookSegments(db, inputs, searchOptions);
|
|
32804
32795
|
} catch (err) {
|
|
32805
|
-
|
|
32806
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
32807
|
-
process.stderr.write(`[caveat:hook] search error: ${msg}
|
|
32808
|
-
`);
|
|
32796
|
+
reportHookError(host, "search error", err);
|
|
32809
32797
|
return [];
|
|
32810
32798
|
}
|
|
32811
32799
|
if (hits.length > 0) {
|
|
32812
32800
|
try {
|
|
32813
32801
|
markHit(db, hits);
|
|
32814
32802
|
} catch (err) {
|
|
32815
|
-
|
|
32816
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
32817
|
-
process.stderr.write(`[caveat:hook] markHit error: ${msg}
|
|
32818
|
-
`);
|
|
32803
|
+
reportHookError(host, "markHit error", err);
|
|
32819
32804
|
}
|
|
32820
32805
|
} else {
|
|
32821
32806
|
try {
|
|
32822
|
-
logHookQueryMiss({ caveatHome, agent:
|
|
32807
|
+
logHookQueryMiss({ caveatHome, agent: host.agent, surface: inputs[0].surface, query: queryForLog });
|
|
32823
32808
|
} catch (err) {
|
|
32824
|
-
|
|
32825
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
32826
|
-
process.stderr.write(`[caveat:hook] query log error: ${msg}
|
|
32827
|
-
`);
|
|
32809
|
+
reportHookError(host, "query log error", err);
|
|
32828
32810
|
}
|
|
32829
32811
|
}
|
|
32830
32812
|
try {
|
|
@@ -32833,50 +32815,37 @@ function searchCaveatsSafely(input) {
|
|
|
32833
32815
|
db?.close();
|
|
32834
32816
|
}
|
|
32835
32817
|
}
|
|
32836
|
-
function
|
|
32837
|
-
|
|
32838
|
-
return readSessionSignals(path);
|
|
32839
|
-
} catch (err) {
|
|
32840
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
32841
|
-
process.stderr.write(`[caveat:hook] transcript read error: ${msg}
|
|
32842
|
-
`);
|
|
32843
|
-
return null;
|
|
32844
|
-
}
|
|
32845
|
-
}
|
|
32846
|
-
function systemReminderOutput(text) {
|
|
32847
|
-
return `<system-reminder>${text.replace(/</g, "\u2039").replace(/>/g, "\u203A")}</system-reminder>`;
|
|
32818
|
+
function pendingCleanupFailureText(host) {
|
|
32819
|
+
return `[${host.stderrTag}] pending reminder cleanup failed`;
|
|
32848
32820
|
}
|
|
32849
|
-
function
|
|
32850
|
-
|
|
32851
|
-
}
|
|
32852
|
-
function drainForSession(sessionId) {
|
|
32853
|
-
const ctx = buildContextSafely();
|
|
32821
|
+
function drainForSession(host, sessionId) {
|
|
32822
|
+
const ctx = buildContextSafely(host);
|
|
32854
32823
|
if (!ctx) return [];
|
|
32855
32824
|
const local = drainPendingRemindersDetailed(ctx.caveatHome, sessionId);
|
|
32856
32825
|
const global = drainPendingRemindersDetailed(ctx.caveatHome, "_global");
|
|
32857
32826
|
for (const _failure of [...local.cleanupFailures, ...global.cleanupFailures]) {
|
|
32858
|
-
process.stderr.write(`${
|
|
32827
|
+
process.stderr.write(`${pendingCleanupFailureText(host)}
|
|
32859
32828
|
`);
|
|
32860
32829
|
}
|
|
32861
32830
|
return [...local.reminders, ...global.reminders];
|
|
32862
32831
|
}
|
|
32863
|
-
function
|
|
32864
|
-
if (text.startsWith(
|
|
32832
|
+
function contextDedupeKey(host, text) {
|
|
32833
|
+
if (text.startsWith(STOP_REMINDER_PREFIX)) return host.stopDedupeKey;
|
|
32865
32834
|
return text.trim();
|
|
32866
32835
|
}
|
|
32867
|
-
function
|
|
32836
|
+
function compactContexts(host, contexts) {
|
|
32868
32837
|
const selected = [];
|
|
32869
32838
|
const seen = /* @__PURE__ */ new Set();
|
|
32870
32839
|
for (let i = contexts.length - 1; i >= 0; i -= 1) {
|
|
32871
32840
|
const text = contexts[i]?.trim();
|
|
32872
32841
|
if (!text) continue;
|
|
32873
|
-
const key =
|
|
32842
|
+
const key = contextDedupeKey(host, text);
|
|
32874
32843
|
if (seen.has(key)) continue;
|
|
32875
32844
|
seen.add(key);
|
|
32876
32845
|
selected.push(text);
|
|
32877
32846
|
}
|
|
32878
32847
|
selected.reverse();
|
|
32879
|
-
const limited = selected.slice(-
|
|
32848
|
+
const limited = selected.slice(-MAX_CONTEXT_BLOCKS);
|
|
32880
32849
|
const omitted = selected.length - limited.length;
|
|
32881
32850
|
if (omitted > 0) {
|
|
32882
32851
|
limited.push(
|
|
@@ -32885,7 +32854,7 @@ function compactClaudeContexts(contexts) {
|
|
|
32885
32854
|
}
|
|
32886
32855
|
return limited;
|
|
32887
32856
|
}
|
|
32888
|
-
function
|
|
32857
|
+
function sanitizeStateId(raw) {
|
|
32889
32858
|
const clean = raw.replace(/[^A-Za-z0-9_-]/g, "");
|
|
32890
32859
|
return clean.length > 0 ? clean : "_unknown";
|
|
32891
32860
|
}
|
|
@@ -32901,59 +32870,59 @@ function stopSignalKey(signals, related) {
|
|
|
32901
32870
|
});
|
|
32902
32871
|
return createHash("sha256").update(body).digest("hex");
|
|
32903
32872
|
}
|
|
32904
|
-
function stopStatePath(caveatHome, sessionId) {
|
|
32905
|
-
return join11(caveatHome,
|
|
32873
|
+
function stopStatePath(host, caveatHome, sessionId) {
|
|
32874
|
+
return join11(caveatHome, host.stopStateDir, `${sanitizeStateId(sessionId)}.txt`);
|
|
32906
32875
|
}
|
|
32907
|
-
function wasStopReminderQueued(caveatHome, sessionId, key) {
|
|
32908
|
-
const path = stopStatePath(caveatHome, sessionId);
|
|
32876
|
+
function wasStopReminderQueued(host, caveatHome, sessionId, key) {
|
|
32877
|
+
const path = stopStatePath(host, caveatHome, sessionId);
|
|
32909
32878
|
try {
|
|
32910
32879
|
return readFileSync5(path, "utf-8") === key;
|
|
32911
32880
|
} catch {
|
|
32912
32881
|
return false;
|
|
32913
32882
|
}
|
|
32914
32883
|
}
|
|
32915
|
-
function markStopReminderQueued(caveatHome, sessionId, key) {
|
|
32916
|
-
const path = stopStatePath(caveatHome, sessionId);
|
|
32917
|
-
|
|
32918
|
-
|
|
32884
|
+
function markStopReminderQueued(host, caveatHome, sessionId, key) {
|
|
32885
|
+
const path = stopStatePath(host, caveatHome, sessionId);
|
|
32886
|
+
mkdirSync4(join11(caveatHome, host.stopStateDir), { recursive: true });
|
|
32887
|
+
writeFileSync4(path, key, "utf-8");
|
|
32919
32888
|
}
|
|
32920
|
-
function queueStopForSession(sessionId, signals, related) {
|
|
32921
|
-
const ctx = buildContextSafely();
|
|
32889
|
+
function queueStopForSession(host, sessionId, signals, related, buildText) {
|
|
32890
|
+
const ctx = buildContextSafely(host);
|
|
32922
32891
|
if (!ctx) return;
|
|
32923
32892
|
const key = stopSignalKey(signals, related);
|
|
32924
|
-
if (wasStopReminderQueued(ctx.caveatHome, sessionId, key)) return;
|
|
32893
|
+
if (wasStopReminderQueued(host, ctx.caveatHome, sessionId, key)) return;
|
|
32925
32894
|
let result;
|
|
32926
32895
|
try {
|
|
32927
32896
|
result = buildAndPublishPendingReminder(ctx.caveatHome, sessionId, buildPendingSemanticKey({
|
|
32928
|
-
agent:
|
|
32897
|
+
agent: host.agent,
|
|
32929
32898
|
surface: "stop",
|
|
32930
32899
|
refs: related,
|
|
32931
32900
|
stopSignalDigest: key
|
|
32932
|
-
}),
|
|
32901
|
+
}), buildText);
|
|
32933
32902
|
} catch {
|
|
32934
|
-
process.stderr.write(
|
|
32903
|
+
process.stderr.write(`[${host.stderrTag}] pending reminder build or publish failed
|
|
32904
|
+
`);
|
|
32935
32905
|
return;
|
|
32936
32906
|
}
|
|
32937
32907
|
if (!result.ran) return;
|
|
32938
32908
|
try {
|
|
32939
|
-
markStopReminderQueued(ctx.caveatHome, sessionId, key);
|
|
32909
|
+
markStopReminderQueued(host, ctx.caveatHome, sessionId, key);
|
|
32940
32910
|
} catch (err) {
|
|
32941
|
-
|
|
32942
|
-
process.stderr.write(`[caveat:hook] pending reminder write error: ${msg}
|
|
32911
|
+
process.stderr.write(`[${host.stderrTag}] pending reminder write error: ${errorMessage4(err)}
|
|
32943
32912
|
`);
|
|
32944
32913
|
}
|
|
32945
32914
|
}
|
|
32946
32915
|
function extractToolResponseText(response) {
|
|
32947
32916
|
if (typeof response === "string") return response;
|
|
32948
32917
|
if (Array.isArray(response)) {
|
|
32949
|
-
|
|
32950
|
-
|
|
32951
|
-
if (typeof item === "
|
|
32952
|
-
|
|
32953
|
-
|
|
32918
|
+
return response.map((item) => {
|
|
32919
|
+
if (typeof item === "string") return item;
|
|
32920
|
+
if (item !== null && typeof item === "object") {
|
|
32921
|
+
const text = item.text;
|
|
32922
|
+
return typeof text === "string" ? text : "";
|
|
32954
32923
|
}
|
|
32955
|
-
|
|
32956
|
-
|
|
32924
|
+
return "";
|
|
32925
|
+
}).filter(Boolean).join(" ");
|
|
32957
32926
|
}
|
|
32958
32927
|
if (response !== null && typeof response === "object") {
|
|
32959
32928
|
const r = response;
|
|
@@ -32966,6 +32935,32 @@ function extractToolResponseText(response) {
|
|
|
32966
32935
|
}
|
|
32967
32936
|
return "";
|
|
32968
32937
|
}
|
|
32938
|
+
|
|
32939
|
+
// src/commands/hookCmd.ts
|
|
32940
|
+
var CLAUDE_HOST = {
|
|
32941
|
+
agent: "claude",
|
|
32942
|
+
stderrTag: "caveat:hook",
|
|
32943
|
+
errorCode: "CAVEAT.CLAUDE_HOOK_FAILED",
|
|
32944
|
+
stopStateDir: "claude-stop-state",
|
|
32945
|
+
stopDedupeKey: "claude-stop-reminder"
|
|
32946
|
+
};
|
|
32947
|
+
var silentLogger = hookSilentLogger(CLAUDE_HOST);
|
|
32948
|
+
function getSessionId(payload) {
|
|
32949
|
+
const v = payload.session_id ?? payload.sessionId;
|
|
32950
|
+
return typeof v === "string" && v.length > 0 ? v : "_unknown";
|
|
32951
|
+
}
|
|
32952
|
+
function loadSignalsSafely(path) {
|
|
32953
|
+
try {
|
|
32954
|
+
return readSessionSignals(path);
|
|
32955
|
+
} catch (err) {
|
|
32956
|
+
process.stderr.write(`[caveat:hook] transcript read error: ${errorMessage4(err)}
|
|
32957
|
+
`);
|
|
32958
|
+
return null;
|
|
32959
|
+
}
|
|
32960
|
+
}
|
|
32961
|
+
function systemReminderOutput(text) {
|
|
32962
|
+
return `<system-reminder>${text.replace(/</g, "\u2039").replace(/>/g, "\u203A")}</system-reminder>`;
|
|
32963
|
+
}
|
|
32969
32964
|
function toolTopicText(payload) {
|
|
32970
32965
|
const parts = [];
|
|
32971
32966
|
const toolName = payload.tool_name ?? payload.toolName;
|
|
@@ -33000,15 +32995,14 @@ function spawnWorker(job) {
|
|
|
33000
32995
|
try {
|
|
33001
32996
|
root = workerRoot();
|
|
33002
32997
|
sweepStaleWorkerDirs(Date.now(), root);
|
|
33003
|
-
workDir = mkdtempSync2(
|
|
32998
|
+
workDir = mkdtempSync2(join12(root, "job-"));
|
|
33004
32999
|
chmodSync(workDir, 448);
|
|
33005
|
-
workFile =
|
|
33000
|
+
workFile = join12(workDir, `${randomBytes(4).toString("hex")}.json`);
|
|
33006
33001
|
writeFileSync5(workFile, JSON.stringify({ ...job, schemaVersion: "caveat-worker-job/v2" }), { encoding: "utf-8", mode: 384, flag: "wx" });
|
|
33007
33002
|
} catch (err) {
|
|
33008
|
-
|
|
33009
|
-
process.stderr.write(`[caveat:hook] worker writefile error: ${msg}
|
|
33003
|
+
process.stderr.write(`[caveat:hook] worker writefile error: ${errorMessage4(err)}
|
|
33010
33004
|
`);
|
|
33011
|
-
cleanupWorkerDir(workDir, workDir ?
|
|
33005
|
+
cleanupWorkerDir(workDir, workDir ? dirname5(workDir) : void 0);
|
|
33012
33006
|
return;
|
|
33013
33007
|
}
|
|
33014
33008
|
const cliScript = process.argv[1];
|
|
@@ -33024,8 +33018,7 @@ function spawnWorker(job) {
|
|
|
33024
33018
|
);
|
|
33025
33019
|
child.unref();
|
|
33026
33020
|
} catch (err) {
|
|
33027
|
-
|
|
33028
|
-
process.stderr.write(`[caveat:hook] worker spawn error: ${msg}
|
|
33021
|
+
process.stderr.write(`[caveat:hook] worker spawn error: ${errorMessage4(err)}
|
|
33029
33022
|
`);
|
|
33030
33023
|
try {
|
|
33031
33024
|
cleanupWorkerDir(workDir, root);
|
|
@@ -33036,11 +33029,11 @@ function spawnWorker(job) {
|
|
|
33036
33029
|
async function runWorker(workFile) {
|
|
33037
33030
|
let raw;
|
|
33038
33031
|
try {
|
|
33039
|
-
raw =
|
|
33032
|
+
raw = readFileSync6(workFile, "utf-8");
|
|
33040
33033
|
} catch {
|
|
33041
33034
|
process.exit(0);
|
|
33042
33035
|
}
|
|
33043
|
-
cleanupWorkerDir(
|
|
33036
|
+
cleanupWorkerDir(dirname5(workFile), dirname5(dirname5(workFile)));
|
|
33044
33037
|
let job;
|
|
33045
33038
|
try {
|
|
33046
33039
|
job = JSON.parse(raw);
|
|
@@ -33048,13 +33041,13 @@ async function runWorker(workFile) {
|
|
|
33048
33041
|
process.exit(0);
|
|
33049
33042
|
}
|
|
33050
33043
|
if (!job.failureText || !job.sessionId) process.exit(0);
|
|
33051
|
-
const hits = searchCaveatsSafely({
|
|
33044
|
+
const hits = searchCaveatsSafely(CLAUDE_HOST, {
|
|
33052
33045
|
topicText: job.topicText,
|
|
33053
33046
|
failureText: job.failureText,
|
|
33054
33047
|
surface: "tool_error"
|
|
33055
33048
|
});
|
|
33056
33049
|
if (hits.length === 0) process.exit(0);
|
|
33057
|
-
const ctx = buildContextSafely();
|
|
33050
|
+
const ctx = buildContextSafely(CLAUDE_HOST);
|
|
33058
33051
|
if (!ctx) process.exit(0);
|
|
33059
33052
|
let result;
|
|
33060
33053
|
try {
|
|
@@ -33081,11 +33074,11 @@ function isOwnedWorkerDir(path, root = workerRoot()) {
|
|
|
33081
33074
|
try {
|
|
33082
33075
|
const inputStat = lstatSync(path);
|
|
33083
33076
|
if (inputStat.isSymbolicLink()) return false;
|
|
33084
|
-
const tmpRoot =
|
|
33085
|
-
const resolved =
|
|
33077
|
+
const tmpRoot = realpathSync3(root);
|
|
33078
|
+
const resolved = realpathSync3(path);
|
|
33086
33079
|
const stat = lstatSync(resolved);
|
|
33087
33080
|
const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
|
|
33088
|
-
return
|
|
33081
|
+
return dirname5(resolved) === tmpRoot && basename(resolved).startsWith("job-") && stat.isDirectory() && !stat.isSymbolicLink() && hasPrivateOwnership(stat, uid);
|
|
33089
33082
|
} catch {
|
|
33090
33083
|
return false;
|
|
33091
33084
|
}
|
|
@@ -33100,7 +33093,7 @@ function sweepStaleWorkerDirs(now = Date.now(), root = workerRoot()) {
|
|
|
33100
33093
|
}
|
|
33101
33094
|
for (const entry of entries) {
|
|
33102
33095
|
if (!entry.startsWith("job-")) continue;
|
|
33103
|
-
const path =
|
|
33096
|
+
const path = join12(root, entry);
|
|
33104
33097
|
try {
|
|
33105
33098
|
if (!isOwnedWorkerDir(path, root)) continue;
|
|
33106
33099
|
const stat = lstatSync(path);
|
|
@@ -33112,32 +33105,32 @@ function sweepStaleWorkerDirs(now = Date.now(), root = workerRoot()) {
|
|
|
33112
33105
|
}
|
|
33113
33106
|
}
|
|
33114
33107
|
function workerRoot(base = tmpdir2()) {
|
|
33115
|
-
const root =
|
|
33108
|
+
const root = join12(base, WORKER_ROOT);
|
|
33116
33109
|
mkdirSync5(root, { recursive: true, mode: 448 });
|
|
33117
33110
|
const stat = lstatSync(root);
|
|
33118
33111
|
const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
|
|
33119
33112
|
if (!stat.isDirectory() || stat.isSymbolicLink() || !hasPrivateOwnership(stat, uid)) throw new Error("worker root is unsafe");
|
|
33120
|
-
const marker =
|
|
33113
|
+
const marker = join12(root, WORKER_MARKER);
|
|
33121
33114
|
try {
|
|
33122
33115
|
writeFileSync5(marker, "caveat-worker/v1\n", { mode: 384, flag: "wx" });
|
|
33123
33116
|
} catch (error51) {
|
|
33124
33117
|
if (!(error51 && typeof error51 === "object" && "code" in error51 && error51.code === "EEXIST")) throw error51;
|
|
33125
33118
|
}
|
|
33126
33119
|
const markerStat = lstatSync(marker);
|
|
33127
|
-
if (!markerStat.isFile() || markerStat.isSymbolicLink() || !hasPrivateOwnership(markerStat, uid) ||
|
|
33120
|
+
if (!markerStat.isFile() || markerStat.isSymbolicLink() || !hasPrivateOwnership(markerStat, uid) || readFileSync6(marker, "utf-8") !== "caveat-worker/v1\n") throw new Error("worker root marker is invalid");
|
|
33128
33121
|
return root;
|
|
33129
33122
|
}
|
|
33130
33123
|
function isStaleWorkerJobDir(path) {
|
|
33131
33124
|
const entries = readdirSync2(path);
|
|
33132
33125
|
if (entries.length !== 1 || !entries[0].endsWith(".json")) return false;
|
|
33133
|
-
const file2 =
|
|
33126
|
+
const file2 = join12(path, entries[0]);
|
|
33134
33127
|
const stat = lstatSync(file2);
|
|
33135
33128
|
const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
|
|
33136
33129
|
if (!stat.isFile() || stat.isSymbolicLink() || !hasPrivateOwnership(stat, uid)) return false;
|
|
33137
|
-
return isKnownStaleWorkerJob(JSON.parse(
|
|
33130
|
+
return isKnownStaleWorkerJob(JSON.parse(readFileSync6(file2, "utf-8")));
|
|
33138
33131
|
}
|
|
33139
33132
|
function hasPrivateOwnership(stat, uid) {
|
|
33140
|
-
return
|
|
33133
|
+
return isPrivateOwnerStat(stat, uid);
|
|
33141
33134
|
}
|
|
33142
33135
|
function isWorkerJob(value) {
|
|
33143
33136
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
@@ -33156,23 +33149,19 @@ function isKnownStaleWorkerJob(value) {
|
|
|
33156
33149
|
}
|
|
33157
33150
|
function writeLastReindex(caveatHome, value) {
|
|
33158
33151
|
try {
|
|
33159
|
-
writeFileSync5(
|
|
33152
|
+
writeFileSync5(join12(caveatHome, "index", ".last-reindex.json"), JSON.stringify(value), "utf-8");
|
|
33160
33153
|
} catch (err) {
|
|
33161
|
-
|
|
33162
|
-
process.stderr.write(`[caveat:hook] reindex status write error: ${msg}
|
|
33154
|
+
process.stderr.write(`[caveat:hook] reindex status write error: ${errorMessage4(err)}
|
|
33163
33155
|
`);
|
|
33164
33156
|
}
|
|
33165
33157
|
}
|
|
33166
|
-
function errorMessage4(err) {
|
|
33167
|
-
return err instanceof Error ? err.message : String(err);
|
|
33168
|
-
}
|
|
33169
33158
|
async function runReindexWorker() {
|
|
33170
33159
|
if (process.env.CAVEAT_INDEX_AUTOSYNC === "off") {
|
|
33171
33160
|
process.stderr.write("[caveat:hook] auto reindex disabled by CAVEAT_INDEX_AUTOSYNC=off\n");
|
|
33172
33161
|
return;
|
|
33173
33162
|
}
|
|
33174
|
-
const ctx = buildContextSafely();
|
|
33175
|
-
if (!ctx || !
|
|
33163
|
+
const ctx = buildContextSafely(CLAUDE_HOST);
|
|
33164
|
+
if (!ctx || !existsSync11(ctx.paths.dbPath)) {
|
|
33176
33165
|
process.stderr.write("[caveat:hook] auto reindex skipped: index database does not exist\n");
|
|
33177
33166
|
return;
|
|
33178
33167
|
}
|
|
@@ -33197,7 +33186,7 @@ async function runReindexWorker() {
|
|
|
33197
33186
|
perSource: result.perSource
|
|
33198
33187
|
});
|
|
33199
33188
|
} catch (err) {
|
|
33200
|
-
const msg =
|
|
33189
|
+
const msg = errorMessage4(err);
|
|
33201
33190
|
process.stderr.write(`[caveat:hook] reindex error: ${msg}
|
|
33202
33191
|
`);
|
|
33203
33192
|
writeLastReindex(ctx.caveatHome, {
|
|
@@ -33210,8 +33199,7 @@ async function runReindexWorker() {
|
|
|
33210
33199
|
try {
|
|
33211
33200
|
releaseReindexLock(lock);
|
|
33212
33201
|
} catch (err) {
|
|
33213
|
-
|
|
33214
|
-
process.stderr.write(`[caveat:hook] reindex lock release error: ${msg}
|
|
33202
|
+
process.stderr.write(`[caveat:hook] reindex lock release error: ${errorMessage4(err)}
|
|
33215
33203
|
`);
|
|
33216
33204
|
}
|
|
33217
33205
|
}
|
|
@@ -33221,7 +33209,7 @@ async function runAutoSyncWorker() {
|
|
|
33221
33209
|
process.stderr.write("[caveat:hook] auto sync disabled by CAVEAT_AUTO_SYNC=off\n");
|
|
33222
33210
|
return;
|
|
33223
33211
|
}
|
|
33224
|
-
const ctx = buildContextSafely();
|
|
33212
|
+
const ctx = buildContextSafely(CLAUDE_HOST);
|
|
33225
33213
|
if (!ctx) return;
|
|
33226
33214
|
try {
|
|
33227
33215
|
await runAutoSync({
|
|
@@ -33240,7 +33228,7 @@ function buildToolErrorReminder(job, hits) {
|
|
|
33240
33228
|
const mode = hookCodexSidecarMode();
|
|
33241
33229
|
if (mode === "off") return base;
|
|
33242
33230
|
const projectRoot = process.cwd();
|
|
33243
|
-
const hasSidecarConfig =
|
|
33231
|
+
const hasSidecarConfig = existsSync11(join12(projectRoot, ".codex-sidecar.yml"));
|
|
33244
33232
|
if (mode === "auto" && !hasSidecarConfig) return base;
|
|
33245
33233
|
const advisory = runCodexSidecarAdvisory({
|
|
33246
33234
|
searchText: job.failureText,
|
|
@@ -33276,7 +33264,7 @@ function buildStopReminder(signals, related) {
|
|
|
33276
33264
|
const mode = hookCodexSidecarMode();
|
|
33277
33265
|
if (mode === "off") return base;
|
|
33278
33266
|
const projectRoot = process.cwd();
|
|
33279
|
-
const hasSidecarConfig =
|
|
33267
|
+
const hasSidecarConfig = existsSync11(join12(projectRoot, ".codex-sidecar.yml"));
|
|
33280
33268
|
if (mode === "auto" && !hasSidecarConfig) return base;
|
|
33281
33269
|
const advisory = runCodexSidecarAdvisory({
|
|
33282
33270
|
searchText: struggleSearchText(signals),
|
|
@@ -33333,21 +33321,20 @@ async function runHook(name, arg) {
|
|
|
33333
33321
|
try {
|
|
33334
33322
|
raw = await readStdin();
|
|
33335
33323
|
} catch (err) {
|
|
33336
|
-
|
|
33337
|
-
process.stderr.write(`[caveat:hook] stdin read error: ${msg}
|
|
33324
|
+
process.stderr.write(`[caveat:hook] stdin read error: ${errorMessage4(err)}
|
|
33338
33325
|
`);
|
|
33339
33326
|
process.exit(0);
|
|
33340
33327
|
}
|
|
33341
|
-
const payload = parsePayload(raw);
|
|
33328
|
+
const payload = parsePayload(CLAUDE_HOST, raw);
|
|
33342
33329
|
const sessionId = getSessionId(payload);
|
|
33343
|
-
const contexts = name === "stop" ? [] : drainForSession(sessionId);
|
|
33330
|
+
const contexts = name === "stop" ? [] : drainForSession(CLAUDE_HOST, sessionId);
|
|
33344
33331
|
if (name === "user-prompt-submit") {
|
|
33345
33332
|
const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
|
|
33346
|
-
const hits = searchCaveatsSafely({ topicText: prompt, failureText: prompt, surface: "user_prompt" });
|
|
33333
|
+
const hits = searchCaveatsSafely(CLAUDE_HOST, { topicText: prompt, failureText: prompt, surface: "user_prompt" });
|
|
33347
33334
|
if (hits.length > 0) {
|
|
33348
33335
|
contexts.push(userPromptSubmitReminderText(hits));
|
|
33349
33336
|
}
|
|
33350
|
-
const compacted =
|
|
33337
|
+
const compacted = compactContexts(CLAUDE_HOST, contexts);
|
|
33351
33338
|
if (compacted.length > 0) {
|
|
33352
33339
|
process.stdout.write(`${systemReminderOutput(compacted.join("\n\n"))}
|
|
33353
33340
|
`);
|
|
@@ -33355,7 +33342,7 @@ async function runHook(name, arg) {
|
|
|
33355
33342
|
process.exit(0);
|
|
33356
33343
|
}
|
|
33357
33344
|
if (name === "post-tool-use") {
|
|
33358
|
-
const compacted =
|
|
33345
|
+
const compacted = compactContexts(CLAUDE_HOST, contexts);
|
|
33359
33346
|
if (compacted.length > 0) {
|
|
33360
33347
|
process.stdout.write(`${systemReminderOutput(compacted.join("\n\n"))}
|
|
33361
33348
|
`);
|
|
@@ -33377,27 +33364,24 @@ async function runHook(name, arg) {
|
|
|
33377
33364
|
process.exit(0);
|
|
33378
33365
|
}
|
|
33379
33366
|
if (name === "stop") {
|
|
33380
|
-
const ctx = buildContextSafely();
|
|
33367
|
+
const ctx = buildContextSafely(CLAUDE_HOST);
|
|
33381
33368
|
if (ctx) {
|
|
33382
33369
|
try {
|
|
33383
33370
|
maybeSweepPendingDirs(ctx.caveatHome);
|
|
33384
33371
|
} catch (err) {
|
|
33385
|
-
|
|
33386
|
-
process.stderr.write(`[caveat:hook] pending sweep error: ${msg}
|
|
33372
|
+
process.stderr.write(`[caveat:hook] pending sweep error: ${errorMessage4(err)}
|
|
33387
33373
|
`);
|
|
33388
33374
|
}
|
|
33389
33375
|
try {
|
|
33390
33376
|
maybeTriggerAutoReindex(ctx);
|
|
33391
33377
|
} catch (err) {
|
|
33392
|
-
|
|
33393
|
-
process.stderr.write(`[caveat:hook] auto reindex trigger error: ${msg}
|
|
33378
|
+
process.stderr.write(`[caveat:hook] auto reindex trigger error: ${errorMessage4(err)}
|
|
33394
33379
|
`);
|
|
33395
33380
|
}
|
|
33396
33381
|
try {
|
|
33397
33382
|
maybeTriggerAutoSync(ctx);
|
|
33398
33383
|
} catch (err) {
|
|
33399
|
-
|
|
33400
|
-
process.stderr.write(`[caveat:hook] auto sync trigger error: ${msg}
|
|
33384
|
+
process.stderr.write(`[caveat:hook] auto sync trigger error: ${errorMessage4(err)}
|
|
33401
33385
|
`);
|
|
33402
33386
|
}
|
|
33403
33387
|
}
|
|
@@ -33405,12 +33389,12 @@ async function runHook(name, arg) {
|
|
|
33405
33389
|
const transcriptPath = typeof payload.transcript_path === "string" ? payload.transcript_path : "";
|
|
33406
33390
|
const signals = transcriptPath ? loadSignalsSafely(transcriptPath) : null;
|
|
33407
33391
|
if (!signals || !hasAnyStruggleSignal(signals)) process.exit(0);
|
|
33408
|
-
const related = searchCaveatsSafely(signals.errorSnippets.map((failureText) => ({
|
|
33392
|
+
const related = searchCaveatsSafely(CLAUDE_HOST, signals.errorSnippets.map((failureText) => ({
|
|
33409
33393
|
topicText: "",
|
|
33410
33394
|
failureText,
|
|
33411
33395
|
surface: "stop"
|
|
33412
33396
|
})));
|
|
33413
|
-
queueStopForSession(sessionId, signals, related);
|
|
33397
|
+
queueStopForSession(CLAUDE_HOST, sessionId, signals, related, () => buildStopReminder(signals, related));
|
|
33414
33398
|
process.exit(0);
|
|
33415
33399
|
}
|
|
33416
33400
|
process.stderr.write(`[caveat:hook] unknown hook name: ${name}
|
|
@@ -33420,105 +33404,22 @@ async function runHook(name, arg) {
|
|
|
33420
33404
|
|
|
33421
33405
|
// src/commands/codexHookCmd.ts
|
|
33422
33406
|
import { spawn as spawn3, spawnSync as spawnSync5 } from "node:child_process";
|
|
33423
|
-
import { existsSync as
|
|
33407
|
+
import { existsSync as existsSync12, readFileSync as readFileSync7, unlinkSync, writeFileSync as writeFileSync6 } from "node:fs";
|
|
33424
33408
|
import { homedir as homedir3, tmpdir as tmpdir3 } from "node:os";
|
|
33425
|
-
import { join as
|
|
33426
|
-
import {
|
|
33427
|
-
var
|
|
33428
|
-
|
|
33429
|
-
|
|
33430
|
-
|
|
33431
|
-
|
|
33432
|
-
|
|
33433
|
-
`)
|
|
33409
|
+
import { join as join13 } from "node:path";
|
|
33410
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
33411
|
+
var CODEX_HOST = {
|
|
33412
|
+
agent: "codex",
|
|
33413
|
+
stderrTag: "caveat:codex-hook",
|
|
33414
|
+
errorCode: "CAVEAT.CODEX_HOOK_FAILED",
|
|
33415
|
+
stopStateDir: "codex-stop-state",
|
|
33416
|
+
stopDedupeKey: "codex-stop-reminder"
|
|
33434
33417
|
};
|
|
33435
|
-
var CODEX_MAX_CONTEXT_BLOCKS = 3;
|
|
33436
|
-
var CODEX_STOP_REMINDER_PREFIX = "[caveat] \u3053\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u3067\u5916\u90E8\u4ED5\u69D8\u306E\u7F60\u306B\u5F53\u305F\u3063\u305F\u53EF\u80FD\u6027\u3092\u793A\u3059\u30B7\u30B0\u30CA\u30EB:";
|
|
33437
|
-
var CODEX_STOP_STATE_DIR = "codex-stop-state";
|
|
33438
|
-
async function readStdin2() {
|
|
33439
|
-
const chunks = [];
|
|
33440
|
-
for await (const chunk of process.stdin) {
|
|
33441
|
-
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
33442
|
-
}
|
|
33443
|
-
return Buffer.concat(chunks).toString("utf-8");
|
|
33444
|
-
}
|
|
33445
|
-
function parsePayload2(raw) {
|
|
33446
|
-
if (!raw) return {};
|
|
33447
|
-
try {
|
|
33448
|
-
return JSON.parse(raw);
|
|
33449
|
-
} catch (err) {
|
|
33450
|
-
observeRuntimeError("CAVEAT.CODEX_HOOK_FAILED", { version: CAVEAT_VERSION });
|
|
33451
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
33452
|
-
process.stderr.write(`[caveat:codex-hook] json parse error: ${msg}
|
|
33453
|
-
`);
|
|
33454
|
-
return {};
|
|
33455
|
-
}
|
|
33456
|
-
}
|
|
33457
|
-
function buildContextSafely2() {
|
|
33458
|
-
try {
|
|
33459
|
-
return buildContext(silentLogger2);
|
|
33460
|
-
} catch (err) {
|
|
33461
|
-
observeRuntimeError("CAVEAT.CODEX_HOOK_FAILED", { version: CAVEAT_VERSION });
|
|
33462
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
33463
|
-
process.stderr.write(`[caveat:codex-hook] context error: ${msg}
|
|
33464
|
-
`);
|
|
33465
|
-
return null;
|
|
33466
|
-
}
|
|
33467
|
-
}
|
|
33468
|
-
function searchCaveatsSafely2(input) {
|
|
33469
|
-
const inputs = Array.isArray(input) ? input : [input];
|
|
33470
|
-
const queryForLog = inputs.map((item) => item.surface === "user_prompt" ? item.topicText || item.failureText : item.failureText).filter(Boolean).join("\n");
|
|
33471
|
-
if (inputs.length === 0 || inputs.every((item) => !item.topicText && !item.failureText)) return [];
|
|
33472
|
-
let db;
|
|
33473
|
-
let caveatHome;
|
|
33474
|
-
let hits;
|
|
33475
|
-
try {
|
|
33476
|
-
const ctx = buildContextSafely2();
|
|
33477
|
-
if (!ctx || !existsSync10(ctx.paths.dbPath)) return [];
|
|
33478
|
-
caveatHome = ctx.caveatHome;
|
|
33479
|
-
db = openDb({ path: ctx.paths.dbPath });
|
|
33480
|
-
const searchOptions = {
|
|
33481
|
-
selfIdentity: defaultSelfIdentityTokens()
|
|
33482
|
-
};
|
|
33483
|
-
hits = inputs.length === 1 ? findCaveatsForHook(db, inputs[0], searchOptions) : findCaveatsForHookSegments(db, inputs, searchOptions);
|
|
33484
|
-
} catch (err) {
|
|
33485
|
-
observeRuntimeError("CAVEAT.CODEX_HOOK_FAILED", { version: CAVEAT_VERSION });
|
|
33486
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
33487
|
-
process.stderr.write(`[caveat:codex-hook] search error: ${msg}
|
|
33488
|
-
`);
|
|
33489
|
-
return [];
|
|
33490
|
-
}
|
|
33491
|
-
if (hits.length > 0) {
|
|
33492
|
-
try {
|
|
33493
|
-
markHit(db, hits);
|
|
33494
|
-
} catch (err) {
|
|
33495
|
-
observeRuntimeError("CAVEAT.CODEX_HOOK_FAILED", { version: CAVEAT_VERSION });
|
|
33496
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
33497
|
-
process.stderr.write(`[caveat:codex-hook] markHit error: ${msg}
|
|
33498
|
-
`);
|
|
33499
|
-
}
|
|
33500
|
-
} else {
|
|
33501
|
-
try {
|
|
33502
|
-
logHookQueryMiss({ caveatHome, agent: "codex", surface: inputs[0].surface, query: queryForLog });
|
|
33503
|
-
} catch (err) {
|
|
33504
|
-
observeRuntimeError("CAVEAT.CODEX_HOOK_FAILED", { version: CAVEAT_VERSION });
|
|
33505
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
33506
|
-
process.stderr.write(`[caveat:codex-hook] query log error: ${msg}
|
|
33507
|
-
`);
|
|
33508
|
-
}
|
|
33509
|
-
}
|
|
33510
|
-
try {
|
|
33511
|
-
return hits;
|
|
33512
|
-
} finally {
|
|
33513
|
-
db?.close();
|
|
33514
|
-
}
|
|
33515
|
-
}
|
|
33516
33418
|
function loadSignalsSafely2(path) {
|
|
33517
33419
|
try {
|
|
33518
33420
|
return readCodexSessionSignals(path);
|
|
33519
33421
|
} catch (err) {
|
|
33520
|
-
|
|
33521
|
-
process.stderr.write(`[caveat:codex-hook] transcript read error: ${msg}
|
|
33422
|
+
process.stderr.write(`[caveat:codex-hook] transcript read error: ${errorMessage4(err)}
|
|
33522
33423
|
`);
|
|
33523
33424
|
return null;
|
|
33524
33425
|
}
|
|
@@ -33527,29 +33428,6 @@ function codexSessionId(payload) {
|
|
|
33527
33428
|
const v = payload.session_id ?? payload.sessionId;
|
|
33528
33429
|
return typeof v === "string" && v.length > 0 ? v : null;
|
|
33529
33430
|
}
|
|
33530
|
-
function extractToolResponseText2(response) {
|
|
33531
|
-
if (typeof response === "string") return response;
|
|
33532
|
-
if (Array.isArray(response)) {
|
|
33533
|
-
return response.map((item) => {
|
|
33534
|
-
if (typeof item === "string") return item;
|
|
33535
|
-
if (item !== null && typeof item === "object") {
|
|
33536
|
-
const text = item.text;
|
|
33537
|
-
return typeof text === "string" ? text : "";
|
|
33538
|
-
}
|
|
33539
|
-
return "";
|
|
33540
|
-
}).filter(Boolean).join(" ");
|
|
33541
|
-
}
|
|
33542
|
-
if (response !== null && typeof response === "object") {
|
|
33543
|
-
const r = response;
|
|
33544
|
-
if (typeof r.content === "string") return r.content;
|
|
33545
|
-
if (Array.isArray(r.content)) return extractToolResponseText2(r.content);
|
|
33546
|
-
if (typeof r.output === "string") return r.output;
|
|
33547
|
-
if (typeof r.stdout === "string" || typeof r.stderr === "string") {
|
|
33548
|
-
return [r.stdout, r.stderr].filter((x) => typeof x === "string").join(" ");
|
|
33549
|
-
}
|
|
33550
|
-
}
|
|
33551
|
-
return "";
|
|
33552
|
-
}
|
|
33553
33431
|
function numericExitCode(v) {
|
|
33554
33432
|
return typeof v === "number" && Number.isInteger(v) ? v : null;
|
|
33555
33433
|
}
|
|
@@ -33558,10 +33436,10 @@ function processExitCodeFromText(text) {
|
|
|
33558
33436
|
return m ? Number(m[1]) : null;
|
|
33559
33437
|
}
|
|
33560
33438
|
function transcriptToolOutput(transcriptPath, toolUseId) {
|
|
33561
|
-
if (!transcriptPath || !toolUseId || !
|
|
33439
|
+
if (!transcriptPath || !toolUseId || !existsSync12(transcriptPath)) return null;
|
|
33562
33440
|
let raw = "";
|
|
33563
33441
|
try {
|
|
33564
|
-
raw =
|
|
33442
|
+
raw = readFileSync7(transcriptPath, "utf-8");
|
|
33565
33443
|
} catch {
|
|
33566
33444
|
return null;
|
|
33567
33445
|
}
|
|
@@ -33615,7 +33493,7 @@ function isCodexToolError(payload) {
|
|
|
33615
33493
|
const exit2 = numericExitCode(r.exit_code ?? r.exitCode);
|
|
33616
33494
|
if (exit2 !== null) return exit2 !== 0;
|
|
33617
33495
|
}
|
|
33618
|
-
const responseExit = processExitCodeFromText(
|
|
33496
|
+
const responseExit = processExitCodeFromText(extractToolResponseText(resp));
|
|
33619
33497
|
if (responseExit !== null) return responseExit !== 0;
|
|
33620
33498
|
const transcriptExit = transcriptExitCode(payload);
|
|
33621
33499
|
if (transcriptExit !== null) return transcriptExit !== 0;
|
|
@@ -33626,7 +33504,7 @@ function buildCodexPostToolUseWorkerJob(payload) {
|
|
|
33626
33504
|
if (!sessionId) return null;
|
|
33627
33505
|
const transcriptPath = typeof payload.transcript_path === "string" ? payload.transcript_path : void 0;
|
|
33628
33506
|
const toolUseId = typeof payload.tool_use_id === "string" ? payload.tool_use_id : void 0;
|
|
33629
|
-
const responseText =
|
|
33507
|
+
const responseText = extractToolResponseText(payload.tool_response ?? payload.toolResponse);
|
|
33630
33508
|
const inputText = toolInputText(payload.tool_input);
|
|
33631
33509
|
const transcriptOutput = transcriptPath && toolUseId ? transcriptToolOutput(transcriptPath, toolUseId) : null;
|
|
33632
33510
|
const topicText = inputText.trim();
|
|
@@ -33656,101 +33534,6 @@ function codexContextOutput(text, eventName = "UserPromptSubmit") {
|
|
|
33656
33534
|
}
|
|
33657
33535
|
});
|
|
33658
33536
|
}
|
|
33659
|
-
function codexPendingCleanupFailureText() {
|
|
33660
|
-
return "[caveat:codex-hook] pending reminder cleanup failed";
|
|
33661
|
-
}
|
|
33662
|
-
function drainForSession2(sessionId) {
|
|
33663
|
-
const ctx = buildContextSafely2();
|
|
33664
|
-
if (!ctx) return [];
|
|
33665
|
-
const local = drainPendingRemindersDetailed(ctx.caveatHome, sessionId);
|
|
33666
|
-
const global = drainPendingRemindersDetailed(ctx.caveatHome, "_global");
|
|
33667
|
-
for (const _failure of [...local.cleanupFailures, ...global.cleanupFailures]) {
|
|
33668
|
-
process.stderr.write(`${codexPendingCleanupFailureText()}
|
|
33669
|
-
`);
|
|
33670
|
-
}
|
|
33671
|
-
return [...local.reminders, ...global.reminders];
|
|
33672
|
-
}
|
|
33673
|
-
function codexContextDedupeKey(text) {
|
|
33674
|
-
if (text.startsWith(CODEX_STOP_REMINDER_PREFIX)) return "codex-stop-reminder";
|
|
33675
|
-
return text.trim();
|
|
33676
|
-
}
|
|
33677
|
-
function compactCodexContexts(contexts) {
|
|
33678
|
-
const selected = [];
|
|
33679
|
-
const seen = /* @__PURE__ */ new Set();
|
|
33680
|
-
for (let i = contexts.length - 1; i >= 0; i -= 1) {
|
|
33681
|
-
const text = contexts[i]?.trim();
|
|
33682
|
-
if (!text) continue;
|
|
33683
|
-
const key = codexContextDedupeKey(text);
|
|
33684
|
-
if (seen.has(key)) continue;
|
|
33685
|
-
seen.add(key);
|
|
33686
|
-
selected.push(text);
|
|
33687
|
-
}
|
|
33688
|
-
selected.reverse();
|
|
33689
|
-
const limited = selected.slice(-CODEX_MAX_CONTEXT_BLOCKS);
|
|
33690
|
-
const omitted = selected.length - limited.length;
|
|
33691
|
-
if (omitted > 0) {
|
|
33692
|
-
limited.push(`[caveat] pending reminder ${omitted} \u4EF6\u3092\u91CD\u8907\u307E\u305F\u306F\u4E0A\u9650\u306B\u3088\u308A\u7701\u7565\u3057\u307E\u3057\u305F\u3002`);
|
|
33693
|
-
}
|
|
33694
|
-
return limited;
|
|
33695
|
-
}
|
|
33696
|
-
function sanitizeCodexStateId(raw) {
|
|
33697
|
-
const clean = raw.replace(/[^A-Za-z0-9_-]/g, "");
|
|
33698
|
-
return clean.length > 0 ? clean : "_unknown";
|
|
33699
|
-
}
|
|
33700
|
-
function stopSignalKey2(signals, related) {
|
|
33701
|
-
const body = JSON.stringify({
|
|
33702
|
-
toolFailureCount: signals.toolFailureCount,
|
|
33703
|
-
fileEditCounts: signals.fileEditCounts.map((e) => [e.path, e.count]),
|
|
33704
|
-
webSearchCount: signals.webSearchCount,
|
|
33705
|
-
webFetchCount: signals.webFetchCount,
|
|
33706
|
-
bashRetryCount: signals.bashRetryCount,
|
|
33707
|
-
searchQueries: signals.searchQueries,
|
|
33708
|
-
related: related.map((h) => [h.source, h.id])
|
|
33709
|
-
});
|
|
33710
|
-
return createHash2("sha256").update(body).digest("hex");
|
|
33711
|
-
}
|
|
33712
|
-
function stopStatePath2(caveatHome, sessionId) {
|
|
33713
|
-
return join12(caveatHome, CODEX_STOP_STATE_DIR, `${sanitizeCodexStateId(sessionId)}.txt`);
|
|
33714
|
-
}
|
|
33715
|
-
function wasStopReminderQueued2(caveatHome, sessionId, key) {
|
|
33716
|
-
const path = stopStatePath2(caveatHome, sessionId);
|
|
33717
|
-
try {
|
|
33718
|
-
return readFileSync6(path, "utf-8") === key;
|
|
33719
|
-
} catch {
|
|
33720
|
-
return false;
|
|
33721
|
-
}
|
|
33722
|
-
}
|
|
33723
|
-
function markStopReminderQueued2(caveatHome, sessionId, key) {
|
|
33724
|
-
const path = stopStatePath2(caveatHome, sessionId);
|
|
33725
|
-
mkdirSync6(join12(caveatHome, CODEX_STOP_STATE_DIR), { recursive: true });
|
|
33726
|
-
writeFileSync6(path, key, "utf-8");
|
|
33727
|
-
}
|
|
33728
|
-
function queueStopForSession2(sessionId, signals, related) {
|
|
33729
|
-
const ctx = buildContextSafely2();
|
|
33730
|
-
if (!ctx) return;
|
|
33731
|
-
const key = stopSignalKey2(signals, related);
|
|
33732
|
-
if (wasStopReminderQueued2(ctx.caveatHome, sessionId, key)) return;
|
|
33733
|
-
let result;
|
|
33734
|
-
try {
|
|
33735
|
-
result = buildAndPublishPendingReminder(ctx.caveatHome, sessionId, buildPendingSemanticKey({
|
|
33736
|
-
agent: "codex",
|
|
33737
|
-
surface: "stop",
|
|
33738
|
-
refs: related,
|
|
33739
|
-
stopSignalDigest: key
|
|
33740
|
-
}), () => stopReminderText(signals, related));
|
|
33741
|
-
} catch {
|
|
33742
|
-
process.stderr.write("[caveat:codex-hook] pending reminder build or publish failed\n");
|
|
33743
|
-
return;
|
|
33744
|
-
}
|
|
33745
|
-
if (!result.ran) return;
|
|
33746
|
-
try {
|
|
33747
|
-
markStopReminderQueued2(ctx.caveatHome, sessionId, key);
|
|
33748
|
-
} catch (err) {
|
|
33749
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
33750
|
-
process.stderr.write(`[caveat:codex-hook] pending reminder write error: ${msg}
|
|
33751
|
-
`);
|
|
33752
|
-
}
|
|
33753
|
-
}
|
|
33754
33537
|
async function waitForTranscriptOutput(transcriptPath, toolUseId) {
|
|
33755
33538
|
const deadline = Date.now() + 2e3;
|
|
33756
33539
|
while (Date.now() <= deadline) {
|
|
@@ -33776,13 +33559,13 @@ async function processCodexWorkerJob(job, opts = { waitForTranscript: true }) {
|
|
|
33776
33559
|
}
|
|
33777
33560
|
}
|
|
33778
33561
|
if (!knownError && job.allowSymptomOnly !== true) return;
|
|
33779
|
-
const hits =
|
|
33562
|
+
const hits = searchCaveatsSafely(CODEX_HOST, {
|
|
33780
33563
|
topicText: job.topicText,
|
|
33781
33564
|
failureText,
|
|
33782
33565
|
surface: "tool_error"
|
|
33783
33566
|
});
|
|
33784
33567
|
if (hits.length === 0) return;
|
|
33785
|
-
const ctx =
|
|
33568
|
+
const ctx = buildContextSafely(CODEX_HOST);
|
|
33786
33569
|
if (!ctx) return;
|
|
33787
33570
|
let result;
|
|
33788
33571
|
try {
|
|
@@ -33800,7 +33583,7 @@ async function processCodexWorkerJob(job, opts = { waitForTranscript: true }) {
|
|
|
33800
33583
|
async function runCodexWorker(workFile) {
|
|
33801
33584
|
let raw;
|
|
33802
33585
|
try {
|
|
33803
|
-
raw =
|
|
33586
|
+
raw = readFileSync7(workFile, "utf-8");
|
|
33804
33587
|
} catch {
|
|
33805
33588
|
process.exit(0);
|
|
33806
33589
|
}
|
|
@@ -33817,7 +33600,7 @@ async function runCodexWorker(workFile) {
|
|
|
33817
33600
|
await processCodexWorkerJob(job);
|
|
33818
33601
|
process.exit(0);
|
|
33819
33602
|
}
|
|
33820
|
-
function runDiagnostics(codexHome = process.env.CODEX_HOME ??
|
|
33603
|
+
function runDiagnostics(codexHome = process.env.CODEX_HOME ?? join13(homedir3(), ".codex")) {
|
|
33821
33604
|
const features = spawnSync5("codex", ["features", "list"], {
|
|
33822
33605
|
encoding: "utf-8",
|
|
33823
33606
|
maxBuffer: 1024 * 1024,
|
|
@@ -33834,6 +33617,7 @@ function runDiagnostics(codexHome = process.env.CODEX_HOME ?? join12(homedir3(),
|
|
|
33834
33617
|
codexHome,
|
|
33835
33618
|
hooksPath: installation.hooksPath,
|
|
33836
33619
|
installedHooks: installation.hooks,
|
|
33620
|
+
legacyTimeoutSec: installation.legacyTimeoutSec,
|
|
33837
33621
|
evidence: featureOutput.split("\n").find((line) => line.trim().startsWith("hooks")) ?? null
|
|
33838
33622
|
};
|
|
33839
33623
|
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
@@ -33854,24 +33638,23 @@ async function runCodexHook(name, arg) {
|
|
|
33854
33638
|
}
|
|
33855
33639
|
let raw = "";
|
|
33856
33640
|
try {
|
|
33857
|
-
raw = await
|
|
33641
|
+
raw = await readStdin();
|
|
33858
33642
|
} catch (err) {
|
|
33859
|
-
|
|
33860
|
-
process.stderr.write(`[caveat:codex-hook] stdin read error: ${msg}
|
|
33643
|
+
process.stderr.write(`[caveat:codex-hook] stdin read error: ${errorMessage4(err)}
|
|
33861
33644
|
`);
|
|
33862
33645
|
process.exit(0);
|
|
33863
33646
|
}
|
|
33864
|
-
const payload =
|
|
33647
|
+
const payload = parsePayload(CODEX_HOST, raw);
|
|
33865
33648
|
const sessionId = codexSessionId(payload);
|
|
33866
33649
|
if (!sessionId) process.stderr.write("[caveat:codex-hook] missing session_id; pending drain disabled\n");
|
|
33867
33650
|
if (name === "user-prompt-submit") {
|
|
33868
|
-
const contexts = sessionId ?
|
|
33651
|
+
const contexts = sessionId ? drainForSession(CODEX_HOST, sessionId) : [];
|
|
33869
33652
|
const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
|
|
33870
|
-
const hits =
|
|
33653
|
+
const hits = searchCaveatsSafely(CODEX_HOST, { topicText: prompt, failureText: prompt, surface: "user_prompt" });
|
|
33871
33654
|
if (hits.length > 0) {
|
|
33872
33655
|
contexts.push(userPromptSubmitReminderText(hits));
|
|
33873
33656
|
}
|
|
33874
|
-
const compacted =
|
|
33657
|
+
const compacted = compactContexts(CODEX_HOST, contexts);
|
|
33875
33658
|
if (compacted.length > 0) {
|
|
33876
33659
|
process.stdout.write(`${codexContextOutput(compacted.join("\n\n"))}
|
|
33877
33660
|
`);
|
|
@@ -33885,39 +33668,36 @@ async function runCodexHook(name, arg) {
|
|
|
33885
33668
|
}
|
|
33886
33669
|
if (name === "stop") {
|
|
33887
33670
|
try {
|
|
33888
|
-
const ctx =
|
|
33671
|
+
const ctx = buildContextSafely(CODEX_HOST);
|
|
33889
33672
|
if (ctx) {
|
|
33890
33673
|
try {
|
|
33891
33674
|
maybeSweepPendingDirs(ctx.caveatHome);
|
|
33892
33675
|
} catch (err) {
|
|
33893
|
-
|
|
33894
|
-
process.stderr.write(`[caveat:codex-hook] pending sweep error: ${msg}
|
|
33676
|
+
process.stderr.write(`[caveat:codex-hook] pending sweep error: ${errorMessage4(err)}
|
|
33895
33677
|
`);
|
|
33896
33678
|
}
|
|
33897
33679
|
maybeTriggerAutoReindex(ctx);
|
|
33898
33680
|
try {
|
|
33899
33681
|
maybeTriggerAutoSync(ctx);
|
|
33900
33682
|
} catch (err) {
|
|
33901
|
-
|
|
33902
|
-
process.stderr.write(`[caveat:codex-hook] auto sync trigger error: ${msg}
|
|
33683
|
+
process.stderr.write(`[caveat:codex-hook] auto sync trigger error: ${errorMessage4(err)}
|
|
33903
33684
|
`);
|
|
33904
33685
|
}
|
|
33905
33686
|
}
|
|
33906
33687
|
} catch (err) {
|
|
33907
|
-
|
|
33908
|
-
process.stderr.write(`[caveat:codex-hook] auto reindex trigger error: ${msg}
|
|
33688
|
+
process.stderr.write(`[caveat:codex-hook] auto reindex trigger error: ${errorMessage4(err)}
|
|
33909
33689
|
`);
|
|
33910
33690
|
}
|
|
33911
33691
|
if (payload.stop_hook_active === true) process.exit(0);
|
|
33912
33692
|
const transcriptPath = typeof payload.transcript_path === "string" ? payload.transcript_path : "";
|
|
33913
33693
|
const signals = transcriptPath ? loadSignalsSafely2(transcriptPath) : null;
|
|
33914
33694
|
if (!signals || !hasAnyStruggleSignal(signals)) process.exit(0);
|
|
33915
|
-
const related =
|
|
33695
|
+
const related = searchCaveatsSafely(CODEX_HOST, signals.errorSnippets.map((failureText) => ({
|
|
33916
33696
|
topicText: "",
|
|
33917
33697
|
failureText,
|
|
33918
33698
|
surface: "stop"
|
|
33919
33699
|
})));
|
|
33920
|
-
if (sessionId)
|
|
33700
|
+
if (sessionId) queueStopForSession(CODEX_HOST, sessionId, signals, related, () => stopReminderText(signals, related));
|
|
33921
33701
|
process.exit(0);
|
|
33922
33702
|
}
|
|
33923
33703
|
process.stderr.write(`[caveat:codex-hook] unknown hook name: ${name}
|
|
@@ -33926,9 +33706,9 @@ async function runCodexHook(name, arg) {
|
|
|
33926
33706
|
}
|
|
33927
33707
|
|
|
33928
33708
|
// src/commands/pull.ts
|
|
33929
|
-
import { existsSync as
|
|
33709
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
33930
33710
|
async function runPull(ctx) {
|
|
33931
|
-
const hasCommunityDir =
|
|
33711
|
+
const hasCommunityDir = existsSync13(ctx.paths.communityDir);
|
|
33932
33712
|
if (!hasCommunityDir) {
|
|
33933
33713
|
ctx.logger.info(
|
|
33934
33714
|
"no community repos yet \u2014 add one with `caveat community add <github-url>`."
|
|
@@ -34028,9 +33808,9 @@ async function runSync(ctx, opts, dependencies = {}) {
|
|
|
34028
33808
|
|
|
34029
33809
|
// src/commands/codexSidecar.ts
|
|
34030
33810
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
34031
|
-
import { closeSync, constants as constants3, fstatSync, lstatSync as lstatSync2, mkdirSync as
|
|
33811
|
+
import { closeSync, constants as constants3, fstatSync, lstatSync as lstatSync2, mkdirSync as mkdirSync6, mkdtempSync as mkdtempSync3, openSync, readSync as readSync2, realpathSync as realpathSync4, rmSync as rmSync3, writeFileSync as writeFileSync7 } from "node:fs";
|
|
34032
33812
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
34033
|
-
import { basename as basename2, dirname as
|
|
33813
|
+
import { basename as basename2, dirname as dirname6, join as join14 } from "node:path";
|
|
34034
33814
|
import { cwd, exit } from "node:process";
|
|
34035
33815
|
function runCodexSidecarDiagnostics(logger, opts) {
|
|
34036
33816
|
const plan = buildCodexSidecarDiagnosticsCommand({
|
|
@@ -34062,8 +33842,8 @@ function runCodexSidecarWithCaveats(ctx, workflow, prompt, opts) {
|
|
|
34062
33842
|
process.stdout.write(JSON.stringify({ status: "skipped", decision }, null, 2) + "\n");
|
|
34063
33843
|
exit(0);
|
|
34064
33844
|
}
|
|
34065
|
-
const contextDir = mkdtempSync3(
|
|
34066
|
-
const contextFile =
|
|
33845
|
+
const contextDir = mkdtempSync3(join14(tmpdir4(), "caveat-sidecar-context-"));
|
|
33846
|
+
const contextFile = join14(contextDir, "context.json");
|
|
34067
33847
|
let status2 = 1;
|
|
34068
33848
|
try {
|
|
34069
33849
|
const blocks = collectCaveatContextBlocks(ctx, {
|
|
@@ -34128,16 +33908,16 @@ function readHookSignalAdditionalContextFile(path, testProbe) {
|
|
|
34128
33908
|
return [block];
|
|
34129
33909
|
}
|
|
34130
33910
|
function assertPrivateRegular(stat, uid, platform) {
|
|
34131
|
-
const privateOwner =
|
|
33911
|
+
const privateOwner = isPrivateOwnerStat(stat, uid, platform);
|
|
34132
33912
|
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_ADDITIONAL_CONTEXT_BYTES || !privateOwner) throw new Error("additional context file must be a private owner-only regular file within 4096 bytes");
|
|
34133
33913
|
}
|
|
34134
33914
|
function assertWindowsPrivateTempContainer(path, platform) {
|
|
34135
|
-
if (platform
|
|
34136
|
-
const parent =
|
|
33915
|
+
if (!isWindows(platform)) return;
|
|
33916
|
+
const parent = dirname6(path);
|
|
34137
33917
|
const parentStat = lstatSync2(parent);
|
|
34138
|
-
const resolvedParent =
|
|
34139
|
-
const resolvedTemp =
|
|
34140
|
-
if (parentStat.isSymbolicLink() || !parentStat.isDirectory() ||
|
|
33918
|
+
const resolvedParent = realpathSync4(parent);
|
|
33919
|
+
const resolvedTemp = realpathSync4(tmpdir4());
|
|
33920
|
+
if (parentStat.isSymbolicLink() || !parentStat.isDirectory() || dirname6(resolvedParent) !== resolvedTemp || !basename2(resolvedParent).startsWith("caveat-")) {
|
|
34141
33921
|
throw new Error("additional context file must be inside a reserved per-user Caveat temporary directory");
|
|
34142
33922
|
}
|
|
34143
33923
|
}
|
|
@@ -34226,7 +34006,7 @@ function executePlan(logger, command, args, options = {}) {
|
|
|
34226
34006
|
}
|
|
34227
34007
|
function saveStructuredResult(path, stdout) {
|
|
34228
34008
|
const parsed = JSON.parse(stdout);
|
|
34229
|
-
|
|
34009
|
+
mkdirSync6(dirname6(path), { recursive: true });
|
|
34230
34010
|
writeFileSync7(path, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
34231
34011
|
}
|
|
34232
34012
|
function shellDisplayQuote(value) {
|
|
@@ -34315,8 +34095,8 @@ function runCommunityRemove(ctx, handle, opts) {
|
|
|
34315
34095
|
|
|
34316
34096
|
// src/commands/factoryDiagnostics.ts
|
|
34317
34097
|
import { execFileSync } from "node:child_process";
|
|
34318
|
-
import { existsSync as
|
|
34319
|
-
import { join as
|
|
34098
|
+
import { existsSync as existsSync14, readFileSync as readFileSync8 } from "node:fs";
|
|
34099
|
+
import { join as join15 } from "node:path";
|
|
34320
34100
|
import { DatabaseSync } from "node:sqlite";
|
|
34321
34101
|
import { parse as parseToml2 } from "smol-toml";
|
|
34322
34102
|
var status = (ok, reason) => ({ status: ok ? "ready" : "not_ready", reason_code: ok ? "ready" : reason });
|
|
@@ -34328,10 +34108,10 @@ function isRecord2(value) {
|
|
|
34328
34108
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
34329
34109
|
}
|
|
34330
34110
|
function claudeRegistration(home, nodePath, cliScriptPath) {
|
|
34331
|
-
const path =
|
|
34332
|
-
if (!
|
|
34111
|
+
const path = join15(home, ".claude.json");
|
|
34112
|
+
if (!existsSync14(path)) return status(false, "not_registered");
|
|
34333
34113
|
try {
|
|
34334
|
-
const value = JSON.parse(
|
|
34114
|
+
const value = JSON.parse(readFileSync8(path, "utf8"));
|
|
34335
34115
|
if (!isRecord2(value) || !isRecord2(value.mcpServers)) return status(false, "not_registered");
|
|
34336
34116
|
return status(isCaveatClaudeMcpRegistration(value.mcpServers.caveat, nodePath, cliScriptPath), "not_registered");
|
|
34337
34117
|
} catch {
|
|
@@ -34340,7 +34120,7 @@ function claudeRegistration(home, nodePath, cliScriptPath) {
|
|
|
34340
34120
|
}
|
|
34341
34121
|
function claudeHooks(home, nodePath, cliScriptPath) {
|
|
34342
34122
|
try {
|
|
34343
|
-
const settings = JSON.parse(
|
|
34123
|
+
const settings = JSON.parse(readFileSync8(join15(home, ".claude", "settings.json"), "utf8"));
|
|
34344
34124
|
const present = (event, subcommand) => settings.hooks?.[event]?.some((entry) => entry.hooks?.some((item) => typeof item.command === "string" && isCanonicalCaveatClaudeHookCommand(item.command, subcommand, nodePath, cliScriptPath))) ?? false;
|
|
34345
34125
|
return { user_prompt_submit: hook(present("UserPromptSubmit", "user-prompt-submit")), post_tool_use: hook(present("PostToolUse", "post-tool-use")), post_tool_use_failure: hook(present("PostToolUseFailure", "post-tool-use")), stop: hook(present("Stop", "stop")) };
|
|
34346
34126
|
} catch {
|
|
@@ -34362,7 +34142,7 @@ function strictSearchSchema(db) {
|
|
|
34362
34142
|
return columns.map((column) => `${column.name}:${column.hidden}`).join(",") === "id:0,title:0,body:0,tags:0,entries_fts:1,rank:1";
|
|
34363
34143
|
}
|
|
34364
34144
|
function database(path) {
|
|
34365
|
-
if (!
|
|
34145
|
+
if (!existsSync14(path)) return { status: "not_ready", reason_code: "missing", schema_version: null, supported_schema_version: 3, migration_status: "unverified" };
|
|
34366
34146
|
try {
|
|
34367
34147
|
const db = new DatabaseSync(path, { readOnly: true });
|
|
34368
34148
|
try {
|
|
@@ -34387,10 +34167,10 @@ function database(path) {
|
|
|
34387
34167
|
}
|
|
34388
34168
|
}
|
|
34389
34169
|
function codexFeature(codexHome) {
|
|
34390
|
-
const path =
|
|
34391
|
-
if (!
|
|
34170
|
+
const path = join15(codexHome, "config.toml");
|
|
34171
|
+
if (!existsSync14(path)) return status(false, "feature_disabled");
|
|
34392
34172
|
try {
|
|
34393
|
-
const config2 = parseToml2(
|
|
34173
|
+
const config2 = parseToml2(readFileSync8(path, "utf8"));
|
|
34394
34174
|
if (!isRecord2(config2.features)) return status(false, "feature_disabled");
|
|
34395
34175
|
const features = config2.features;
|
|
34396
34176
|
return status(features.hooks === true && features.codex_hooks === void 0, "feature_disabled");
|
|
@@ -34399,10 +34179,10 @@ function codexFeature(codexHome) {
|
|
|
34399
34179
|
}
|
|
34400
34180
|
}
|
|
34401
34181
|
function codexHooks(codexHome, nodePath, cliScriptPath) {
|
|
34402
|
-
const value = JSON.parse(
|
|
34182
|
+
const value = JSON.parse(readFileSync8(join15(codexHome, "hooks.json"), "utf8"));
|
|
34403
34183
|
if (!isRecord2(value) || !isRecord2(value.hooks)) throw Error("config_unreadable");
|
|
34404
34184
|
const hooks = value.hooks;
|
|
34405
|
-
const present = (event, subcommand) => Array.isArray(hooks[event]) && hooks[event].some((entry) => isRecord2(entry) && Array.isArray(entry.hooks) && entry.hooks.some((item) =>
|
|
34185
|
+
const present = (event, subcommand) => Array.isArray(hooks[event]) && hooks[event].some((entry) => isRecord2(entry) && Array.isArray(entry.hooks) && entry.hooks.some((item) => isCanonicalCaveatCodexHookEntry(item, subcommand, nodePath, cliScriptPath)));
|
|
34406
34186
|
return { userPromptSubmit: present("UserPromptSubmit", "user-prompt-submit"), postToolUse: present("PostToolUse", "post-tool-use"), stop: present("Stop", "stop") };
|
|
34407
34187
|
}
|
|
34408
34188
|
function sync(own) {
|
|
@@ -34434,7 +34214,7 @@ function sync(own) {
|
|
|
34434
34214
|
return unverified("upstream_unavailable");
|
|
34435
34215
|
}
|
|
34436
34216
|
}
|
|
34437
|
-
function factoryDiagnostics(ctx, codexHome = process.env.CODEX_HOME ??
|
|
34217
|
+
function factoryDiagnostics(ctx, codexHome = process.env.CODEX_HOME ?? join15(ctx.userHome, ".codex")) {
|
|
34438
34218
|
const nodePath = process.execPath;
|
|
34439
34219
|
const cliScriptPath = process.argv[1] ?? "";
|
|
34440
34220
|
const db = database(ctx.paths.dbPath);
|