claude-spotter 1.4.10 → 1.4.12
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/CHANGELOG.md +42 -0
- package/README.ja.md +15 -0
- package/README.md +15 -0
- package/package.json +1 -1
- package/src/cli/codex-hook-cmd.mjs +38 -3
- package/src/core/codex-sidecar-runner.mjs +1 -1
- package/src/version.mjs +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.4.12
|
|
4
|
+
|
|
5
|
+
**macOS/Homebrew install verification docs**。npm registry からの clean global install 後に
|
|
6
|
+
`spotter install` と `spotter codex-hook install` が通ること、Codex hook が versioned
|
|
7
|
+
Homebrew Cellar Node path ではなく安定 symlink を使うことを README に明記した。
|
|
8
|
+
|
|
9
|
+
### 変更点
|
|
10
|
+
|
|
11
|
+
- **編集 [README.md](README.md) / [README.ja.md](README.ja.md)**:
|
|
12
|
+
macOS Homebrew Node 環境では Codex hook command が `/opt/homebrew/bin/node` を使うこと、
|
|
13
|
+
release install smoke 手順 (`npm uninstall -g`, `npm install -g`, `spotter install -y`,
|
|
14
|
+
`spotter codex-hook install`) を追加。
|
|
15
|
+
|
|
16
|
+
### 検証
|
|
17
|
+
|
|
18
|
+
- npm registry からの実インストールで確認:
|
|
19
|
+
`npm uninstall -g claude-spotter`, `npm install -g claude-spotter`,
|
|
20
|
+
`spotter --version`, `spotter install -y`, `spotter codex-hook install` が exit 0。
|
|
21
|
+
|
|
22
|
+
## 1.4.11
|
|
23
|
+
|
|
24
|
+
**macOS Homebrew Node の versioned Cellar path を Codex hook に残さない修正**。
|
|
25
|
+
`spotter codex-hook install` が `/opt/homebrew/Cellar/node/<version>/bin/node` を hook command に
|
|
26
|
+
書くと、Homebrew の Node 更新後に Codex hook が古い Node を指したまま壊れるため、現在の
|
|
27
|
+
Node 実体と一致する PATH 上の安定 symlink を優先するようにした。
|
|
28
|
+
|
|
29
|
+
### 変更点
|
|
30
|
+
|
|
31
|
+
- **編集 [src/cli/codex-hook-cmd.mjs](src/cli/codex-hook-cmd.mjs)**:
|
|
32
|
+
`resolveCodexHookNodePath()` を追加。PATH 上の `node` が `process.execPath` と同じ realpath を
|
|
33
|
+
指す場合は、その安定パスを hook command に使う。該当しない環境では従来通り
|
|
34
|
+
`process.execPath` に fallback するため、Windows / WSL2 の挙動は維持される。
|
|
35
|
+
- 既存の Spotter-owned Codex hook が古い Cellar Node path を指している場合、再実行で現在の
|
|
36
|
+
stable Node path と npm global package path に書き換える。Caveat など他 tool の hook は保持する。
|
|
37
|
+
|
|
38
|
+
### 検証
|
|
39
|
+
|
|
40
|
+
- `node --test` 334 tests / 333 pass / 1 skip 緑。
|
|
41
|
+
- npm registry からの実インストールで確認:
|
|
42
|
+
`npm uninstall -g claude-spotter`, `npm install -g claude-spotter`,
|
|
43
|
+
`spotter --version`, `spotter install -y`, `spotter codex-hook install` が exit 0。
|
|
44
|
+
|
|
3
45
|
## 1.4.10
|
|
4
46
|
|
|
5
47
|
**Claude host primary auditor: Codex CLI 検出で自動採用、なければ Haiku の 2 段選択へ**。
|
package/README.ja.md
CHANGED
|
@@ -43,6 +43,11 @@ cd your-project
|
|
|
43
43
|
spotter install
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
macOS の Homebrew Node 環境では、Codex hook command の Node パスに現在の実体と一致する
|
|
47
|
+
安定 symlink (`/opt/homebrew/bin/node`) を使います。
|
|
48
|
+
`/opt/homebrew/Cellar/node/<version>/...` のような version 固定パスを書かないため、
|
|
49
|
+
Homebrew で Node が更新されても Codex hook が古い Node パスに取り残されません。
|
|
50
|
+
|
|
46
51
|
`v0.3.0` 以降は**プロジェクト単位の明示的 install** を採用しています (v0.2 までの `postinstall` 自動登録はデーモン増殖の主因だったため撤回)。各プロジェクトの `.claude/settings.json` に hook を登録し、そのプロジェクトでの Claude Code セッションのみで有効になります。
|
|
47
52
|
Codex CLI が使える環境では、同じ `spotter install` が user-level の Codex native hooks も登録します。実際に動くプロジェクトは `spotter install` が作る `.spotter/marker.json` で制限されるため、無関係な Codex セッションでは Spotter は起動しません。
|
|
48
53
|
Codex 側では現行の `[features].hooks = true` を有効化し、互換のため旧 `codex_hooks` diagnostics output も認識します。
|
|
@@ -53,6 +58,16 @@ Spotter を upgrade した後、release note で hook 設定変更が案内さ
|
|
|
53
58
|
spotter uninstall # このプロジェクトの hook 登録を解除
|
|
54
59
|
```
|
|
55
60
|
|
|
61
|
+
リリース時の npm install smoke:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm uninstall -g claude-spotter
|
|
65
|
+
npm install -g claude-spotter
|
|
66
|
+
spotter --version
|
|
67
|
+
spotter install -y
|
|
68
|
+
spotter codex-hook install
|
|
69
|
+
```
|
|
70
|
+
|
|
56
71
|
## 動作要件
|
|
57
72
|
|
|
58
73
|
- **Node.js 22.5 以上**
|
package/README.md
CHANGED
|
@@ -43,6 +43,11 @@ cd your-project
|
|
|
43
43
|
spotter install
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
On macOS with Homebrew Node, Codex hook commands use the stable
|
|
47
|
+
`/opt/homebrew/bin/node` symlink when it resolves to the current Node binary,
|
|
48
|
+
instead of a versioned `/opt/homebrew/Cellar/node/<version>/...` path. That keeps
|
|
49
|
+
Codex hooks working across Homebrew Node upgrades.
|
|
50
|
+
|
|
46
51
|
Since `v0.3.0`, Spotter requires **explicit per-project install** (the earlier `postinstall` auto-registration was the leading cause of orphan daemons). `spotter install` writes hooks into the project's `.claude/settings.json`; the audit is then active only in Claude Code sessions for that project.
|
|
47
52
|
When the Codex CLI is available, the same `spotter install` also registers user-level Codex native hooks. Project activation still depends on the same per-project `.spotter/marker.json`, so unrelated Codex sessions do not trigger Spotter.
|
|
48
53
|
For Codex, install enables the current `[features].hooks = true` flag and still recognizes older `codex_hooks` diagnostics output for compatibility.
|
|
@@ -53,6 +58,16 @@ After upgrading Spotter, re-run `spotter install` in each installed project when
|
|
|
53
58
|
spotter uninstall # remove hooks from this project
|
|
54
59
|
```
|
|
55
60
|
|
|
61
|
+
Release install smoke:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm uninstall -g claude-spotter
|
|
65
|
+
npm install -g claude-spotter
|
|
66
|
+
spotter --version
|
|
67
|
+
spotter install -y
|
|
68
|
+
spotter codex-hook install
|
|
69
|
+
```
|
|
70
|
+
|
|
56
71
|
## Requirements
|
|
57
72
|
|
|
58
73
|
- **Node.js 22.5+**
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import { existsSync } from 'node:fs';
|
|
2
|
+
import { existsSync, realpathSync } from 'node:fs';
|
|
3
3
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
|
-
import { dirname, join, resolve } from 'node:path';
|
|
5
|
+
import { delimiter, dirname, join, resolve } from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { createAuditorBackend } from '../core/auditor-backend.mjs';
|
|
8
8
|
import { codexLastAssistantMessage, readCodexUsedTools } from '../core/codex-transcript.mjs';
|
|
@@ -315,7 +315,7 @@ export async function runCodexHookDiagnosticsCommand({
|
|
|
315
315
|
writeOutput(JSON.stringify(result, null, 2) + '\n');
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
-
export async function installCodexHooks({ codexHome = defaultCodexHome(), nodePath =
|
|
318
|
+
export async function installCodexHooks({ codexHome = defaultCodexHome(), nodePath = resolveCodexHookNodePath(), spotterBin = SPOTTER_BIN } = {}) {
|
|
319
319
|
const hooksPath = join(codexHome, 'hooks.json');
|
|
320
320
|
const configPath = join(codexHome, 'config.toml');
|
|
321
321
|
const current = await loadJson(hooksPath);
|
|
@@ -340,6 +340,41 @@ export async function installCodexHooks({ codexHome = defaultCodexHome(), nodePa
|
|
|
340
340
|
};
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
+
function safeRealpath(path, realpath = realpathSync.native) {
|
|
344
|
+
try {
|
|
345
|
+
return realpath(path);
|
|
346
|
+
} catch {
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export function resolveCodexHookNodePath({
|
|
352
|
+
env = process.env,
|
|
353
|
+
execPath = process.execPath,
|
|
354
|
+
platform = process.platform,
|
|
355
|
+
exists = existsSync,
|
|
356
|
+
realpath = realpathSync.native,
|
|
357
|
+
} = {}) {
|
|
358
|
+
const execRealpath = safeRealpath(execPath, realpath);
|
|
359
|
+
const pathEnv = env.PATH || env.Path || '';
|
|
360
|
+
const names = platform === 'win32'
|
|
361
|
+
? ['node.exe', 'node.cmd', 'node.bat', 'node']
|
|
362
|
+
: ['node'];
|
|
363
|
+
|
|
364
|
+
for (const dir of pathEnv.split(delimiter).filter(Boolean)) {
|
|
365
|
+
for (const name of names) {
|
|
366
|
+
const candidate = join(dir, name);
|
|
367
|
+
if (!exists(candidate)) continue;
|
|
368
|
+
const candidateRealpath = safeRealpath(candidate, realpath);
|
|
369
|
+
if (execRealpath && candidateRealpath && candidateRealpath === execRealpath) {
|
|
370
|
+
return candidate;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return execPath;
|
|
376
|
+
}
|
|
377
|
+
|
|
343
378
|
export async function uninstallCodexHooks({ codexHome = defaultCodexHome() } = {}) {
|
|
344
379
|
const hooksPath = join(codexHome, 'hooks.json');
|
|
345
380
|
const current = await loadJson(hooksPath);
|
|
@@ -468,7 +468,7 @@ function execPortable(execFileFn, cmd, args, spawnOptions) {
|
|
|
468
468
|
windowsHide: true,
|
|
469
469
|
maxBuffer: 10 * 1024 * 1024,
|
|
470
470
|
};
|
|
471
|
-
if (process.platform === 'win32') {
|
|
471
|
+
if (process.platform === 'win32' && cmd !== process.execPath) {
|
|
472
472
|
return execFileFn('cmd.exe', ['/c', cmd, ...args], options);
|
|
473
473
|
}
|
|
474
474
|
return execFileFn(cmd, args, options);
|
package/src/version.mjs
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
4
|
+
|
|
5
|
+
const pkgPath = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
|
|
6
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
2
7
|
export const version = pkg.version;
|