claude-spotter 1.4.20 → 1.4.22
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 +28 -0
- package/README.ja.md +7 -9
- package/README.md +9 -9
- package/bin/spotter.mjs +4 -2
- package/package.json +1 -1
- package/src/cli/doctor.mjs +8 -1
- package/src/cli/install.mjs +76 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.4.22 — 2026-07-13
|
|
4
|
+
|
|
5
|
+
- **default-on裁定の文書修正。** v1.4.21の実装どおりdefault-onは確定事項であり、7日・30 fresh resultの測定は精度改善にだけ使う。ON/OFFの再審査やdefault-off rollbackを選択肢としていた誤記をREADME、正典、SLO、計画、課題台帳から削除した。
|
|
6
|
+
|
|
7
|
+
## 1.4.21 — 2026-07-13
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **Throughline監査文脈をdefault-onへ変更。** `spotter install`がPATH上のThroughlineをabsolute pathへ解決できる場合、project markerへ既定設定する。marker v2の`origin:default|explicit`で旧既定disabledだけを移行し、明示OFFは再install後も維持する。POSIXはrealpath、Windows npm shimはabsolute `node.exe + throughline.mjs`を保存する。
|
|
12
|
+
- **不在時は明示disabled。** Throughlineが見つからない場合は固定理由付きdisabledとし、current-only監査や別backendへfallbackしない。installerとdoctorが状態・OFF手順を表示する。
|
|
13
|
+
- **実運用効果測定。** default-onはowner確定済み。7日以上・fresh 30件以上の過検出・見逃し・stale率・latencyは精度改善にだけ使い、ON/OFFを再審査しない。L2本文は評価ログへ保存しない。
|
|
14
|
+
|
|
15
|
+
### 検証・公開
|
|
16
|
+
|
|
17
|
+
478 tests(476 pass / 2 skip)、macOS/Linux/Windows × Node 22.5/22.xのCI 6/6、packの秘密・
|
|
18
|
+
開発者固有path scan、隔離tarball installを通過。公開commitは`5026ace`。npm `claude-spotter@1.4.21`、
|
|
19
|
+
tag / GitHub Release `v1.4.21`、registry由来global installを同期した。registry版の空projectへ
|
|
20
|
+
`spotter install -y`を実行し、marker v2がThroughlineのglobal実体を`origin:default`で設定し、
|
|
21
|
+
doctorがconnector availableを返すことを確認した。
|
|
22
|
+
|
|
3
23
|
## 1.4.20 — 2026-07-13
|
|
4
24
|
|
|
5
25
|
### Added
|
|
6
26
|
|
|
7
27
|
- **Throughline監査文脈のopt-in配布。** 新規projectは既定`disabled`のまま、ownerがabsoluteなThroughline commandとrepeatableな先頭引数で有効化できる。freshな完了L2 user/assistant pairだけを`N=2`、body 600文字、total 4,000文字に制限し、Codex CLIへstdinで渡す。Haikuはこのcontext経路では呼ばず、fresh以外でもAIを呼ばない。connector障害は固定警告に限定する。親出力は安全なcatalog tool ID由来の固定・非命令形助言だけで、L2、reason、provider rawを反射しない。7日・30 fresh resultのproduction default昇格gateは未完了のため、配布後もproject opt-inを維持する。
|
|
8
28
|
|
|
29
|
+
### 検証・公開
|
|
30
|
+
|
|
31
|
+
Spotter 476 tests(474 pass / 2 skip)、Throughline 580 tests(全pass)、両CIのmacOS/Linux/Windows ×
|
|
32
|
+
Node matrix各6/6、packの秘密・開発者固有path scan、隔離tarball installを通過。公開commitは`7cbc3a1`、
|
|
33
|
+
npm `claude-spotter@1.4.20`、tag / GitHub Release `v1.4.20`、registry由来global installを同期した。
|
|
34
|
+
`spotter doctor`は0 warnings、監査文脈connectorはavailable。Codex hook trustだけは仕様上機械検証不能のため、
|
|
35
|
+
`/hooks`での人手確認を残す。
|
|
36
|
+
|
|
9
37
|
## 1.4.19
|
|
10
38
|
|
|
11
39
|
親セッションの暴走を誘発できたHook出力の信頼境界を修正する。監査用AIは内部で構造化判定を返すだけとし、
|
package/README.ja.md
CHANGED
|
@@ -146,19 +146,18 @@ flowchart LR
|
|
|
146
146
|
|
|
147
147
|
両者に共通するのは **「主体に頼らない仕組み」**。併用できます。
|
|
148
148
|
|
|
149
|
-
###
|
|
149
|
+
### Throughline auditor context(既定ON)
|
|
150
150
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
設定は保持されます。無効化へ戻すには次を実行します。
|
|
151
|
+
`spotter install`はPATH上のThroughlineを絶対パスへ解決できる場合、auditor contextを既定で有効化します。
|
|
152
|
+
旧版の既定`disabled` markerも通常の再installで有効化されます。過去に明示的に無効化したprojectは
|
|
153
|
+
`origin: explicit`として保持され、勝手に再有効化されません。無効化するには次を実行します。
|
|
155
154
|
|
|
156
155
|
```bash
|
|
157
156
|
spotter install -y --auditor-context disabled
|
|
158
157
|
```
|
|
159
158
|
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
自動解決できない環境で手動設定する場合は、絶対パスのThroughline実行ファイルと、必要なら
|
|
160
|
+
繰り返し指定できる`--throughline-arg`を設定します。
|
|
162
161
|
|
|
163
162
|
```bash
|
|
164
163
|
spotter install -y --auditor-context throughline \
|
|
@@ -191,8 +190,7 @@ spotter auditor model-matrix --fixtures test/fixtures/auditor-model-matrix.v2.js
|
|
|
191
190
|
--recent-turns 2 --body-cap 600
|
|
192
191
|
```
|
|
193
192
|
|
|
194
|
-
|
|
195
|
-
7 日・30 fresh result gate は未完了です。
|
|
193
|
+
評価結論はN=2 / 600です。既定ONは確定済みで、7日・30 fresh resultの実運用測定は精度改善に使います。
|
|
196
194
|
|
|
197
195
|
## よく使うコマンド
|
|
198
196
|
|
package/README.md
CHANGED
|
@@ -146,19 +146,19 @@ The audited catalog is host-local: Claude uses `<project>/.spotter/tool-db.json`
|
|
|
146
146
|
|
|
147
147
|
Both share the principle of **"don't rely on the primary agent to do it itself."** They compose well — you can run them together.
|
|
148
148
|
|
|
149
|
-
###
|
|
149
|
+
### Throughline auditor context (default-on)
|
|
150
150
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
151
|
+
When `spotter install` can resolve Throughline on PATH to an absolute executable,
|
|
152
|
+
auditor context is enabled by default. A normal reinstall migrates legacy markers
|
|
153
|
+
whose disabled state came from the old default. An explicit project opt-out is
|
|
154
|
+
stored with `origin: explicit` and is never silently re-enabled. Disable it with:
|
|
155
155
|
|
|
156
156
|
```bash
|
|
157
157
|
spotter install -y --auditor-context disabled
|
|
158
158
|
```
|
|
159
159
|
|
|
160
|
-
|
|
161
|
-
leading arguments with repeatable `--throughline-arg`:
|
|
160
|
+
When automatic discovery is unavailable, configure a direct absolute Throughline
|
|
161
|
+
executable and any leading arguments with repeatable `--throughline-arg`:
|
|
162
162
|
|
|
163
163
|
```bash
|
|
164
164
|
spotter install -y --auditor-context throughline \
|
|
@@ -195,8 +195,8 @@ spotter auditor model-matrix --fixtures test/fixtures/auditor-model-matrix.v2.js
|
|
|
195
195
|
--recent-turns 2 --body-cap 600
|
|
196
196
|
```
|
|
197
197
|
|
|
198
|
-
|
|
199
|
-
|
|
198
|
+
The evaluated setting is N=2 / 600. Default-on is final; the 7-day,
|
|
199
|
+
30-fresh-result sample is used only to identify precision improvements.
|
|
200
200
|
|
|
201
201
|
## Common commands
|
|
202
202
|
|
package/bin/spotter.mjs
CHANGED
|
@@ -25,6 +25,8 @@ Usage:
|
|
|
25
25
|
[--throughline-command ABS] [--throughline-arg VALUE]
|
|
26
26
|
register hooks in <cwd>/.claude/settings.json
|
|
27
27
|
and create <cwd>/.spotter/marker.json
|
|
28
|
+
(Throughline on PATH enables context by default;
|
|
29
|
+
pass disabled for a persistent project opt-out)
|
|
28
30
|
(run inside each project you want audited)
|
|
29
31
|
spotter install --user [-y] legacy: register globally in ~/.claude/settings.json
|
|
30
32
|
(NOT RECOMMENDED — fires for every Claude Code session
|
|
@@ -175,8 +177,8 @@ function parseInstallArgs(argv) {
|
|
|
175
177
|
if (mode === 'disabled' && (command !== null || args.length > 0)) throw invalidInstallArgs();
|
|
176
178
|
if (mode === 'throughline' && (command === null || !isAbsoluteCommand(command) || isShellWrapper(command))) throw invalidInstallArgs();
|
|
177
179
|
const auditorContext = mode === null ? undefined : mode === 'disabled'
|
|
178
|
-
? { mode: 'disabled' }
|
|
179
|
-
: { mode: 'throughline', command, args };
|
|
180
|
+
? { mode: 'disabled', origin: 'explicit' }
|
|
181
|
+
: { mode: 'throughline', command, args, origin: 'explicit' };
|
|
180
182
|
return { target, autoYes, auditorContext };
|
|
181
183
|
}
|
|
182
184
|
|
package/package.json
CHANGED
package/src/cli/doctor.mjs
CHANGED
|
@@ -152,7 +152,14 @@ export async function inspectAuditorContextConfiguration({
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
const config = marker?.auditorContext;
|
|
155
|
-
if (config === undefined
|
|
155
|
+
if (config === undefined) return disabled;
|
|
156
|
+
if (config?.mode === 'disabled') {
|
|
157
|
+
if (config.origin === 'explicit') return { ok: true, mode: 'disabled', detail: 'explicit project opt-out' };
|
|
158
|
+
if (config.reason === 'throughline_unavailable') {
|
|
159
|
+
return { ok: true, mode: 'disabled', detail: 'default disabled: Throughline unavailable' };
|
|
160
|
+
}
|
|
161
|
+
return disabled;
|
|
162
|
+
}
|
|
156
163
|
if (config?.mode !== 'throughline') {
|
|
157
164
|
return { ok: false, mode: 'unknown', detail: 'invalid configuration' };
|
|
158
165
|
}
|
package/src/cli/install.mjs
CHANGED
|
@@ -12,10 +12,11 @@
|
|
|
12
12
|
// Install seeds the DB automatically via `refresh` (project-mode only — user-mode
|
|
13
13
|
// has no projectRoot so DB seeding is skipped there).
|
|
14
14
|
|
|
15
|
-
import { mkdir, writeFile, readFile, access } from 'node:fs/promises';
|
|
15
|
+
import { mkdir, writeFile, readFile, access, realpath } from 'node:fs/promises';
|
|
16
|
+
import { constants as fsConstants } from 'node:fs';
|
|
16
17
|
import { spawnSync } from 'node:child_process';
|
|
17
18
|
import { homedir } from 'node:os';
|
|
18
|
-
import { join, resolve, dirname } from 'node:path';
|
|
19
|
+
import { join, resolve, dirname, delimiter, extname } from 'node:path';
|
|
19
20
|
import { fileURLToPath } from 'node:url';
|
|
20
21
|
import { createInterface } from 'node:readline/promises';
|
|
21
22
|
import { version as SPOTTER_VERSION } from '../version.mjs';
|
|
@@ -29,7 +30,7 @@ const SPOTTER_BIN = join(PACKAGE_ROOT, 'bin', 'spotter.mjs');
|
|
|
29
30
|
|
|
30
31
|
const SPOTTER_HOME = join(homedir(), '.spotter');
|
|
31
32
|
|
|
32
|
-
const MARKER_VERSION = '
|
|
33
|
+
const MARKER_VERSION = '2';
|
|
33
34
|
|
|
34
35
|
// v1.3.0: UserPromptSubmit / Stop を 60s に統一。理由:
|
|
35
36
|
// - daemon 側 Haiku timeout は 45s (DEFAULT_HAIKU_TIMEOUT_MS @ daemon.mjs)
|
|
@@ -58,6 +59,7 @@ export async function runInstall({
|
|
|
58
59
|
codexCliPresentFn = isCodexCliPresent,
|
|
59
60
|
installCodexHooksFn = installCodexHooks,
|
|
60
61
|
auditorContext,
|
|
62
|
+
resolveDefaultAuditorContextFn = resolveDefaultAuditorContext,
|
|
61
63
|
} = {}) {
|
|
62
64
|
const settingsPath = target === 'user'
|
|
63
65
|
? join(homedir(), '.claude', 'settings.json')
|
|
@@ -86,8 +88,8 @@ export async function runInstall({
|
|
|
86
88
|
const markerPath = join(markerDir, 'marker.json');
|
|
87
89
|
await mkdir(markerDir, { recursive: true });
|
|
88
90
|
const preservedAuditorContext = auditorContext === undefined
|
|
89
|
-
? await readExistingAuditorContext(markerPath)
|
|
90
|
-
: auditorContext;
|
|
91
|
+
? await readExistingAuditorContext(markerPath, resolveDefaultAuditorContextFn)
|
|
92
|
+
: { ...auditorContext, origin: 'explicit' };
|
|
91
93
|
const marker = {
|
|
92
94
|
markerVersion: MARKER_VERSION,
|
|
93
95
|
spotterVersion: SPOTTER_VERSION,
|
|
@@ -96,6 +98,13 @@ export async function runInstall({
|
|
|
96
98
|
};
|
|
97
99
|
await writeFile(markerPath, JSON.stringify(marker, null, 2) + '\n', 'utf8');
|
|
98
100
|
console.log(` wrote ${markerPath}`);
|
|
101
|
+
console.log(` auditor context: ${preservedAuditorContext.mode} (${preservedAuditorContext.origin})`);
|
|
102
|
+
if (preservedAuditorContext.mode === 'throughline') {
|
|
103
|
+
console.log(' sends bounded completed user/assistant text to the selected Codex auditor; disable with:');
|
|
104
|
+
console.log(' spotter install -y --auditor-context disabled');
|
|
105
|
+
} else if (preservedAuditorContext.reason === 'throughline_unavailable') {
|
|
106
|
+
console.log(' Throughline was not found; context auditing remains disabled (no current-only fallback)');
|
|
107
|
+
}
|
|
99
108
|
}
|
|
100
109
|
|
|
101
110
|
// 4. compute desired settings.json with hooks
|
|
@@ -176,16 +185,75 @@ export async function runInstall({
|
|
|
176
185
|
}
|
|
177
186
|
}
|
|
178
187
|
|
|
179
|
-
async function readExistingAuditorContext(markerPath) {
|
|
188
|
+
async function readExistingAuditorContext(markerPath, resolveDefaultAuditorContextFn) {
|
|
180
189
|
try {
|
|
181
190
|
const marker = JSON.parse(await readFile(markerPath, 'utf8'));
|
|
182
|
-
|
|
191
|
+
const existing = marker?.auditorContext;
|
|
192
|
+
if (existing?.mode === 'throughline') {
|
|
193
|
+
return { ...existing, origin: existing.origin ?? 'explicit' };
|
|
194
|
+
}
|
|
195
|
+
if (existing?.mode === 'disabled' && existing.origin === 'explicit') {
|
|
196
|
+
return existing;
|
|
197
|
+
}
|
|
198
|
+
return resolveDefaultAuditorContextFn();
|
|
183
199
|
} catch (err) {
|
|
184
|
-
if (err?.code === 'ENOENT') return
|
|
200
|
+
if (err?.code === 'ENOENT') return resolveDefaultAuditorContextFn();
|
|
185
201
|
throw err;
|
|
186
202
|
}
|
|
187
203
|
}
|
|
188
204
|
|
|
205
|
+
export async function resolveDefaultAuditorContext({
|
|
206
|
+
env = process.env,
|
|
207
|
+
platform = process.platform,
|
|
208
|
+
nodePath = process.execPath,
|
|
209
|
+
accessFn = access,
|
|
210
|
+
realpathFn = realpath,
|
|
211
|
+
} = {}) {
|
|
212
|
+
const pathValue = platform === 'win32' ? (env.Path ?? env.PATH ?? '') : (env.PATH ?? '');
|
|
213
|
+
const pathSeparator = platform === 'win32' ? ';' : delimiter;
|
|
214
|
+
const names = platform === 'win32'
|
|
215
|
+
? ['throughline.exe', 'throughline.cmd', 'throughline.bat', 'throughline']
|
|
216
|
+
: ['throughline'];
|
|
217
|
+
|
|
218
|
+
for (const directory of pathValue.split(pathSeparator).filter(Boolean)) {
|
|
219
|
+
for (const name of names) {
|
|
220
|
+
const candidate = join(directory, name);
|
|
221
|
+
if (!await canExecute(candidate, accessFn)) continue;
|
|
222
|
+
const extension = extname(candidate).toLowerCase();
|
|
223
|
+
if (extension === '.cmd' || extension === '.bat') {
|
|
224
|
+
const script = join(directory, 'node_modules', 'throughline', 'bin', 'throughline.mjs');
|
|
225
|
+
if (!await canRead(script, accessFn)) continue;
|
|
226
|
+
return { mode: 'throughline', command: nodePath, args: [script], origin: 'default' };
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
mode: 'throughline',
|
|
230
|
+
command: await realpathFn(candidate),
|
|
231
|
+
args: [],
|
|
232
|
+
origin: 'default',
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return { mode: 'disabled', origin: 'default', reason: 'throughline_unavailable' };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async function canExecute(path, accessFn) {
|
|
240
|
+
try {
|
|
241
|
+
await accessFn(path, fsConstants.X_OK);
|
|
242
|
+
return true;
|
|
243
|
+
} catch {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async function canRead(path, accessFn) {
|
|
249
|
+
try {
|
|
250
|
+
await accessFn(path, fsConstants.R_OK);
|
|
251
|
+
return true;
|
|
252
|
+
} catch {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
189
257
|
export function codexInstallNextSteps(projectRoot) {
|
|
190
258
|
return [
|
|
191
259
|
'review the three Spotter hooks with Codex /hooks',
|