caveat-cli 0.14.8 → 0.14.10
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/README.md +15 -0
- package/dist/chunk-ECC5LJZF.js +0 -0
- package/dist/index.js +156 -89
- package/dist/index.js.map +1 -1
- package/dist/server-YQ4DJ2H7.js +0 -0
- package/package.json +12 -12
package/README.md
CHANGED
|
@@ -12,6 +12,11 @@ caveat init # Claude Code MCP + hooks
|
|
|
12
12
|
caveat codex-hook install # optional: native Codex hooks
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
+
On macOS with Homebrew Node, Caveat installs hook commands through the stable
|
|
16
|
+
`/opt/homebrew/bin/node` symlink when it points at the current Node binary. This
|
|
17
|
+
keeps Claude Code and Codex hooks working after Homebrew moves Node between
|
|
18
|
+
`/opt/homebrew/Cellar/node/<version>/...` directories.
|
|
19
|
+
|
|
15
20
|
`caveat init` (idempotent, `--dry-run` supported) does the Claude Code setup:
|
|
16
21
|
|
|
17
22
|
1. Scaffolds `~/.caveat/own/` (your personal knowledge repo) + `~/.caveat/index/caveat.db`
|
|
@@ -82,6 +87,16 @@ If you want `~/.caveat/own/` to live elsewhere (e.g. a git-tracked directory you
|
|
|
82
87
|
- Claude Code installed if you want Claude MCP / hooks integration. Without it, `caveat init --skip-claude` still provisions local state.
|
|
83
88
|
- Codex installed if you want native Codex hooks via `caveat codex-hook install`.
|
|
84
89
|
|
|
90
|
+
Release install smoke:
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
npm uninstall -g caveat-cli
|
|
94
|
+
npm install -g caveat-cli
|
|
95
|
+
caveat --version
|
|
96
|
+
caveat init
|
|
97
|
+
caveat codex-hook install
|
|
98
|
+
```
|
|
99
|
+
|
|
85
100
|
## License
|
|
86
101
|
|
|
87
102
|
MIT
|
package/dist/chunk-ECC5LJZF.js
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
@@ -6854,8 +6854,8 @@ var stdoutLogger = {
|
|
|
6854
6854
|
};
|
|
6855
6855
|
|
|
6856
6856
|
// src/commands/init.ts
|
|
6857
|
-
import { existsSync as
|
|
6858
|
-
import { dirname as dirname3, join as
|
|
6857
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, renameSync, rmdirSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
6858
|
+
import { dirname as dirname3, join as join5 } from "node:path";
|
|
6859
6859
|
|
|
6860
6860
|
// src/claudeInstall.ts
|
|
6861
6861
|
import { spawnSync } from "node:child_process";
|
|
@@ -6877,10 +6877,16 @@ function hookCommand(nodePath, cliScriptPath, event) {
|
|
|
6877
6877
|
function upsertHook(settings, event, command) {
|
|
6878
6878
|
settings.hooks ??= {};
|
|
6879
6879
|
const list = settings.hooks[event] ??= [];
|
|
6880
|
-
const
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6880
|
+
const subcommand = command.includes("hook user-prompt-submit") ? "user-prompt-submit" : command.includes("hook post-tool-use") ? "post-tool-use" : command.includes("hook stop") ? "stop" : null;
|
|
6881
|
+
for (const entry of list) {
|
|
6882
|
+
for (const hook of entry.hooks ?? []) {
|
|
6883
|
+
if (isSameHookCommand(hook.command, command)) return "unchanged";
|
|
6884
|
+
if (subcommand && isCaveatClaudeHookCommand(hook.command, subcommand)) {
|
|
6885
|
+
hook.command = command;
|
|
6886
|
+
return "added";
|
|
6887
|
+
}
|
|
6888
|
+
}
|
|
6889
|
+
}
|
|
6884
6890
|
list.push({ hooks: [{ type: "command", command }] });
|
|
6885
6891
|
return "added";
|
|
6886
6892
|
}
|
|
@@ -6907,6 +6913,13 @@ function findExistingHookCommand(settings, event, command) {
|
|
|
6907
6913
|
function isSameHookCommand(actual, expected) {
|
|
6908
6914
|
return actual === expected || actual.endsWith(` ${expected}`);
|
|
6909
6915
|
}
|
|
6916
|
+
function isEnvPrefixedCommand(command) {
|
|
6917
|
+
return /^[A-Z_][A-Z0-9_]*=/.test(command.trim());
|
|
6918
|
+
}
|
|
6919
|
+
function isCaveatClaudeHookCommand(actual, event) {
|
|
6920
|
+
const lower = actual.toLowerCase();
|
|
6921
|
+
return !isEnvPrefixedCommand(actual) && lower.includes("caveat") && actual.includes(`hook ${event}`);
|
|
6922
|
+
}
|
|
6910
6923
|
function readSettings(path) {
|
|
6911
6924
|
if (!existsSync(path)) return {};
|
|
6912
6925
|
return JSON.parse(readFileSync2(path, "utf-8"));
|
|
@@ -7037,6 +7050,40 @@ function uninstallClaudeIntegration(opts) {
|
|
|
7037
7050
|
};
|
|
7038
7051
|
}
|
|
7039
7052
|
|
|
7053
|
+
// src/nodePath.ts
|
|
7054
|
+
import { existsSync as existsSync2, realpathSync } from "node:fs";
|
|
7055
|
+
import { delimiter, join as join4 } from "node:path";
|
|
7056
|
+
var defaultRealpath = (path) => realpathSync.native(path);
|
|
7057
|
+
function safeRealpath(path, realpath = defaultRealpath) {
|
|
7058
|
+
try {
|
|
7059
|
+
return realpath(path);
|
|
7060
|
+
} catch {
|
|
7061
|
+
return null;
|
|
7062
|
+
}
|
|
7063
|
+
}
|
|
7064
|
+
function resolveHookNodePath({
|
|
7065
|
+
env = process.env,
|
|
7066
|
+
execPath = process.execPath,
|
|
7067
|
+
platform = process.platform,
|
|
7068
|
+
exists = existsSync2,
|
|
7069
|
+
realpath = defaultRealpath
|
|
7070
|
+
} = {}) {
|
|
7071
|
+
const execRealpath = safeRealpath(execPath, realpath);
|
|
7072
|
+
const pathEnv = env.PATH ?? env.Path ?? "";
|
|
7073
|
+
const names = platform === "win32" ? ["node.exe", "node.cmd", "node.bat", "node"] : ["node"];
|
|
7074
|
+
for (const dir of pathEnv.split(delimiter).filter(Boolean)) {
|
|
7075
|
+
for (const name of names) {
|
|
7076
|
+
const candidate = join4(dir, name);
|
|
7077
|
+
if (!exists(candidate)) continue;
|
|
7078
|
+
const candidateRealpath = safeRealpath(candidate, realpath);
|
|
7079
|
+
if (execRealpath && candidateRealpath && candidateRealpath === execRealpath) {
|
|
7080
|
+
return candidate;
|
|
7081
|
+
}
|
|
7082
|
+
}
|
|
7083
|
+
}
|
|
7084
|
+
return execPath;
|
|
7085
|
+
}
|
|
7086
|
+
|
|
7040
7087
|
// src/commands/init.ts
|
|
7041
7088
|
var KNOWLEDGE_GITIGNORE = [
|
|
7042
7089
|
"# Never commit entries flagged private (visibility: private is enforced by the",
|
|
@@ -7050,7 +7097,7 @@ var KNOWLEDGE_GITIGNORE = [
|
|
|
7050
7097
|
async function runInit(ctx, opts = { skipClaude: false, dryRun: false }) {
|
|
7051
7098
|
ensureUserConfig(ctx.userConfigPath);
|
|
7052
7099
|
ctx.logger.info(`user config: ${ctx.userConfigPath}`);
|
|
7053
|
-
if (!
|
|
7100
|
+
if (!existsSync3(ctx.paths.knowledgeRepo)) {
|
|
7054
7101
|
mkdirSync2(ctx.paths.knowledgeRepo, { recursive: true });
|
|
7055
7102
|
mkdirSync2(ctx.paths.entriesDir, { recursive: true });
|
|
7056
7103
|
ctx.logger.info(`knowledge repo scaffolded: ${ctx.paths.knowledgeRepo}`);
|
|
@@ -7058,14 +7105,14 @@ async function runInit(ctx, opts = { skipClaude: false, dryRun: false }) {
|
|
|
7058
7105
|
ctx.logger.info(`knowledge repo: ${ctx.paths.knowledgeRepo}`);
|
|
7059
7106
|
}
|
|
7060
7107
|
migrateLegacyCommunityDir(ctx);
|
|
7061
|
-
const gitignorePath =
|
|
7062
|
-
if (!
|
|
7108
|
+
const gitignorePath = join5(ctx.paths.knowledgeRepo, ".gitignore");
|
|
7109
|
+
if (!existsSync3(gitignorePath)) {
|
|
7063
7110
|
writeFileSync2(gitignorePath, KNOWLEDGE_GITIGNORE, "utf-8");
|
|
7064
7111
|
ctx.logger.info(`.gitignore created: ${gitignorePath}`);
|
|
7065
7112
|
}
|
|
7066
7113
|
if (!opts.dryRun) {
|
|
7067
7114
|
const dbDir = dirname3(ctx.paths.dbPath);
|
|
7068
|
-
if (!
|
|
7115
|
+
if (!existsSync3(dbDir)) mkdirSync2(dbDir, { recursive: true });
|
|
7069
7116
|
const db = openDb({ path: ctx.paths.dbPath, logger: ctx.logger });
|
|
7070
7117
|
db.close();
|
|
7071
7118
|
ctx.logger.info(`db initialized: ${ctx.paths.dbPath}`);
|
|
@@ -7096,20 +7143,20 @@ async function runInit(ctx, opts = { skipClaude: false, dryRun: false }) {
|
|
|
7096
7143
|
return;
|
|
7097
7144
|
}
|
|
7098
7145
|
const result = installClaudeIntegration({
|
|
7099
|
-
claudeDir:
|
|
7146
|
+
claudeDir: join5(ctx.userHome, ".claude"),
|
|
7100
7147
|
cliScriptPath,
|
|
7101
|
-
nodePath:
|
|
7148
|
+
nodePath: resolveHookNodePath(),
|
|
7102
7149
|
dryRun: opts.dryRun,
|
|
7103
7150
|
logger: ctx.logger
|
|
7104
7151
|
});
|
|
7105
7152
|
reportInstallResult(ctx, result, opts.dryRun);
|
|
7106
7153
|
}
|
|
7107
7154
|
function migrateLegacyCommunityDir(ctx) {
|
|
7108
|
-
const legacy =
|
|
7155
|
+
const legacy = join5(ctx.paths.knowledgeRepo, "community");
|
|
7109
7156
|
const current = ctx.paths.communityDir;
|
|
7110
7157
|
if (legacy === current) return;
|
|
7111
|
-
if (!
|
|
7112
|
-
if (
|
|
7158
|
+
if (!existsSync3(legacy)) return;
|
|
7159
|
+
if (existsSync3(current)) {
|
|
7113
7160
|
ctx.logger.warn(
|
|
7114
7161
|
`legacy community dir still exists at ${legacy} \u2014 remove manually (new location in use)`
|
|
7115
7162
|
);
|
|
@@ -7118,7 +7165,7 @@ function migrateLegacyCommunityDir(ctx) {
|
|
|
7118
7165
|
mkdirSync2(current, { recursive: true });
|
|
7119
7166
|
for (const entry of readdirSync(legacy, { withFileTypes: true })) {
|
|
7120
7167
|
if (!entry.isDirectory()) continue;
|
|
7121
|
-
renameSync(
|
|
7168
|
+
renameSync(join5(legacy, entry.name), join5(current, entry.name));
|
|
7122
7169
|
}
|
|
7123
7170
|
try {
|
|
7124
7171
|
rmdirSync(legacy);
|
|
@@ -7133,9 +7180,9 @@ function runUninstall(ctx, opts) {
|
|
|
7133
7180
|
process.exit(1);
|
|
7134
7181
|
}
|
|
7135
7182
|
const result = uninstallClaudeIntegration({
|
|
7136
|
-
claudeDir:
|
|
7183
|
+
claudeDir: join5(ctx.userHome, ".claude"),
|
|
7137
7184
|
cliScriptPath,
|
|
7138
|
-
nodePath:
|
|
7185
|
+
nodePath: resolveHookNodePath(),
|
|
7139
7186
|
dryRun: opts.dryRun,
|
|
7140
7187
|
logger: ctx.logger
|
|
7141
7188
|
});
|
|
@@ -7180,29 +7227,29 @@ function reportInstallResult(ctx, result, dryRun) {
|
|
|
7180
7227
|
}
|
|
7181
7228
|
|
|
7182
7229
|
// src/commands/indexCmd.ts
|
|
7183
|
-
import { existsSync as
|
|
7184
|
-
import { dirname as dirname4, join as
|
|
7230
|
+
import { existsSync as existsSync4, readdirSync as readdirSync2, mkdirSync as mkdirSync3 } from "node:fs";
|
|
7231
|
+
import { dirname as dirname4, join as join6 } from "node:path";
|
|
7185
7232
|
function runIndex(ctx, opts) {
|
|
7186
7233
|
const dbDir = dirname4(ctx.paths.dbPath);
|
|
7187
|
-
if (!
|
|
7234
|
+
if (!existsSync4(dbDir)) mkdirSync3(dbDir, { recursive: true });
|
|
7188
7235
|
const db = openDb({ path: ctx.paths.dbPath, logger: ctx.logger });
|
|
7189
7236
|
try {
|
|
7190
7237
|
if (opts.full) {
|
|
7191
7238
|
ctx.logger.info("full rebuild: DELETE FROM entries");
|
|
7192
7239
|
rebuildAll(db);
|
|
7193
7240
|
}
|
|
7194
|
-
if (
|
|
7241
|
+
if (existsSync4(ctx.paths.entriesDir)) {
|
|
7195
7242
|
const result = scanSource({ db, source: "own", entriesRoot: ctx.paths.entriesDir });
|
|
7196
7243
|
ctx.logger.info(`own: +${result.added} ~${result.updated} -${result.deleted}`);
|
|
7197
7244
|
} else {
|
|
7198
7245
|
ctx.logger.warn(`entries dir not found: ${ctx.paths.entriesDir}`);
|
|
7199
7246
|
}
|
|
7200
|
-
if (
|
|
7247
|
+
if (existsSync4(ctx.paths.communityDir)) {
|
|
7201
7248
|
for (const entry of readdirSync2(ctx.paths.communityDir, { withFileTypes: true })) {
|
|
7202
7249
|
if (!entry.isDirectory()) continue;
|
|
7203
7250
|
const source = `community/${entry.name}`;
|
|
7204
|
-
const root =
|
|
7205
|
-
if (!
|
|
7251
|
+
const root = join6(ctx.paths.communityDir, entry.name, "entries");
|
|
7252
|
+
if (!existsSync4(root)) continue;
|
|
7206
7253
|
const result = scanSource({ db, source, entriesRoot: root });
|
|
7207
7254
|
ctx.logger.info(`${source}: +${result.added} ~${result.updated} -${result.deleted}`);
|
|
7208
7255
|
}
|
|
@@ -12612,8 +12659,8 @@ function emoji() {
|
|
|
12612
12659
|
}
|
|
12613
12660
|
var ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
|
|
12614
12661
|
var ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;
|
|
12615
|
-
var mac = (
|
|
12616
|
-
const escapedDelim = escapeRegex(
|
|
12662
|
+
var mac = (delimiter2) => {
|
|
12663
|
+
const escapedDelim = escapeRegex(delimiter2 ?? ":");
|
|
12617
12664
|
return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
|
|
12618
12665
|
};
|
|
12619
12666
|
var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;
|
|
@@ -31430,11 +31477,11 @@ var StdioServerTransport = class {
|
|
|
31430
31477
|
|
|
31431
31478
|
// ../mcp/dist/context.js
|
|
31432
31479
|
import { homedir as homedir2 } from "node:os";
|
|
31433
|
-
import { join as
|
|
31480
|
+
import { join as join7 } from "node:path";
|
|
31434
31481
|
function buildMcpContext(overrides = {}) {
|
|
31435
31482
|
const userHome = overrides.userHome ?? homedir2();
|
|
31436
31483
|
const caveatHome = overrides.caveatHome ?? findCaveatHome(userHome);
|
|
31437
|
-
const userConfigPath =
|
|
31484
|
+
const userConfigPath = join7(userHome, ".caveatrc.json");
|
|
31438
31485
|
const logger = overrides.logger ?? stderrLogger;
|
|
31439
31486
|
const config2 = loadConfig(userConfigPath);
|
|
31440
31487
|
const paths = resolvePaths(caveatHome, config2.knowledgeRepo, userHome);
|
|
@@ -31569,13 +31616,13 @@ function handleListRecent(ctx, args) {
|
|
|
31569
31616
|
}
|
|
31570
31617
|
|
|
31571
31618
|
// ../mcp/dist/tools/pull.js
|
|
31572
|
-
import { existsSync as
|
|
31573
|
-
import { join as
|
|
31619
|
+
import { existsSync as existsSync5, readdirSync as readdirSync3 } from "node:fs";
|
|
31620
|
+
import { join as join8 } from "node:path";
|
|
31574
31621
|
var pullInputShape = {};
|
|
31575
31622
|
async function handlePull(ctx, _args = {}) {
|
|
31576
31623
|
const pulled = [];
|
|
31577
31624
|
const indexed = [];
|
|
31578
|
-
if (
|
|
31625
|
+
if (existsSync5(ctx.paths.communityDir)) {
|
|
31579
31626
|
const results = await communityPull({
|
|
31580
31627
|
communityDir: ctx.paths.communityDir,
|
|
31581
31628
|
logger: ctx.logger
|
|
@@ -31585,16 +31632,16 @@ async function handlePull(ctx, _args = {}) {
|
|
|
31585
31632
|
}
|
|
31586
31633
|
}
|
|
31587
31634
|
rebuildAll(ctx.db);
|
|
31588
|
-
if (
|
|
31635
|
+
if (existsSync5(ctx.paths.entriesDir)) {
|
|
31589
31636
|
const own = scanSource({ db: ctx.db, source: "own", entriesRoot: ctx.paths.entriesDir });
|
|
31590
31637
|
indexed.push({ source: "own", ...own });
|
|
31591
31638
|
}
|
|
31592
|
-
if (
|
|
31639
|
+
if (existsSync5(ctx.paths.communityDir)) {
|
|
31593
31640
|
for (const entry of readdirSync3(ctx.paths.communityDir, { withFileTypes: true })) {
|
|
31594
31641
|
if (!entry.isDirectory()) continue;
|
|
31595
31642
|
const source = `community/${entry.name}`;
|
|
31596
|
-
const root =
|
|
31597
|
-
if (!
|
|
31643
|
+
const root = join8(ctx.paths.communityDir, entry.name, "entries");
|
|
31644
|
+
if (!existsSync5(root)) continue;
|
|
31598
31645
|
const scan = scanSource({ db: ctx.db, source, entriesRoot: root });
|
|
31599
31646
|
indexed.push({ source, ...scan });
|
|
31600
31647
|
}
|
|
@@ -31709,7 +31756,7 @@ async function runMcpServer() {
|
|
|
31709
31756
|
// src/commands/hookCmd.ts
|
|
31710
31757
|
import { spawn, spawnSync as spawnSync2 } from "node:child_process";
|
|
31711
31758
|
import {
|
|
31712
|
-
existsSync as
|
|
31759
|
+
existsSync as existsSync6,
|
|
31713
31760
|
mkdirSync as mkdirSync4,
|
|
31714
31761
|
mkdtempSync,
|
|
31715
31762
|
readFileSync as readFileSync3,
|
|
@@ -31718,7 +31765,7 @@ import {
|
|
|
31718
31765
|
writeFileSync as writeFileSync3
|
|
31719
31766
|
} from "node:fs";
|
|
31720
31767
|
import { tmpdir } from "node:os";
|
|
31721
|
-
import { join as
|
|
31768
|
+
import { join as join9 } from "node:path";
|
|
31722
31769
|
import { createHash, randomBytes } from "node:crypto";
|
|
31723
31770
|
var silentLogger = {
|
|
31724
31771
|
info: () => {
|
|
@@ -31768,7 +31815,7 @@ function searchCaveatsFromTextSafely(text) {
|
|
|
31768
31815
|
let db;
|
|
31769
31816
|
try {
|
|
31770
31817
|
const ctx = buildContextSafely();
|
|
31771
|
-
if (!ctx || !
|
|
31818
|
+
if (!ctx || !existsSync6(ctx.paths.dbPath)) return [];
|
|
31772
31819
|
db = openDb({ path: ctx.paths.dbPath });
|
|
31773
31820
|
const hits = findCaveatsForPrompt(db, text, {
|
|
31774
31821
|
selfIdentity: defaultSelfIdentityTokens()
|
|
@@ -31852,7 +31899,7 @@ function stopSignalKey(signals, related) {
|
|
|
31852
31899
|
return createHash("sha256").update(body).digest("hex");
|
|
31853
31900
|
}
|
|
31854
31901
|
function stopStatePath(caveatHome, sessionId) {
|
|
31855
|
-
return
|
|
31902
|
+
return join9(caveatHome, CLAUDE_STOP_STATE_DIR, `${sanitizeClaudeStateId(sessionId)}.txt`);
|
|
31856
31903
|
}
|
|
31857
31904
|
function wasStopReminderQueued(caveatHome, sessionId, key) {
|
|
31858
31905
|
const path = stopStatePath(caveatHome, sessionId);
|
|
@@ -31864,7 +31911,7 @@ function wasStopReminderQueued(caveatHome, sessionId, key) {
|
|
|
31864
31911
|
}
|
|
31865
31912
|
function markStopReminderQueued(caveatHome, sessionId, key) {
|
|
31866
31913
|
const path = stopStatePath(caveatHome, sessionId);
|
|
31867
|
-
mkdirSync4(
|
|
31914
|
+
mkdirSync4(join9(caveatHome, CLAUDE_STOP_STATE_DIR), { recursive: true });
|
|
31868
31915
|
writeFileSync3(path, key, "utf-8");
|
|
31869
31916
|
}
|
|
31870
31917
|
function queueStopForSession(sessionId, signals, related) {
|
|
@@ -31915,7 +31962,7 @@ function isToolError(payload) {
|
|
|
31915
31962
|
return false;
|
|
31916
31963
|
}
|
|
31917
31964
|
function spawnWorker(job) {
|
|
31918
|
-
const workFile =
|
|
31965
|
+
const workFile = join9(
|
|
31919
31966
|
tmpdir(),
|
|
31920
31967
|
`caveat-worker-${Date.now()}-${randomBytes(4).toString("hex")}.json`
|
|
31921
31968
|
);
|
|
@@ -31979,7 +32026,7 @@ function buildToolErrorReminder(job, hits) {
|
|
|
31979
32026
|
const mode = hookCodexSidecarMode();
|
|
31980
32027
|
if (mode === "off") return base;
|
|
31981
32028
|
const projectRoot = process.cwd();
|
|
31982
|
-
const hasSidecarConfig =
|
|
32029
|
+
const hasSidecarConfig = existsSync6(join9(projectRoot, ".codex-sidecar.yml"));
|
|
31983
32030
|
if (mode === "auto" && !hasSidecarConfig) return base;
|
|
31984
32031
|
const advisory = runCodexSidecarAdvisory({
|
|
31985
32032
|
searchText: job.searchText,
|
|
@@ -32016,7 +32063,7 @@ function buildStopReminder(signals, related) {
|
|
|
32016
32063
|
const mode = hookCodexSidecarMode();
|
|
32017
32064
|
if (mode === "off") return base;
|
|
32018
32065
|
const projectRoot = process.cwd();
|
|
32019
|
-
const hasSidecarConfig =
|
|
32066
|
+
const hasSidecarConfig = existsSync6(join9(projectRoot, ".codex-sidecar.yml"));
|
|
32020
32067
|
if (mode === "auto" && !hasSidecarConfig) return base;
|
|
32021
32068
|
const advisory = runCodexSidecarAdvisory({
|
|
32022
32069
|
searchText: struggleSearchText(signals),
|
|
@@ -32081,8 +32128,8 @@ function runCodexSidecarAdvisory(input) {
|
|
|
32081
32128
|
"--availability",
|
|
32082
32129
|
"operational"
|
|
32083
32130
|
];
|
|
32084
|
-
const resultDir = mkdtempSync(
|
|
32085
|
-
const resultFile =
|
|
32131
|
+
const resultDir = mkdtempSync(join9(tmpdir(), "caveat-codex-advisory-"));
|
|
32132
|
+
const resultFile = join9(resultDir, "result.json");
|
|
32086
32133
|
args.push("--save-result", resultFile);
|
|
32087
32134
|
const nodeCli = process.env.CAVEAT_CODEX_SIDECAR_NODE_CLI;
|
|
32088
32135
|
if (nodeCli) {
|
|
@@ -32197,14 +32244,14 @@ async function runHook(name, arg) {
|
|
|
32197
32244
|
|
|
32198
32245
|
// src/commands/codexHookCmd.ts
|
|
32199
32246
|
import { spawn as spawn2, spawnSync as spawnSync3 } from "node:child_process";
|
|
32200
|
-
import { existsSync as
|
|
32247
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "node:fs";
|
|
32201
32248
|
import { homedir as homedir3, tmpdir as tmpdir2 } from "node:os";
|
|
32202
|
-
import { join as
|
|
32249
|
+
import { join as join11 } from "node:path";
|
|
32203
32250
|
import { createHash as createHash2, randomBytes as randomBytes2 } from "node:crypto";
|
|
32204
32251
|
|
|
32205
32252
|
// src/codexHookInstall.ts
|
|
32206
|
-
import { copyFileSync as copyFileSync2, existsSync as
|
|
32207
|
-
import { dirname as dirname5, join as
|
|
32253
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
32254
|
+
import { dirname as dirname5, join as join10 } from "node:path";
|
|
32208
32255
|
function quote2(p) {
|
|
32209
32256
|
return p.includes(" ") ? `"${p}"` : p;
|
|
32210
32257
|
}
|
|
@@ -32217,20 +32264,24 @@ function eventCommandFragment(event) {
|
|
|
32217
32264
|
function isSameHookCommand2(actual, expected) {
|
|
32218
32265
|
return actual === expected || actual.endsWith(` ${expected}`);
|
|
32219
32266
|
}
|
|
32267
|
+
function isCaveatCodexHookCommand(actual, event) {
|
|
32268
|
+
const lower = actual.toLowerCase();
|
|
32269
|
+
return lower.includes("caveat") && actual.includes(eventCommandFragment(event));
|
|
32270
|
+
}
|
|
32220
32271
|
function hasCaveatHook(hooksJson, event, fragment) {
|
|
32221
32272
|
return hooksJson.hooks?.[event]?.some(
|
|
32222
32273
|
(entry) => entry.hooks?.some((h) => h.command.includes(fragment))
|
|
32223
32274
|
) ?? false;
|
|
32224
32275
|
}
|
|
32225
32276
|
function readHooks(path) {
|
|
32226
|
-
if (!
|
|
32277
|
+
if (!existsSync7(path)) return {};
|
|
32227
32278
|
return JSON.parse(readFileSync4(path, "utf-8"));
|
|
32228
32279
|
}
|
|
32229
32280
|
function writeJsonWithBackup(path, value) {
|
|
32230
32281
|
const dir = dirname5(path);
|
|
32231
|
-
if (!
|
|
32282
|
+
if (!existsSync7(dir)) mkdirSync5(dir, { recursive: true });
|
|
32232
32283
|
let backupPath = "";
|
|
32233
|
-
if (
|
|
32284
|
+
if (existsSync7(path)) {
|
|
32234
32285
|
backupPath = `${path}.caveat-backup-${Date.now()}`;
|
|
32235
32286
|
copyFileSync2(path, backupPath);
|
|
32236
32287
|
}
|
|
@@ -32238,13 +32289,21 @@ function writeJsonWithBackup(path, value) {
|
|
|
32238
32289
|
`, "utf-8");
|
|
32239
32290
|
return backupPath;
|
|
32240
32291
|
}
|
|
32241
|
-
function upsertHook2(hooksJson, event, command) {
|
|
32292
|
+
function upsertHook2(hooksJson, event, command, subcommand) {
|
|
32242
32293
|
hooksJson.hooks ??= {};
|
|
32243
32294
|
const list = hooksJson.hooks[event] ??= [];
|
|
32244
|
-
const
|
|
32245
|
-
(
|
|
32246
|
-
|
|
32247
|
-
|
|
32295
|
+
for (const entry of list) {
|
|
32296
|
+
for (const hook of entry.hooks ?? []) {
|
|
32297
|
+
if (isSameHookCommand2(hook.command, command)) return "unchanged";
|
|
32298
|
+
if (isCaveatCodexHookCommand(hook.command, subcommand)) {
|
|
32299
|
+
hook.command = command;
|
|
32300
|
+
hook.timeoutSec = 5;
|
|
32301
|
+
hook.async = false;
|
|
32302
|
+
hook.statusMessage = null;
|
|
32303
|
+
return "added";
|
|
32304
|
+
}
|
|
32305
|
+
}
|
|
32306
|
+
}
|
|
32248
32307
|
list.push({
|
|
32249
32308
|
hooks: [
|
|
32250
32309
|
{
|
|
@@ -32258,12 +32317,14 @@ function upsertHook2(hooksJson, event, command) {
|
|
|
32258
32317
|
});
|
|
32259
32318
|
return "added";
|
|
32260
32319
|
}
|
|
32261
|
-
function removeHook2(hooksJson, event, command) {
|
|
32320
|
+
function removeHook2(hooksJson, event, command, subcommand) {
|
|
32262
32321
|
const list = hooksJson.hooks?.[event];
|
|
32263
32322
|
if (!list) return false;
|
|
32264
32323
|
const before = list.length;
|
|
32265
32324
|
const filtered = list.filter(
|
|
32266
|
-
(entry) => !entry.hooks?.some(
|
|
32325
|
+
(entry) => !entry.hooks?.some(
|
|
32326
|
+
(h) => isSameHookCommand2(h.command, command) || isCaveatCodexHookCommand(h.command, subcommand)
|
|
32327
|
+
)
|
|
32267
32328
|
);
|
|
32268
32329
|
if (filtered.length === before) return false;
|
|
32269
32330
|
hooksJson.hooks[event] = filtered;
|
|
@@ -32297,9 +32358,9 @@ codex_hooks = true
|
|
|
32297
32358
|
}
|
|
32298
32359
|
function writeConfigWithBackup(path, text) {
|
|
32299
32360
|
const dir = dirname5(path);
|
|
32300
|
-
if (!
|
|
32361
|
+
if (!existsSync7(dir)) mkdirSync5(dir, { recursive: true });
|
|
32301
32362
|
let backupPath = "";
|
|
32302
|
-
if (
|
|
32363
|
+
if (existsSync7(path)) {
|
|
32303
32364
|
backupPath = `${path}.caveat-backup-${Date.now()}`;
|
|
32304
32365
|
copyFileSync2(path, backupPath);
|
|
32305
32366
|
}
|
|
@@ -32307,25 +32368,28 @@ function writeConfigWithBackup(path, text) {
|
|
|
32307
32368
|
return backupPath;
|
|
32308
32369
|
}
|
|
32309
32370
|
function installCodexHooks(opts) {
|
|
32310
|
-
const hooksPath =
|
|
32311
|
-
const configPath =
|
|
32371
|
+
const hooksPath = join10(opts.codexHome, "hooks.json");
|
|
32372
|
+
const configPath = join10(opts.codexHome, "config.toml");
|
|
32312
32373
|
const hooksJson = readHooks(hooksPath);
|
|
32313
32374
|
const userPromptSubmit = upsertHook2(
|
|
32314
32375
|
hooksJson,
|
|
32315
32376
|
"UserPromptSubmit",
|
|
32316
|
-
hookCommand2(opts.nodePath, opts.cliScriptPath, "user-prompt-submit")
|
|
32377
|
+
hookCommand2(opts.nodePath, opts.cliScriptPath, "user-prompt-submit"),
|
|
32378
|
+
"user-prompt-submit"
|
|
32317
32379
|
);
|
|
32318
32380
|
const postToolUse = upsertHook2(
|
|
32319
32381
|
hooksJson,
|
|
32320
32382
|
"PostToolUse",
|
|
32321
|
-
hookCommand2(opts.nodePath, opts.cliScriptPath, "post-tool-use")
|
|
32383
|
+
hookCommand2(opts.nodePath, opts.cliScriptPath, "post-tool-use"),
|
|
32384
|
+
"post-tool-use"
|
|
32322
32385
|
);
|
|
32323
32386
|
const stop = upsertHook2(
|
|
32324
32387
|
hooksJson,
|
|
32325
32388
|
"Stop",
|
|
32326
|
-
hookCommand2(opts.nodePath, opts.cliScriptPath, "stop")
|
|
32389
|
+
hookCommand2(opts.nodePath, opts.cliScriptPath, "stop"),
|
|
32390
|
+
"stop"
|
|
32327
32391
|
);
|
|
32328
|
-
const rawConfig =
|
|
32392
|
+
const rawConfig = existsSync7(configPath) ? readFileSync4(configPath, "utf-8") : "";
|
|
32329
32393
|
const enabled = enableCodexHooksFeature(rawConfig);
|
|
32330
32394
|
const anyHookAdded = userPromptSubmit === "added" || postToolUse === "added" || stop === "added";
|
|
32331
32395
|
let backupPath;
|
|
@@ -32351,22 +32415,25 @@ function installCodexHooks(opts) {
|
|
|
32351
32415
|
};
|
|
32352
32416
|
}
|
|
32353
32417
|
function uninstallCodexHooks(opts) {
|
|
32354
|
-
const hooksPath =
|
|
32418
|
+
const hooksPath = join10(opts.codexHome, "hooks.json");
|
|
32355
32419
|
const hooksJson = readHooks(hooksPath);
|
|
32356
32420
|
const userPromptSubmitRemoved = removeHook2(
|
|
32357
32421
|
hooksJson,
|
|
32358
32422
|
"UserPromptSubmit",
|
|
32359
|
-
hookCommand2(opts.nodePath, opts.cliScriptPath, "user-prompt-submit")
|
|
32423
|
+
hookCommand2(opts.nodePath, opts.cliScriptPath, "user-prompt-submit"),
|
|
32424
|
+
"user-prompt-submit"
|
|
32360
32425
|
);
|
|
32361
32426
|
const postToolUseRemoved = removeHook2(
|
|
32362
32427
|
hooksJson,
|
|
32363
32428
|
"PostToolUse",
|
|
32364
|
-
hookCommand2(opts.nodePath, opts.cliScriptPath, "post-tool-use")
|
|
32429
|
+
hookCommand2(opts.nodePath, opts.cliScriptPath, "post-tool-use"),
|
|
32430
|
+
"post-tool-use"
|
|
32365
32431
|
);
|
|
32366
32432
|
const stopRemoved = removeHook2(
|
|
32367
32433
|
hooksJson,
|
|
32368
32434
|
"Stop",
|
|
32369
|
-
hookCommand2(opts.nodePath, opts.cliScriptPath, "stop")
|
|
32435
|
+
hookCommand2(opts.nodePath, opts.cliScriptPath, "stop"),
|
|
32436
|
+
"stop"
|
|
32370
32437
|
);
|
|
32371
32438
|
let backupPath;
|
|
32372
32439
|
if (opts.dryRun) {
|
|
@@ -32386,7 +32453,7 @@ function uninstallCodexHooks(opts) {
|
|
|
32386
32453
|
};
|
|
32387
32454
|
}
|
|
32388
32455
|
function detectCodexHookInstallation(codexHome) {
|
|
32389
|
-
const hooksPath =
|
|
32456
|
+
const hooksPath = join10(codexHome, "hooks.json");
|
|
32390
32457
|
const hooksJson = readHooks(hooksPath);
|
|
32391
32458
|
const hooks = {
|
|
32392
32459
|
userPromptSubmit: hasCaveatHook(
|
|
@@ -32450,7 +32517,7 @@ function searchCaveatsFromTextSafely2(text) {
|
|
|
32450
32517
|
let db;
|
|
32451
32518
|
try {
|
|
32452
32519
|
const ctx = buildContextSafely2();
|
|
32453
|
-
if (!ctx || !
|
|
32520
|
+
if (!ctx || !existsSync8(ctx.paths.dbPath)) return [];
|
|
32454
32521
|
db = openDb({ path: ctx.paths.dbPath });
|
|
32455
32522
|
const hits = findCaveatsForPrompt(db, text, {
|
|
32456
32523
|
selfIdentity: defaultSelfIdentityTokens()
|
|
@@ -32519,7 +32586,7 @@ function processExitCodeFromText(text) {
|
|
|
32519
32586
|
return m ? Number(m[1]) : null;
|
|
32520
32587
|
}
|
|
32521
32588
|
function transcriptToolOutput(transcriptPath, toolUseId) {
|
|
32522
|
-
if (!transcriptPath || !toolUseId || !
|
|
32589
|
+
if (!transcriptPath || !toolUseId || !existsSync8(transcriptPath)) return null;
|
|
32523
32590
|
let raw = "";
|
|
32524
32591
|
try {
|
|
32525
32592
|
raw = readFileSync5(transcriptPath, "utf-8");
|
|
@@ -32660,7 +32727,7 @@ function stopSignalKey2(signals, related) {
|
|
|
32660
32727
|
return createHash2("sha256").update(body).digest("hex");
|
|
32661
32728
|
}
|
|
32662
32729
|
function stopStatePath2(caveatHome, sessionId) {
|
|
32663
|
-
return
|
|
32730
|
+
return join11(caveatHome, CODEX_STOP_STATE_DIR, `${sanitizeCodexStateId(sessionId)}.txt`);
|
|
32664
32731
|
}
|
|
32665
32732
|
function wasStopReminderQueued2(caveatHome, sessionId, key) {
|
|
32666
32733
|
const path = stopStatePath2(caveatHome, sessionId);
|
|
@@ -32672,7 +32739,7 @@ function wasStopReminderQueued2(caveatHome, sessionId, key) {
|
|
|
32672
32739
|
}
|
|
32673
32740
|
function markStopReminderQueued2(caveatHome, sessionId, key) {
|
|
32674
32741
|
const path = stopStatePath2(caveatHome, sessionId);
|
|
32675
|
-
mkdirSync6(
|
|
32742
|
+
mkdirSync6(join11(caveatHome, CODEX_STOP_STATE_DIR), { recursive: true });
|
|
32676
32743
|
writeFileSync5(path, key, "utf-8");
|
|
32677
32744
|
}
|
|
32678
32745
|
function queueStopForSession2(sessionId, signals, related) {
|
|
@@ -32743,7 +32810,7 @@ async function runCodexWorker(workFile) {
|
|
|
32743
32810
|
await processCodexWorkerJob(job);
|
|
32744
32811
|
process.exit(0);
|
|
32745
32812
|
}
|
|
32746
|
-
function runDiagnostics(codexHome = process.env.CODEX_HOME ??
|
|
32813
|
+
function runDiagnostics(codexHome = process.env.CODEX_HOME ?? join11(homedir3(), ".codex")) {
|
|
32747
32814
|
const features = spawnSync3("codex", ["features", "list"], {
|
|
32748
32815
|
encoding: "utf-8",
|
|
32749
32816
|
maxBuffer: 1024 * 1024
|
|
@@ -32820,10 +32887,10 @@ async function runCodexHook(name, arg) {
|
|
|
32820
32887
|
}
|
|
32821
32888
|
|
|
32822
32889
|
// src/commands/pull.ts
|
|
32823
|
-
import { existsSync as
|
|
32824
|
-
import { join as
|
|
32890
|
+
import { existsSync as existsSync9, readdirSync as readdirSync4 } from "node:fs";
|
|
32891
|
+
import { join as join12 } from "node:path";
|
|
32825
32892
|
async function runPull(ctx) {
|
|
32826
|
-
if (!
|
|
32893
|
+
if (!existsSync9(ctx.paths.communityDir)) {
|
|
32827
32894
|
ctx.logger.info(
|
|
32828
32895
|
"no community repos yet \u2014 add one with `caveat community add <github-url>`."
|
|
32829
32896
|
);
|
|
@@ -32843,15 +32910,15 @@ async function runPull(ctx) {
|
|
|
32843
32910
|
const db = openDb({ path: ctx.paths.dbPath, logger: ctx.logger });
|
|
32844
32911
|
try {
|
|
32845
32912
|
rebuildAll(db);
|
|
32846
|
-
if (
|
|
32913
|
+
if (existsSync9(ctx.paths.entriesDir)) {
|
|
32847
32914
|
const own = scanSource({ db, source: "own", entriesRoot: ctx.paths.entriesDir });
|
|
32848
32915
|
ctx.logger.info(`own: +${own.added}`);
|
|
32849
32916
|
}
|
|
32850
32917
|
for (const entry of readdirSync4(ctx.paths.communityDir, { withFileTypes: true })) {
|
|
32851
32918
|
if (!entry.isDirectory()) continue;
|
|
32852
32919
|
const source = `community/${entry.name}`;
|
|
32853
|
-
const root =
|
|
32854
|
-
if (!
|
|
32920
|
+
const root = join12(ctx.paths.communityDir, entry.name, "entries");
|
|
32921
|
+
if (!existsSync9(root)) continue;
|
|
32855
32922
|
const scan = scanSource({ db, source, entriesRoot: root });
|
|
32856
32923
|
ctx.logger.info(`${source}: +${scan.added}`);
|
|
32857
32924
|
}
|
|
@@ -32864,7 +32931,7 @@ async function runPull(ctx) {
|
|
|
32864
32931
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
32865
32932
|
import { mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync2, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
|
|
32866
32933
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
32867
|
-
import { dirname as dirname6, join as
|
|
32934
|
+
import { dirname as dirname6, join as join13 } from "node:path";
|
|
32868
32935
|
import { cwd, exit } from "node:process";
|
|
32869
32936
|
function runCodexSidecarDiagnostics(logger, opts) {
|
|
32870
32937
|
const plan = buildCodexSidecarDiagnosticsCommand({
|
|
@@ -32896,8 +32963,8 @@ function runCodexSidecarWithCaveats(ctx, workflow, prompt, opts) {
|
|
|
32896
32963
|
process.stdout.write(JSON.stringify({ status: "skipped", decision }, null, 2) + "\n");
|
|
32897
32964
|
exit(0);
|
|
32898
32965
|
}
|
|
32899
|
-
const contextDir = mkdtempSync2(
|
|
32900
|
-
const contextFile =
|
|
32966
|
+
const contextDir = mkdtempSync2(join13(tmpdir3(), "caveat-sidecar-context-"));
|
|
32967
|
+
const contextFile = join13(contextDir, "context.json");
|
|
32901
32968
|
let status = 1;
|
|
32902
32969
|
try {
|
|
32903
32970
|
const blocks = collectCaveatContextBlocks(ctx, {
|
|
@@ -33152,7 +33219,7 @@ codexHook.command("install").description("Install Caveat hooks into ~/.codex/hoo
|
|
|
33152
33219
|
const result = installCodexHooks({
|
|
33153
33220
|
codexHome: opts.codexHome,
|
|
33154
33221
|
cliScriptPath,
|
|
33155
|
-
nodePath:
|
|
33222
|
+
nodePath: resolveHookNodePath(),
|
|
33156
33223
|
dryRun: opts.dryRun,
|
|
33157
33224
|
logger: stdoutLogger
|
|
33158
33225
|
});
|
|
@@ -33174,7 +33241,7 @@ codexHook.command("uninstall").description("Remove Caveat-owned Codex hooks from
|
|
|
33174
33241
|
const result = uninstallCodexHooks({
|
|
33175
33242
|
codexHome: opts.codexHome,
|
|
33176
33243
|
cliScriptPath,
|
|
33177
|
-
nodePath:
|
|
33244
|
+
nodePath: resolveHookNodePath(),
|
|
33178
33245
|
dryRun: opts.dryRun,
|
|
33179
33246
|
logger: stdoutLogger
|
|
33180
33247
|
});
|