isitdone について
isitdoneは、エージェントの終了前にリポジトリのテスト、型チェック、lintを実行する、LLMを使わないStopフック兼コマンドラインツールです。テストを弱める変更の検出や複数のエージェントフックに対応し、プルリクエストでも同じ検証を実行できます。
主な機能
- 01エージェント停止前にリポジトリのチェックを実行
- 02テストを弱める変更の検出
- 03関連する編集後にエージェントへ警告
- 04GitHub Actionによるプルリクエスト検証
- 05Git状態に結び付いた検証レシートの作成
活用シーン
- 01チェック失敗時に完了宣言をブロック
- 02エージェントの編集中にテスト弱体化を検出
- 03検証結果をプルリクエストに添付
MCP 対応
READMEでは、停止フックのないエージェント向けにMCPサーバーとして使えると述べていますが、提示された範囲にMCPツールの説明はありません。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
3掲載時のツール一覧 · 2 週間前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| cwd | string | いいえ | リポジトリ、またはその内部ディレクトリの絶対パス。デフォルト:クライアントの最初のワークスペースルート。ない場合は、サーバーの起動ディレクトリ。 |
| base | string | いいえ | テスト整合性スキャンで比較する Git ref。デフォルト:HEAD(未コミットの変更)。 |
| claim | string | いいえ | これから行う完了の主張を引用符で囲んで指定します。証明書に記録されます。 |
| strict | boolean | いいえ | チェックに合格していても、変更によってテストが弱められている場合(重大または高リスクの問題が検出された場合)は NOT DONE と回答します。 |
| profile | enum | いいえ | full(デフォルト)はすべてのチェックを実行します。lite は型チェックと lint のみを実行し、done: true を報告することはありません。lite | full |
詳細
isitdone
テストに実際に合格するまで、コーディングエージェントに「完了」と言わせないでください。
実際の実行(npm run demo)から生成:エージェントの発言は説明で、フックと CLI の出力は、一時パスを短縮した以外はそのまま記録されています。
isitdone は、Claude Code、Codex CLI、Cursor、Gemini CLI、GitHub Copilot CLI、Qwen Code、Goose、Factory Droid、Devin、Augment、OpenCode、Junie CLI 向けの、LLM も依存関係も不要な Stop フック兼 CLI です。エージェントが作業を完了したと主張してターンを終了しようとすると、isitdone はリポジトリの実際のテスト、型チェック、lint コマンドを正確な作業ツリーで実行し、テストが弱められていないか差分をスキャンして、すべてに合格するまで終了を拒否します。また、編集によってテストが弱められた場合はターンの途中でエージェントに通知し、GitHub Action としてプルリクエストでも同じ検証を実行します。停止フックを持たないエージェント向けには MCP サーバーとして提供され、プルリクエストに貼り付けられる Git に紐づいたレシートも残します。
npx isitdone init
API キーは不要です。ネットワーク通信もテレメトリーもありません。あるのは終了コードだけです。
これが必要になる場面
Claude Code が次のようにターンを終了します:
完了しました。4 つのテストすべてに合格し、認証のリファクタリングも完了しました。
そのターンを終了できるようになる前に、Stop フックが isitdone を実行します。エージェントには代わりに次のように返されます:
isitdone: NOT DONE. 1 check failed on the current working tree (attempt 1/3).
You claimed: "All 4 tests pass and the auth refactor is complete."
npm run typecheck PASS
npm run lint PASS
npm test FAIL 1 failed, 3 passed
--- npm test (last 24 lines) ---
✖ failing tests:
test at test/auth.test.js:14:1
✖ is case-insensitive about the scheme (1.498ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
null !== 'abc'
...
Fix the failures, then run `npx isitdone` and paste its output before claiming completion.
Do not skip, delete or weaken tests to make this pass; if a check is wrong for this repo,
say so explicitly to the user.
エージェントは作業を続けます。本当に完了すると、次のようになります:
$ npx isitdone
isitdone main@2429c82 dirty (1 file)
npm run typecheck PASS 428ms
npm run lint PASS 411ms
npm test PASS 532ms
DONE receipt -> PASS (tree e5e47d9, 3 checks, 1.8s)
$ npx isitdone receipt --md # paste into the PR
| check | result | time |
|---|---|---|
| `npm run typecheck` | PASS | 428ms |
| `npm run lint` | PASS | 411ms |
| `npm test` | PASS | 532ms |
Tree e5e47d9 on main@2429c82 (+1 uncommitted). Receipt: **PASS** · isitdone 0.1.0
さらに 1 つファイルを編集すると、チェックを再実行するまでレシートは STALE になります。手作業で編集したレシートは NONE と表示されます。(npm の isitdone は @aivolution/isitdone の短縮エイリアスです。どちらのコマンドも同じプログラムを実行します。)
実際にはどのくらいの頻度で起きているのでしょうか?
ご自身のマシンで測定できます。isitdone history は、すでにディスク上にあるトランスクリプトを読み取ります。Claude Code(~/.claude/projects)、Codex CLI(~/.codex/sessions:旧形式、ページ分割形式、0.40 より前のロールアウトに対応し、/undo によるロールバックを考慮)、Gemini CLI(~/.gemini/tmp/<project>/chats:.json 形式と 0.39 以降の .jsonl 形式の両方)、Qwen Code(~/.qwen/projects/<cwd>/chats)、Cursor(IDE の state.vscdb バブルストア。インプレースで読み取り専用で開きます。node:sqlite を使うには Node 22.13+ または 24 が必要です。Node 20 では、代わりに agent-transcripts JSONL を読み取りますが、そこには終了コードが含まれないことがレポートに記載されます)。エージェントがファイルを編集した後に完了を主張したターンをすべて見つけ、最後の編集後にテストコマンドが実際に成功したかを確認します。マシンの外にデータが送られることはなく、出力されるのは件数のみです。
$ npx isitdone history --until 2026-09-07 --exclude <private project>
isitdone history ~/.claude/projects until 2026-09-07
scanned 596 sessions in 6 projects; 1320 turns edited files; 495 of those ended with a completion claim; 1 project dir excluded
claude-code 596 sessions 495 claims 35% verified
VERIFIED 35% (173) a test command passed after the last edit
STALE 35% (174) tests passed, then more edits, no re-run
FAILED 1% (3) the last test run failed, "done" claimed anyway
NEVER RAN 29% (145) no test command in the turn at all
65% of "done" claims (322 of 495) had no passing test run behind them.
21 of the verified claims had a failed test run earlier in the same turn; only the last command passed, and it may have been a narrower one (--verbose marks them).
これは作者が実際に得た結果です。ゲート導入前にディスク上にあった Claude Code のセッションを対象に、2026-09-27 に isitdone 0.8.2 で出力しました。対象は 2026-06-20 から 2026-09-07 までの日付の主張で、--until 2026-09-07 で同じ結果を再現できます。公開されたすべての実行と同様に、非公開のプロジェクト 1 件は --exclude で除外しています。それ以外の変更は、ホームディレクトリを ~ に短縮したことと、末尾のプロジェクトごとの行を削除したことだけです。最初に公開した数値は、2026-09-07 に isitdone 0.2.0 で得たものです。516 件の主張のうち 69% でした(検証済み 31%、古い結果 37%、未実行 31%、失敗した実行の行は 0% と表示されました。0.2.0 の通常の丸め方では、これは最大 2 件を意味します)。後続のバージョンでは、同じトランスクリプトを対象に、主張とターンをより厳密に検出します。0.8.1 以降、history は各割合の横に件数を表示し(0.8.2 以降は見出しにも表示)、件数が 1 件以上ある行を 0% に丸めることはありません。そのため、現在の 0% の行は件数がないことを意味します。ぜひ結果を投稿してください。
ゲートをインストールすると、この測定だけでは過少に数えてしまいます。検証は Stop フック内で行われ、トランスクリプトにはフックによる実行がエージェントの実行したテストとして表示されないためです。そこでフックは、独自の判断をローカルログ(.isitdone/decisions.jsonl)に記録します。停止ごとに 1 行で、結果、最後のメッセージが完了を主張したかどうか、チェック件数が記録されます。メッセージ本文やパスは含まれず、セッション ID はハッシュ化されます。history は、スキャンしたプロジェクトでゲートが行った処理も追加します。説明用の数値を含むレイアウトは次のとおりです。
gate the isitdone Stop hook decided 132 stops in 2 projects (.isitdone/decisions.jsonl)
97 passed; 21 turns blocked: 18 fixed and then passed, 2 gave up after the cap, 1 released
64 stops claimed done; 12 of them were refused (19%)
ゲートを現在の場所に置いた理由の詳しい経緯は、docs/why.md にあります(DEV の記事でも公開しており、コメントを受け付けています)。
インストール
ホストごとにコマンドを1つ実行します。リポジトリ内で実行してください。
| ホスト | コマンド | 書き込み先 |
|---|---|---|
| Claude Code | npx isitdone init | .claude/settings.json(Stop) |
| Codex CLI | npx isitdone init --agent codex | .codex/hooks.json(Stop)。続けて Codex で /hooks を実行し、信頼します |
| Cursor | npx isitdone init --agent cursor | .cursor/hooks.json(stop) |
| Gemini CLI | npx isitdone init --agent gemini | .gemini/settings.json(AfterAgent)。または gemini extensions install https://github.com/raimondasl/isitdone-gemini |
| GitHub Copilot CLI | npx isitdone init --agent copilot | .github/hooks/isitdone.json(agentStop)。Copilot を再起動します |
| Qwen Code | npx isitdone init --agent qwen | .qwen/settings.json(Stop) |
| Goose | npx isitdone init --agent goose | .agents/plugins/isitdone/hooks/hooks.json(Stop) |
| Factory Droid | npx isitdone init --agent droid | .factory/hooks.json(Stop) |
| Devin | npx isitdone init --agent devin | .devin/hooks.v1.json(Stop)。Claude Code のフックが存在する場合、Devin もそれを読み込むためスキップされます |
| Augment (Auggie) | npx isitdone init --agent augment | .augment/settings.json(Stop)。Auggie はスクリプトファイルを実行するため、.augment/hooks/isitdone-hook.sh/.cmd を参照します |
| OpenCode | npx isitdone init --agent opencode | .opencode/plugins/isitdone.js(プラグイン。OpenCode にはブロッキングフックがないため、チェックに失敗すると同じセッション内で、目に見える [isitdone] フォローアップメッセージが返されます) |
| Junie CLI(早期アクセス) | npx isitdone init --agent junie --user | ~/.junie/config.json(Stop) |
| すべて | npx isitdone init --agent all | リポジトリに該当する上記すべて |
init はチェックを検出し、Stop フックと(Claude Code、Codex、Gemini CLI、Qwen Code、Devin、OpenCode では)警告のみの編集後フックを書き込み、.gitignore に .isitdone/ を追加します。また、doctor を実行し、合成した「すべてのテストが合格」という停止イベントをフックに通してブロックされることを確認します。さらに、現在のツリーで検出したチェックを一度実行するため、誤った推測(たとえば CI では mypy src/pkg が実行されるのに mypy . とする場合)や、すでに失敗しているツリーを、停止のたびに NOT DONE として示される前に、インストール時点で把握できます。
$ npx isitdone init
isitdone init ~/work/demo-app
detected npm run typecheck, npm run lint, npm test
profile claim-gated (lite checks on every stop, full checks when the agent claims done)
gitignore added .isitdone/
Claude Code added .claude/settings.json
isitdone doctor ~/work/demo-app
ok git main@2429c82, 0 dirty file(s), tree 8d11d2f
ok config no .isitdone.json (auto-detect only)
ok checks typecheck: npm run typecheck [lite, 60s]; lint: npm run lint [lite, 60s]; test: npm test [full, 120s]
ok baseline all 3 checks pass on the current tree (npm run typecheck 428ms, npm run lint 411ms, npm test 532ms)
ok hook:claude Claude Code (project) .claude/settings.json -> npx -y @aivolution/isitdone hook --host claude
ok probe:claude synthetic "tests pass" stop was blocked in 1.9s (decision block with reason)
OK the agent cannot claim done with failing checks in this repo
Claude Code ユーザーは、プラグインとしてインストールすることもできます。/plugin marketplace add raimondasl/isitdone の後に /plugin install isitdone@isitdone を実行します。Gemini CLI ユーザーは、拡張機能としてインストールできます。gemini extensions install https://github.com/raimondasl/isitdone-gemini(isitdone-gemini)を実行します。これらの手順をエージェントに貼り付けて使える版は、docs/install.md にあります。各エージェントのターン終了フックの仕組み(設定ファイル、ペイロード、ブロック方法、ループフラグ)は、ベンダーに依存しない形で docs/agent-hooks.md に記載されており、同じ表のデータは docs/agent-hooks.json にあります。
プロジェクトではなくユーザーレベルの設定にインストールするには、--user を追加します。npx isitdone uninstall で削除できます。npx skills add raimondasl/isitdone を使ってエージェント自身が実行するように指示できます(SKILL.md はリポジトリのルートにあります)。
コードは @aivolution/isitdone パッケージにあります。npm の isitdone は同じコマンドを使える短いエイリアスで、インストールされたフックは常に正式なパッケージを呼び出します。npm i -D @aivolution/isitdone を実行すると、レジストリへの問い合わせなしでオフラインでもフックをローカルで解決できます。
更新
フックは npx 経由で実行されます。npx は独自のインストールキャッシュを保持し、バージョンを固定していないパッケージを自動では更新しません。フックを最新リリースに更新するには、次を実行します。
npx isitdone update # clears the cached copies of both packages and re-warms them
npx isitdone update --check # only reports whether a newer release exists
doctor はフックが実際に実行するバージョンを報告し、新しいリリースが利用可能な場合はその旨を知らせます。@aivolution/isitdone を開発依存関係としてインストールしたプロジェクトでは、代わりに npm update @aivolution/isitdone で更新します。
ターン途中の警告
Stop フックがゲートであり、編集後フックが注意喚起です。Claude Code、Codex、Gemini CLI、Qwen Code、Devin、OpenCode では、init によって、各 Edit/Write の後に実行されるフックも登録されます(Codex と Devin:apply_patch、Gemini と Qwen:write_file/replace、OpenCode:edit/write/apply_patch)。そのフックは、該当ファイルだけを HEAD と照合してスキャンします(そのため、この編集で触れた行だけでなく、ファイル内の未コミットの変更をすべて確認します)。テストが弱くなっていた場合は、ツールの結果の隣に、事実に基づく短いメモを追加します。
isitdone: after your edit, src/auth.test.ts has weaker tests than HEAD (Tests 4 -> 4 Assertions 6 -> 4 Skipped 0 -> 1):
- line 12: test skipped or marked expected-failure [high]
- line 30: assertion weakened: toEqual -> existence check [medium]
If this is intentional, or the change was already there before your edit, tell the user why; otherwise restore the tests. The Stop hook will run the full checks before you can finish.
ブロックすることはなく、高速に動作します(1 ファイルのみで、テストは実行しません)。Cursor には編集後にエージェントから確認できるチャネルがないため、警告は代わりに Stop フックとともに届きます。init --no-edit-hook でスキップできます。また、設定の "integrity": "off" でも無効にできます。
GitHub Action
ローカルのレシートを信頼しないクリーンなチェックアウトから、すべてのプルリクエストで同じ検証を行います。
on: { pull_request: {} }
permissions:
contents: read
pull-requests: write # for the sticky PR comment
jobs:
isitdone:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 } # exact merge-base for the test-integrity diff
- uses: raimondasl/isitdone@v0
GitHub Marketplace の isitdone verify として掲載されています。リポジトリのチェックを実行し、PR のベースとの差分をスキャンして弱くなったテストを検出します(デフォルトでは厳格に動作し、チェックが通っていても重大度が高またはクリティカルの検出結果があるとジョブは失敗します)。レシートをジョブサマリーに書き込み、PR 上のコメントを 1 件更新し続け、検出結果を SARIF としてアップロードできます(with: { sarif: 'true' }。push イベントでは security-events: write が必要です)。フォークからのプルリクエストではデフォルトのトークンは読み取り専用のため、コメントはスキップされ、レシートはジョブサマリーに記載されます。書き込みアクセス権のある token(PAT または GitHub App トークン)を渡すか、pull_request_target で実行すれば、そちらにもコメントできます。base にはブランチ(デフォルト:PR のベース)、コミット sha、タグ、または完全修飾 ref を指定できます。入力:version、base、strict、comment、sarif、token、args、command。出力:status、report、json(--json-file の結果のパス)。
MCP サーバー
停止フックのないエージェントや IDE(VS Code Copilot agent mode、Cline、Windsurf Cascade、Kiro、Zed、JetBrains AI Assistant、Claude Desktop、Amp、Crush、Roo Code)でプログラムをブロックする場合も、同じ検証を Model Context Protocol サーバーとして利用できます。
npx -y @aivolution/isitdone mcp
これは2つの統合方法のうち弱いほうですが、意図的にそうなっています。 フックはホストによって強制されるため、エージェントはチェックに合格するまで、たとえ望んでいなくてもターンを終了できません。MCP ツールはモデルが呼び出すと判断した場合にのみ実行されるため、呼び出しを省略したモデルは、これまでと同じように「完了しました」と主張できます。エージェントがインストールの表にある場合は、フックを使用してください(両方を実行することもできます。同じレシートを共有します)。フックがない場合は、モデルに呼び出すよう指示したツールのほうが、誰も確認しない文言より役に立ちます。
3つのツールを提供しており、サーバーの指示では、作業が完了したと言う前に isitdone_verify を呼び出し、結果を貼り付け、テストに合格させるためにテストを弱めないようモデルに伝えます。
| ツール | 引数 | 返される内容 |
|---|---|---|
isitdone_verify | cwd?、profile?(lite | full)、claim?、base?、strict? | npx isitdone の実行結果:done の true/false、各チェックのステータス、失敗した出力の末尾、テストの整合性に関する検出結果、レシートの状態です。NOT DONE は通常の結果です(isError は false のままです)。そのため、モデルは結果を読み、作業を続けます。 |
isitdone_receipt | cwd? | 現在のツリーに対する PASS、FAIL、STALE、NONE のいずれかです。何も実行しません。 |
isitdone_detect | cwd? | 実行されるチェックと、それぞれが検出された場所です。何も実行しません。 |
各結果はプレーンテキストのレポートで、読み取り可能なクライアント向けに同じ情報を structuredContent(outputSchema 付き)でも返します。cwd 引数がない場合、サーバーはクライアントの最初のワークスペースルート(roots)を使用し、次にサーバーの起動ディレクトリを使用します。
VS Code の .vscode/mcp.json:
{
"servers": {
"isitdone": { "type": "stdio", "command": "npx", "args": ["-y", "@aivolution/isitdone", "mcp"] }
}
}
Cursor(.cursor/mcp.json)、Claude Desktop(Settings の Developer にある claude_desktop_config.json)、Cline(MCP Servers の Configure にある cline_mcp_settings.json)、Windsurf(~/.codeium/windsurf/mcp_config.json)では、共通の形式を使用します。
{
"mcpServers": {
"isitdone": { "command": "npx", "args": ["-y", "@aivolution/isitdone", "mcp"] }
}
}
Claude Code:
claude mcp add isitdone -- npx -y @aivolution/isitdone mcp
Cursor と Claude Code には実際のフックがあるため、そこで MCP サーバーはゲートではなく利便性のためのものです(エージェントはタスクの途中でレシートを要求できます)。npx isitdone init はそのまま使ってください。Windows で npx を直接起動できないクライアントの場合は、"command": "cmd", "args": ["/c", "npx", "-y", "@aivolution/isitdone", "mcp"] を指定します。Claude Desktop にはワークスペースがないため、モデルは cwd を渡す必要があります。呼び出しを強制する仕組みはないので、エージェントのルールファイル(.github/copilot-instructions.md、.clinerules、.windsurfrules、AGENTS.md)にも、次のように記載してください。"作業が完了したと伝える前に、isitdone_verify ツールを呼び出して結果を貼り付けてください。"
実際の運用で重要な点:リポジトリごとに一度に実行できる検証は1つだけで、2回目以降の同一の呼び出しには実行中の結果が返されます。クライアントが時間のかかるテストスイートを独自のツールタイムアウト後に諦めて notifications/cancelled を送信しても、実行は最後まで続き、レシートが残ります。そのため、モデルが再試行すると再びタイムアウトすることなく、すぐに結果が返されます。進捗通知を要求するクライアントには、チェックごとに進捗通知が送信されます。クライアントが stdin を閉じると実行中のチェックは強制終了され、サーバーも終了します。チェックはパイプ上で実行されるため、テストが出力する内容がプロトコルストリームに届くことはありません。サーバーはワイヤ形式(改行区切りの JSON-RPC 2.0、SDK なし、依存関係は引き続きゼロ)に基づいて実装されており、ハンドシェイクのリビジョン 2024-11-05、2025-03-26、2025-06-18、2025-11-25 に加えて、ステートレスな 2026-07-28 リビジョン(server/discover、リクエストごとの _meta、input_required を介した roots)にも対応しています。server.json では、公式 MCP Registry 向けに io.github.raimondasl/isitdone として記述されています。リリースワークフローによって、そこに公開されます。
判定の仕組み
- 検出します。
package.jsonのスクリプト(test、typecheck、lint、build。npm、pnpm、yarn、bun、deno)、pyproject.toml/pytest.ini/requirements.txt(pytest、ruff、flake8、mypy、pyright。uv/poetry/pipenv のランナー)、go.mod(go vet、go test ./...)、Cargo.toml(cargo check、cargo test)、.NET ソリューション、Gradle/Maven、Makefileのターゲットを読み取ります。.isitdone.jsonですべてを上書きできます。 - 申告内容に応じてゲートします。 デフォルトの
claim-gatedプロファイルでは、毎回の停止時に高速な lite チェック(型チェック、lint)を実行し、full チェック(テスト、ビルド)はエージェントの最終メッセージに完了を示す申告(「テストに合格しました」「完了しました」「実装しました」「検証しました」「レビューの準備ができました」など)が含まれる場合にのみ実行します。質問や進捗報告では、2 分かかるテスト実行は行われません。最終メッセージを渡さないホスト(Cursor、Copilot CLI、Factory Droid、Devin、Junie)では、ツリーごとにキャッシュされる full プロファイルを使用します。 - ツリーごとにキャッシュします。 一時的な git インデックスを使って、作業ツリー(追跡対象の変更と未追跡ファイルに加え、サブモジュールと埋め込みリポジトリの内容も含む)のハッシュを計算します。このとき、
.git/objectsに blob は書き込みません。同じツリーハッシュと同じチェック設定に対する PASS レシートを再利用するため、同じ処理が無駄に二度実行されることはありません。 - 実行して判定します。 チェックは新しいサブプロセスで
CI=trueを設定し、チェックごとのタイムアウトを適用して実行されます。最後の 30 行を取得します。何かが失敗すると、フックはホストのブロック形式に従い、申告内容と失敗時の出力を引用した、長さに上限のあるプレーンテキストの理由を返します。すべてに合格するとレシートが書き込まれ、エージェントは停止できます。 - テストが弱められていないか、diff をスキャンします。 テストファイルの削除、新たな
.skip/.only/xfail、アサーションの削除、マッチャーの格下げ(toStrictEqualからtoEqualへ、toThrow("msg")からtoThrow()へ、assertEqualからassertTrueへ)、許容範囲の拡大、空のcatch/except: pass、設定の無効化(|| true、--passWithNoTests、continue-on-error、testPathIgnorePatterns、-DskipTests、ignoreFailures、cargo test -- --skip、CI のテスト手順の削除)を確認します。対象は JS/TS、Python、Go、Rust(#[ignore]、緩和された#[should_panic]、assert_eq!からis_ok()への変更、内容から検出したインラインの#[cfg(test)]モジュール)、Java/Kotlin(JUnit 4/5、TestNG、AssertJ、Hamcrest、@Disabled、assumeTrue(false)、assertEqualsからassertNotNullへの変更。Kotlin は JUnit と kotlin.test のベストエフォート対応)、C#(xUnit、NUnit、MSTest、Skip =、[Ignore]、Assert.EqualからAssert.NotNullへの変更)です。また、Cargo/nextest、Maven/Gradle、csproj/runsettings/xunit.runner.json、および一般的な CI ファイルを含むビルド設定もスキャンします。検出結果は変更前/変更後の行(Tests 47 -> 44 Assertions 112 -> 104 Skipped 0 -> 1)とともに報告され、レシートに記録されます。"integrity": "strict"(または--strict)を指定すると、チェックに合格していても、重大または高リスクの検出結果がある場合は停止をブロックします。// isitdone: allow <reason>を使うと行単位で抑制できます。抑制は非表示にならず報告され、--ciでは新たな抑制も検出結果として扱われます。 - 無限にループしません。 フックは各ホストの
stop_hook_active/loop_countを尊重し(どちらも送信しないホストでは試行回数を自ら数えます)、セッションごとに試行回数を数えます(デフォルト上限は 3 回)。上限に達すると、警告を表示したうえでエージェントの停止を許可します。不正な stdin、壊れた設定、内部エラーが発生した場合も、常に停止を許可します。isitdoneがエージェントを動作不能にすることはありません。
同じ作業ツリーでの 2 つのセッション
チェックでは作業ツリー全体を確認します。2 つのエージェントセッションが同じディレクトリで作業している場合、そのツリーにはもう一方のセッションが作業中のファイルも含まれます。それらを考慮しないゲートでは、先に停止したセッションに責任を負わせ、自分のものではない作業を「修正」するよう求めてしまいます。isitdone は、1 つのルールに基づいて両者を分けます。セッションが 1 つだけなら、これまでどおりゲートします。 それより緩やかな扱いには、別のセッションがこの場所で作業中であることを示す証拠が必要です。
- 編集後フックは、どのセッションがどのファイルを編集したかを記録します(リポジトリのトップにある
.isitdone/sessions/:パスとタイムスタンプ。ローカルのみで、コミットされることはありません)。 - 進行中の作業の証拠となるのは、次のすべてを満たす場合です。このセッションが最初に開始した後、かつ直近1時間以内に別のセッションがフックを実行したこと、その別のセッションが最後に合格した停止後に記録した編集であること(ここでいう停止とは、lint だけ、または何も実行しなかった一時停止ではなく、すべてのチェックを実行してツリー全体を検証した停止です)、そして編集されたファイルがまだ未コミットであることです。このセッションが始まる前に終了した会話(
/clear、再起動、前日のセッション)は「別のセッション」ではありません。また、チェックに合格してターンを終えたセッション(または、チェックに合格して終了した一度限りのヘルパー)には進行中の作業はありません。そのセッションが離れた時点ではファイルに問題がなかったため、その後に発生した失敗は後から加えられた変更が原因であり、その変更を行ったセッションがすべての責任を負います。 - その証拠がある場合、すべてのブロック理由は、別のセッションのファイルから説明を始め、編集、元に戻す、または「修正」してはならないこと、またそれらを破棄する git コマンド(
checkout、restore、reset、stash、clean)を実行してはならないことを明確に指示します。 - 出力にこのセッションが編集したファイルが記載された失敗は、通常どおり
maxAttemptsまでブロックします。 - 出力にこのセッションのファイルが一つも記載されていない失敗は、1回だけブロックします。エージェントには、変更によってその失敗が起きた可能性(変更した型やフィクスチャが、開いたことのないファイルを壊すなど)を判断し、該当する場合は自身の変更を修正するよう求めます。次の停止時にチェックを再実行し、出力に引き続き自身のファイルが一つも記載されていなければ、停止してかまいません。ユーザーにはその旨が伝えられ、ツリーが検証されていないため、レシートには FAIL と記録されます。そのターンでブロックされ、かつその停止時にチェックが実行されない限り、何もリリースされません。すでに指示を含むブロックがあれば、それをこの指示を伝えたものとして数えます。
- 編集記録がまったくないセッション(編集後フックのないホスト、またはシェル経由でのみ編集した場合)は、チェック失敗やテスト整合性に関する厳格な指摘に対する緩和措置を受けられません。何に触れたかを示す証拠がない場合は、単独で作業しているものとしてゲートされます。別のセッションが編集しているファイルと、そのセッションの変更を破棄してはならないことが伝えられます。
- それ以外の場合、別のセッションのファイルに関するテスト整合性の指摘はそのセッションに委ねられ、停止メッセージには指摘の件数が記載されます。
- 同時進行のセッションがある場合、簡易チェックが失敗していてもフルチェックを実行します。これにより、別のセッションのファイルにある lint エラーで、このセッションのテスト失敗が隠れることはありません。
- チェックの実行は、Stop フック、CLI、MCP サーバーのいずれから実行する場合も、プロジェクトごとに直列化されます。実行中のチェックがある場合、その上からテストスイートを実行するのではなく、最大3分(フックからの実行では、チェック自体がフックの制限時間の大半を使う場合はそれより短い時間)待機します。キャッシュされた PASS の確認では待機しません。チェック実行中に加えられた編集について、レシートには正確に記録されます。待機後に、別の実行の PASS を再利用するのは、その実行がまさにこのツリーで開始された場合に限られます。セッションが編集を記録した実行、または既存ファイルが変更・削除された実行では、その実行開始時点のツリーにレシートがひも付けられます(警告付き)。また、セッションは、自身が最後に記録した編集より前に開始された実行から、キャッシュされた PASS を取得することはありません。
「ファイルを記載している」とは、リポジトリ相対パス、リポジトリ内の絶対パス、またはサブディレクトリやワークスペースから実行したチェックが出力する短いパスを指します。ただし、その末尾に一致するファイルがリポジトリ内に一つだけある場合に限ります。単独の Name.java:17 は、その名前がリポジトリ内で一意であり、かつ識別しやすい場合に限り該当します。先にカラーコードを除去し、Windows と macOS では大文字・小文字を区別せずに比較します。
把握しておきたい制限事項。記録は編集後フックから取得するため、Claude Code、Codex、Gemini CLI、Qwen Code、Devin、OpenCode では編集の所有者が特定されます(OpenCode では、サブエージェントの編集は親に帰属します。0.6.1 より前に生成されたプラグインを更新するには、init --agent opencode をもう一度実行してください)。シェルコマンドで変更されたファイルには所有者がいません。ターンの途中で強制終了されたセッションは終了を通知しません。そのため、終了していないセッションのファイルは最大 1 時間、ゲートを緩和し続けます。残りのセッションのファイルを特定しない失敗については、3 回ではなく 1 回のブロックになります。逆方向の空白もあります。稼働中のセッションは、そのフックが新しいセッションの最初のフックより後に実行されるまで、新しいセッションからは見えません。帰属は、失敗の原因ではなく、失敗が報告された場所に基づきます。このセッションの変更によって、別のセッションが編集中のファイルが壊れた場合、唯一の防護策は 1 回のブロックとその質問であり、もう一方のセッションはそのファイルの失敗によって停止されます。チェックの実行中に、編集後フックのないセッションまたはシェル経由で追加されたファイルは、チェック自体の出力と区別できないため、レシートに気付かれないまま含まれることがあります。失敗したテストでは通常、編集されたソースファイルではなくテストファイルが示されるため、セッションがテストも編集していない限り、そのような失敗は「どのファイルも特定しない」とみなされます。チェック出力内の ../ 相対パスは解決されません。解放メッセージは Claude Code、Codex、Gemini CLI、Qwen Code に表示されます。Devin と OpenCode にはメッセージを表示する手段がないため、1 回のブロック後に通知なしで解放されます。実行ロックはベストエフォートです。待機時間が尽きた場合や競合が激しい場合は、以前と同様に実行が重複する可能性があります。"otherSessions": "ignore" を指定すると、指示、1 回のブロック、実行ロックが無効になります(編集の記録は引き続き行われます。読み取り時にレシートと結び付けられるだけです)。
この方法でディレクトリを共有することは可能ですが、理想的ではありません。ツリーの半分が別のセッションによって壊れている間は、リポジトリ全体のチェックに合格できません。並行して長時間作業する場合は、各セッションに専用の git worktree を用意してください。それぞれが専用の .isitdone/ とレシートを持ちます。
ゲートの一時停止
ベンチマーク、隔離された実行、別の場所での長時間ビルドなど、マシンを静かなままにしておく必要があることがあります。一時停止すると、しばらく Stop フックが無効になり、時間が来ると自動的に再び有効になります。ご自身のターミナルで実行してください。
npx isitdone pause # this repository (all its worktrees), for an hour
npx isitdone pause --for 3h # 30m, 3h, 2d, 1w; at most 30 days
npx isitdone pause --global --until 2026-11-02T11:00Z # every repository of this user
npx isitdone resume # end this repository's pause early (--global: the user-wide one)
- フックは停止のたびに一時停止状態を読み取り、チェックの実行中も数秒おきに確認します。すでに実行中のセッションでは次の停止時に反映され、すでに進行中のチェックは停止します(レシートは書き込まれません)。フックで isitdone 0.9.0 以降が実行されていれば、再起動は不要です。
pauseは古いコピーが見つかり、フックがまだ実行される可能性がある場合に知らせます(npx のキャッシュ内にある場合:npx isitdone update。バージョンが固定されている場合やインストール済みの場合:initを再実行するか、npm update @aivolution/isitdoneを実行し、該当するセッションを再起動します)。 - 一時停止中は、停止時に何も実行されず、プロジェクトにも新たに何も書き込まれません。最終メッセージで完了を宣言する(またはその可能性がある、一部のホストではメッセージが渡されないため)一時停止中の停止と、ブロックを途中で打ち切る一時停止は、いずれもゲートが無効になっていることと、その終了時刻をユーザーに知らせます。進捗を示す停止では通知されません。このメッセージは Claude Code、Codex、Gemini CLI、Qwen Code、Factory Droid、Augment に送られ(Copilot CLI では許可理由として送られます)、Cursor、Goose、Devin、OpenCode、Junie には通知用の手段がないため、そこで一時停止を確認できるのは
doctorとhistoryのみです。一時停止中の停止は記録され、historyでは、ブロック後に一時停止によって通過したターンも含め、個別に集計されます。 - 一時停止されるのは自動ゲートのみです。手動で実行する
npx isitdone、MCP ツール、GitHub Action では引き続きチェックが実行され、npx isitdoneとdoctorはフックが一時停止中であることを知らせます。 - 一時停止状態はプロジェクトごとではなく
~/.isitdone/pauses/に保存されるため、「このユーザーのすべてのリポジトリ」とは、このホームディレクトリで実行されるすべてのエージェントを指します。WSL、コンテナ、別のアカウントにはそれぞれ別の状態があります。期限切れまたは読み取り不能な一時停止ファイルは無視されます。判断に迷った場合は、ゲートが実行されます。30 日を超えて一時停止するには、npx isitdone uninstallを実行してください。 - これはエージェントではなく、あなたが操作するスイッチです。
pauseは、エージェントのものと思われるシェル内では実行を拒否します。Claude Code、Codex、Cursor、Gemini CLI、Qwen Code は、コマンドを実行するシェルに環境変数を設定します。また、これらのアプリ内から開いたターミナル(および Claude Code の!プレフィックス)にもその変数が引き継がれます。これは簡単に実行できなくするためのもので、ロックではありません。エージェントが変数をクリアしたり、自身のフック設定を編集するのと同様に、ワークスペースの外で一時停止ファイルを自ら書き込んだりする可能性があります。それが気づかれずに済まないようにするのが、一時停止中にエージェントが「完了」を宣言するたびに表示されるメッセージ、doctor、および判断ログです。
CLI
npx isitdone run all checks, write the receipt, exit 0 (DONE) or 1 (NOT DONE)
npx isitdone --profile lite typecheck + lint only
npx isitdone --json {"ok", "done", "checks": [...]} for scripts and orchestrators (--json-file <path> writes it)
npx isitdone --no-cache re-run even if a PASS receipt exists for this tree
npx isitdone --all keep running tests even if typecheck failed
npx isitdone --strict block when the change weakened tests (high/critical findings)
npx isitdone --ci strict, plus new "isitdone: allow" suppressions count as findings
npx isitdone --base main scan the diff against a branch instead of HEAD (pull requests)
npx isitdone --report out.md write a markdown report; --sarif out.sarif writes SARIF 2.1.0 for code scanning
npx isitdone history [--since 30d] [--exclude <substr>] [--verbose] [--min <pct>]
share of past "done" claims backed by a passing test run
(Claude Code, Codex, Gemini CLI, Qwen Code and Cursor transcripts, local only)
npx isitdone receipt [--md|--json] state of the current tree: PASS | FAIL | STALE | NONE (exit 0 only on full PASS)
npx isitdone detect [--json] which checks would run and where they came from
npx isitdone init [--agent ...] install the Stop hook (claude | codex | cursor | gemini | copilot | qwen | goose | droid |
devin | augment | opencode | junie | all | auto)
npx isitdone doctor prove the installed hook blocks; run the checks once on the current tree (--no-baseline skips)
npx isitdone uninstall remove the hook(s)
npx isitdone pause [--for 3h | --until <time>] [--global]
let agents' stops through unchecked for a while (see "Pausing the gate"); resume [--global] ends it
npx isitdone mcp MCP server on stdio for agents without a stop hook (started by the IDE, see "MCP server")
オーケストレーターやエージェントのループでは、npx isitdone --json は完了判定として機能します。done が true になるのは、現在のツリーで完全なチェックがすべて通過した場合のみです。終了コード:0 は完了、1 は未完了、3 は使用方法または内部エラーです。
設定
任意です。リポジトリのルートにある .isitdone.json、または package.json の "isitdone" キーで設定します。
{
"checks": {
"test": "npm run test:unit", // replace a detected command
"lint": false, // disable one
"e2e": { "cmd": "playwright test", "kind": "full", "timeout": 600 }, // add one
"smoke": { "cmd": "./smoke.sh", "kind": "lite" }
},
"profile": "claim-gated", // claim-gated (default) | lite | full
"timeout": 120, // seconds per full check
"liteTimeout": 60, // seconds per lite check
"maxAttempts": 3, // consecutive blocks before the agent may stop anyway
"build": false, // include the build script even when tests exist
"claimPatterns": ["ship it"], // extra completion-claim regexes
"integrity": "warn", // warn (default) | strict | off
"otherSessions": "respect", // respect (default) | ignore: see "Two sessions in one working tree"
"history": { "exclude": ["client-x"] } // project paths to skip in `isitdone history`
}
kind: "lite" のチェックは停止のたびに実行され、kind: "full" のチェックはエージェントが完了を宣言したときに実行されます。test と build はデフォルトで full、typecheck、lint、check、vet はデフォルトで lite です。
対象外のこと
- 嘘発見器ではありません。 エージェントの文章を採点するのではなく、コマンドを実行して終了コードを読み取ります。申告内容によって決まるのは、実行する量だけです。
- 敵対的なセキュリティ対策ではありません。 レシートには HMAC 署名が付いているため、手編集したファイルは NONE として読み取られます。また、ツリーハッシュにより古いレシートが見えるようになりますが、設定を編集する権限を持つエージェントはフックを削除できます。
isitdoneが防ぐのは、正直な間違いです。「テストが通った」という虚構のほとんどは、そこから生まれます。 - AST ではありません。 テストの整合性スキャンは、公開されている少数の検出器リストを使い、差分を行単位および正規表現で調べます。エージェントが何も修正せずに赤を緑にする一般的な方法を検出しますが、巧妙な手口(たとえば、リグレッションに合わせてテストの期待値を曲げる手口)は見逃します。また、正当なリファクタリングを誤検出することもあります。そのため、デフォルトでは警告にとどめ、すべての検出結果に根拠を表示します。精度と再現率は
bench/のラベル付きコーパスで測定されます(npm run benchを実行すると、使用したバージョンとコミットが表示されます)。2026-09-24 時点の isitdone 0.7.0 では、手動でラベル付けされた 192 件のケースを対象に、正当なケースは誤検出せず(89 件中 0 件)、改ざんケース 103 件中 102 件を検出しました。また、スコア対象の各検出器について、それ単独を指すケースが少なくとも 3 件ずつ含まれていました。実行結果には、ケースごとの再現率に加え、検出器ごとの再現率も表示されます。報告された間違いがあるたびに改善されます。 - LLM ではありません。 ここからモデルを呼び出したり、外部に接続したり、キーを必要としたりすることはありません。
関連ツール
| ツール | 機能 | 関係 |
|---|---|---|
| oh-my-agent | 型チェック/テスト/リンターを実行する停止フックゲートを含むマルチエージェントフレームワーク | isitdone は、レシートと申告内容に基づくゲート制御を備えた、あらゆるホストで使える単独のプリミティブです |
| taskmaster | 完了トークンが現れるまで停止をブロックします | トークンベースで、チェックは実行しません |
| gutcheck | Claude Code の停止フックを使う、差分範囲に限定されたミューテーションプローブ | 補完的な第 2 のゲートです。isitdone はテストスイートが通ることを検証し、gutcheck はテストスイートに意味があることを検証します |
| checkwash、testseal | 弱体化したテストを検出する差分スキャナー | 差分のみを対象とします。isitdone はチェックを実行します(差分スキャンはロードマップに掲載されています) |
| ProofRun | 手書きの設定による、ツリーに紐付いた PASS/FAIL/STALE レシート | レシートの考え方は似ています。isitdone は自動検出し、エージェントにフックします |
ロードマップ
- v0.5 実行時間の長いテストスイート向けに、変更されたファイルに関連するテストだけを実行する
--relatedモード。Cline(attempt_completionの PreToolUse ゲート)と Amp(agent.endプラグイン)のアダプター。session.stoppingの提供開始後に OpenCode 用のネイティブフックを追加し、フックでブロックできるようになった時点で Windsurf/Cascade にも対応。OpenCode のデータベース用のhistory。Kotest/Spek DSL。リグレッションに合わせて期待値が曲げられたことを検出する検出器。 - フックアダプターの予定なし:Roo Code と Kilo Code(フックなし)、Kiro(Stop トリガーでブロックできない)、Crush(PreToolUse のみ)。これらには代わりに MCP サーバー を提供します。Aider にフックはありませんが、
aider --auto-test --test-cmd "npx isitdone"を使うと、編集のたびに同じ判定を返します。
開発
npm ci
npm test # vitest, runs in a throwaway HOME so it never touches your real agent settings
npm run typecheck
npm run build # esbuild -> dist/isitdone.js (CLI) and dist/index.js (library), no dependencies
npm run bench # precision/recall of the test-integrity scanner over bench/cases/
node dist/isitdone.js
このリポジトリ自体でも dogfooding を行っています。CI はプッシュのたびに node dist/isitdone.js --json を実行し、プルリクエストでは GitHub Action を実行します。ライブラリとしてもインポートできます:import { verify, scanIntegrity } from '@aivolution/isitdone'。
ライセンス
MIT
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。