1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

osFoundryがClaude Codeの許可待ちをどう判定しているかを手元のスクリプトで確かめる

1
Posted at

この記事でやること

  • Claude Code用の判定ルール claude.toml を手元に落として中身を確認する
  • 画面のテキストを貼るとstateを返す、20行ほどのPythonスクリプトを書く
  • セッションボードのカードが思った列に入らないときに、どこを順に見ればいいかを整理する

前提・環境

  • osFoundryのデスクトップアプリで、ターミナル画面(/terminal)からClaude Codeを起動している
  • 状態はセッション画面(/sessions)のボードで見ている
  • スクリプトはPython 3.11以上(標準ライブラリの tomllib を使うため)。macOS同梱の python3 が3.9なら、uv run --python 3.12 などで動かす
  • 下に出てくるプロンプトやペインの中身は表示例

判定ルールを落としてくる

osFoundryがClaude Codeの画面を読むときのルールは、アプリに同梱されているものと同じファイルが公開されています。まずはこれを手元に置きます。

curl -s -o claude.toml https://osfoundry.io/agent-detection/claude.toml
grep -n '^\[\[rules\]\]' -A 2 claude.toml

[[rules]] が3つあり、上から blocked、working、idle の順に並んでいるはずです。見るべき点は2つです。

  • 上から順に評価して、最初にマッチしたルールのstateが採用される
  • どのルールにもマッチしなければ idle になる。blocked にはならない

blocked のパターンは、Do you want to (proceed|continue|make this edit|create)、❯\s*1\.\s*Yes、フッターの Esc to cancel · Tab to amend などです。working は esc to interrupt の1つだけです。

画面テキストを判定する小さなスクリプト

ルールの順番どおりに当てて、最初に当たったものを返すだけのスクリプトです。

check_screen.py
import re
import sys
import tomllib

with open(sys.argv[1], "rb") as f:
    manifest = tomllib.load(f)

text = sys.stdin.read()
for rule in manifest["rules"]:
    for pat in rule.get("any", []):
        if re.search(pat, text):
            print(f"{rule['state']:8} <- {pat}")
            sys.exit(0)
print("idle     <- (どのルールにもマッチしない)")

標準入力に画面のテキストを流し込みます。4パターン試すと次のようになりました。

$ printf 'Do you want to make this edit to charge.ts?\n❯ 1. Yes\n  2. Yes, allow all edits during this session\n · esc to interrupt\n' \
    | uv run -q --python 3.12 python check_screen.py claude.toml
blocked  <- Do you want to (proceed|continue|make this edit|create)

$ printf '✻ Working… (esc to interrupt)\n' | uv run -q --python 3.12 python check_screen.py claude.toml
working  <- esc to interrupt

$ printf '● Would you like me to add a regression test?\n> \n  ? for shortcuts\n' \
    | uv run -q --python 3.12 python check_screen.py claude.toml
idle     <- \? for shortcuts

$ printf 'hello\n' | uv run -q --python 3.12 python check_screen.py claude.toml
idle     <- (どのルールにもマッチしない)

1つ目では、プロンプトと esc to interrupt が同じ画面に両方あります。それでも blocked が返るのは、blocked のルールが上にあるからです。順番を入れ替えた claude.toml を作って同じ入力を流すと、working が返ります。ルールの並び順にも意味がある、ということが手元で確かめられます。

3つ目は、Claudeの返答が疑問文で終わっているケースです。「?」で終わる行があっても、許可プロンプトの文言でなければ blocked にはなりません。ルールファイルのコメントにも、Codexのプロンプトの文言である Would you like to を、Claudeの普段の返答にも出てくるという理由で外した経緯が残っています。

なお、デスクトップアプリはPTYのバイト列をそのままgrepしているわけではありません。エミュレートした画面のテキストを読んでいます。ここでは目に見えている画面をそのまま貼っているので、アプリが見ているものとほぼ同じ入力になります。

許可プロンプトの形ごとに流してみる

claude.toml のコメントには、実際にキャプチャしたClaude Codeのプロンプトが4種類書かれています。どれも最後は答えて閉じる、という形です。

種類 画面に出るもの フッター
コマンドの実行 Do you want to proceed? と ❯ 1. Yes Esc to cancel · Tab to amend
新しいファイルの作成 Do you want to create osf-probe.txt? コマンドと同じ
選択式の質問 ❯ 1. osf-probe.txt のような選択肢 Enter to select · ↑/↓ to navigate
プランモードの終了 Exit plan mode? と ❯ 1. Yes, and switch to … なし

それぞれの画面をテキストファイルにして、まとめて流します。中身は表示例です。

mkdir -p screens
printf 'Do you want to proceed?\n❯ 1. Yes\n  2. No\nEsc to cancel · Tab to amend\n' > screens/command.txt
printf 'Do you want to create osf-probe.txt?\n❯ 1. Yes\n  2. No\nEsc to cancel · Tab to amend\n' > screens/new-file.txt
printf 'Which file should I use?\n❯ 1. osf-probe.txt\n  2. README.md\nEnter to select · ↑/↓ to navigate\n' > screens/question.txt
printf 'Exit plan mode?\n❯ 1. Yes, and switch to auto mode\n  2. No, keep planning\n' > screens/plan.txt
printf '> \n  ⏸ manual mode on · esc to interrupt\n' > screens/working.txt

