claude-spotter 1.6.0 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,22 @@
3
3
  各節はそのversion公開時点の変更記録であり、後続versionにより置換された仕様を含む。
4
4
  現行runtime契約は[`docs/00_overview.md`](docs/00_overview.md)から辿る。
5
5
 
6
+ ## 1.6.2 — 2026-08-29
7
+
8
+ - **Windowsの`spotter install -y`がMCP調査後に終了する。** 調査対象のprocess treeを
9
+ Windows共通経路で停止し、stdin / stdout / stderrのpipeを停止前後に破棄する。
10
+ カタログ生成後もstdin pipeがNodeのイベントループを保持し、工場セットアップが
11
+ 完了しなかった問題を修正した。
12
+ - **隔離runtime error workerが指定期限内に呼び出し元へ戻る。** timeout時は結果を先に
13
+ 確定し、子process treeのbest-effort掃除を並行して続ける。Windowsで掃除の待機時間が
14
+ 絶対期限へ上乗せされていた問題を修正した。
15
+ - Cursor hookの安定Node path試験を、空白を含むWindows pathの正しい引用形式へ対応させた。
16
+
17
+ ## 1.6.1 — 2026-08-24
18
+
19
+ - **Cursor hook が Homebrew Cellar の版付き Node を焼かない。** `spotter cursor-hook install` は
20
+ Codex hook と同じ安定 Node path を使い、版更新で sessionStart が消えるのを防ぐ。
21
+
6
22
  ## 1.6.0 — 2026-08-24
7
23
 
8
24
  - **Cursor を tool-db host として足した。** `src/host/adapters.mjs` に
@@ -1,6 +1,6 @@
1
1
  # Spotter評価dashboard運用
2
2
 
3
- 現行npm配布版: **v1.6.0**(2026-08-24)。v1.5.13はOS依存・ベンダー依存の内部配置だけを
3
+ 現行npm配布版: **v1.6.2**(2026-08-29)。v1.5.13はOS依存・ベンダー依存の内部配置だけを
4
4
  変更した挙動同一リファクタで、評価・dashboardのrouting構成は変更していない。
5
5
 
6
6
  この文書はservice設定の正本であり、各端末に現在installされているnpm versionの台帳ではない。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-spotter",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
4
4
  "description": "Audit agent running alongside Claude Code that catches missed tool calls — 気づく役と実行する役の分離",
5
5
  "type": "module",
6
6
  "bin": {
@@ -4,6 +4,7 @@ import { homedir } from 'node:os';
4
4
  import { dirname, join, resolve } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { spawnRefreshDetached } from '../hooks/spawn-daemon.mjs';
7
+ import { resolveCodexHookNodePath } from './codex-hook-cmd.mjs';
7
8
  import {
8
9
  die,
9
10
  findSpotterMarker,
@@ -90,7 +91,7 @@ export function cursorCwd(input) {
90
91
 
91
92
  export async function installCursorHooks({
92
93
  cursorHome = defaultCursorHome(),
93
- nodePath = process.execPath,
94
+ nodePath = resolveCodexHookNodePath(),
94
95
  spotterBin = SPOTTER_BIN,
95
96
  } = {}) {
96
97
  const hooksPath = join(cursorHome, 'hooks.json');
@@ -270,10 +270,14 @@ function runRuntimeWorker(workerPath, args, timeoutMs, signal) {
270
270
  signal?.removeEventListener('abort', abort);
271
271
  resolve(result);
272
272
  };
273
- const stop = (kind) => { if (stopping || settled) return; stopping = true; void killWorkerTree(child).then(() => finish({ kind })); };
273
+ const stop = (kind) => {
274
+ if (stopping || settled) return;
275
+ stopping = true;
276
+ finish({ kind });
277
+ void killWorkerTree(child);
278
+ };
274
279
  const abort = () => stop('cancelled');
275
- const killGraceMs = process.platform === 'win32' ? 250 : 0;
276
- const timer = setTimeout(() => stop('timeout'), Math.max(1, timeoutMs - killGraceMs));
280
+ const timer = setTimeout(() => stop('timeout'), timeoutMs);
277
281
  try {
278
282
  child = spawn(process.execPath, [workerPath, ...args], {
279
283
  stdio: 'ignore',
@@ -11,7 +11,11 @@
11
11
 
12
12
  import { spawn } from 'node:child_process';
13
13
  import { isWindowsAbsolutePath } from '../platform/paths.mjs';
14
- import { execFileWindowsSafe, windowsCompatibleCommand } from '../platform/spawn.mjs';
14
+ import {
15
+ execFileWindowsSafe,
16
+ terminateProcessTree,
17
+ windowsCompatibleCommand,
18
+ } from '../platform/spawn.mjs';
15
19
  import { listToolsHttp } from './investigate-mcp-http.mjs';
16
20
  import { readMcpServers, describeServer } from './mcp-config.mjs';
17
21
  import { version as SPOTTER_VERSION } from '../version.mjs';
@@ -253,6 +257,17 @@ export function buildStdioSpawn(command, args) {
253
257
  return { cmd: invocation.command, cmdArgs: invocation.args };
254
258
  }
255
259
 
260
+ export async function closeMcpTransport(child, { terminateChildFn = terminateProcessTree } = {}) {
261
+ const destroyStreams = () => {
262
+ for (const stream of [child.stdin, child.stdout, child.stderr]) {
263
+ try { stream.destroy(); } catch { /* ignore */ }
264
+ }
265
+ };
266
+ destroyStreams();
267
+ try { await terminateChildFn(child); } catch { /* direct kill fallback already ran */ }
268
+ destroyStreams();
269
+ }
270
+
256
271
  async function spawnAndQuery({ command, args, env = {} }, serverName) {
257
272
  return new Promise((resolve, reject) => {
258
273
  const { cmd, cmdArgs } = buildStdioSpawn(command, args);
@@ -267,16 +282,14 @@ async function spawnAndQuery({ command, args, env = {} }, serverName) {
267
282
  let settled = false;
268
283
  let initializedSent = false;
269
284
 
270
- const cleanup = () => {
271
- try { child.stdin.end(); } catch { /* ignore */ }
272
- try { child.kill(); } catch { /* ignore */ }
273
- };
285
+ const cleanup = () => closeMcpTransport(child);
274
286
 
275
287
  const timer = setTimeout(() => {
276
288
  if (settled) return;
277
289
  settled = true;
278
- cleanup();
279
- reject(new McpInvestigationError(`handshake/list timed out after ${HANDSHAKE_TIMEOUT_MS}ms`, serverName));
290
+ void cleanup().then(() => {
291
+ reject(new McpInvestigationError(`handshake/list timed out after ${HANDSHAKE_TIMEOUT_MS}ms`, serverName));
292
+ });
280
293
  }, HANDSHAKE_TIMEOUT_MS);
281
294
 
282
295
  const send = (msg) => {
@@ -347,13 +360,13 @@ async function spawnAndQuery({ command, args, env = {} }, serverName) {
347
360
  if (settled) return;
348
361
  settled = true;
349
362
  clearTimeout(timer);
350
- cleanup();
363
+ await cleanup();
351
364
  resolve(tools);
352
365
  } catch (err) {
353
366
  if (settled) return;
354
367
  settled = true;
355
368
  clearTimeout(timer);
356
- cleanup();
369
+ await cleanup();
357
370
  reject(err);
358
371
  }
359
372
  })();