for f in screens/*.txt; do
  printf '%-16s ' "$(basename "$f")"
  uv run -q --python 3.12 python check_screen.py claude.toml < "$f"
done
command.txt      blocked  <- Do you want to (proceed|continue|make this edit|create)
new-file.txt     blocked  <- Do you want to (proceed|continue|make this edit|create)
plan.txt         blocked  <- ❯\s*1\.\s*Yes
question.txt     blocked  <- Enter to select · ↑/↓ to navigate
working.txt      working  <- esc to interrupt

プランモードの画面は Exit plan mode\? ではなく ❯\s*1\.\s*Yes で先に当たっています。同じルールの中でもパターンは上から試されるので、どちらで当たっても結果は blocked です。選択式の質問は「Do you want to」を含まないので、フッターの文言で拾われています。フッターまでパターンに入っているのは、こういう形のプロンプトを取りこぼさないためです。

最後の working.txt は、ターンが走っている間のフッターです。手動の許可モードでもフッターに esc to interrupt が出るので、working になります。

カードが「対応が必要」に来ないときに見るところ

上から順に確認すると早いです。

1. 絞り込みの状態

セッションボード右上の「絞り込み」で「対応が必要なもののみ」にチェックが入っているか確認します。入っていれば、残るのはターミナルの状態が blocked のものと、最新のメッセージが返事待ちの質問で終わっているチャットだけです。カードは手で列に入れられないので、どの列にいるかがそのままランタイムの状態を表しています。

2. ペインに今プロンプトが出ているか

判定に使うのは今見えている画面だけで、上にスクロールした200行の履歴は使いません。プロンプトに答え終わって画面の外へ流れたあとは、working か idle に戻るのが正しい動きです。いまのペインの表示をコピーして check_screen.py に流し、blocked が返るかを見ます。

3. 巡回の間隔ぶん待ったか

判定は書き込みのたびではなく、全ペインを700 msごとに1回巡回して行われます。プロンプトが描かれてから列が変わるまで、最大でその1周期ぶんかかります。

4. 手元でルールを上書きしていないか

ルールを自分で上書きしたいときは ~/.osfoundry/agent-detection/claude.toml に置きます。逆に、思った判定にならないときは、ここに古いファイルが残っていないかを見ます。

ls -l ~/.osfoundry/agent-detection/ 2>/dev/null
diff <(curl -s https://osfoundry.io/agent-detection/claude.toml) ~/.osfoundry/agent-detection/claude.toml

上書きできるのはマッチするパターンだけで、起動するプログラムには手を出せません。

自分でパターンを足すときは、置く前に手元で試しておくと安心です。

cp claude.toml my-claude.toml
# my-claude.toml の [[rules]] を編集する
for f in screens/*.txt; do
  printf '%-16s ' "$(basename "$f")"
  uv run -q --python 3.12 python check_screen.py my-claude.toml < "$f"
done
mkdir -p ~/.osfoundry/agent-detection
cp my-claude.toml ~/.osfoundry/agent-detection/claude.toml

見るのは2点です。blocked の4ファイルが blocked のままか。そして、Claudeの普段の返答(疑問文で終わるものなど)を流したときに blocked が出ないか。後者が崩れると、普通の出力でカードが「対応が必要」に飛ぶようになります。ルールファイルのコメントが、それを「このファイルが起こしてはいけない唯一の失敗」と書いているくらいです。

5. どのルールにも当たっていないだけではないか

読めない画面は「待機中」に入ります。「対応が必要」には入りません。誤報が続くとバッジが信用されなくなるので、迷ったら控えめに出す、という設計です。スクリプトで (どのルールにもマッチしない) が出るなら、カードが待機中にいるのは想定どおりです。

よくある疑問

ラッパースクリプト経由で起動したClaude Codeも判定されますか

されます。osFoundryは端末のフォアグラウンドプロセスグループから、端末を握っているのがClaude Codeかどうかを見分けます。ラッパー経由で名前から分からないときは、画面の内容(claude.toml の signature)から判別します。設定は要りません。

osFoundryが代わりにプロンプトに答えることはありますか

ありません。ほかのエージェントがペインに文字を打つ runtime_send は、判断待ちのペインには拒否されます。答える操作は別のツール runtime_answer で、毎回本人の確認が入ります。打ち込む直前に画面を読み直して判定するので、一瞬前に出たプロンプトに普通の入力が吸い込まれることもありません。

osFoundryから起動したClaude Codeは、画面以外の情報も使いますか

使います。osFoundryが自分で起動する場合は --settings でフックを足し、UserPromptSubmit や PreToolUse で working、Stop で done を報告させます。Notification は osf-pane notify がペイロードを見て、permission なら blocked、それ以外なら idle に振り分けます。ただし、画面から読んだ blocked は常に優先されます。

ペインがたくさんあるとき、次に答えるべきものへすぐ移れますか

⌘.(Mod+.)で、次の対応が必要なエージェントへ移動できます。ステータスバーにも案内が出ています。

作業が終わったセッションはどう片付けますか

待機中のカードを完了の列へドラッグします。確認のあと、要約をナレッジファイルに書いてからアーカイブします。削除されるものはありません。実行中のカードは先に一時停止が必要です。

まとめ

  • 判定は claude.toml を上から当てて最初にマッチしたものを使い、どれにも当たらなければ待機中になる
  • check_screen.py に今の画面を流せば、カードがどの列に入るべきかを手元で再現できる
  • 見る順番は、絞り込み、今の画面、巡回の間隔、~/.osfoundry/agent-detection/ の上書きの4つ

ボードの列や絞り込みの説明はエージェントのセッションボードのページにあります。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?