claude agents と /bg —— Agent View 実装の分析
ソースコード:
2.1.139-unbundled/extracted/src/entrypoints/cli.js(ビルド版208bf4b4、2026-05-11)。 公式ブログ参考:Agent view in Claude Code。
"Agent view" は、1つの機能、3つの入口、ディスク上の1つの job モデルです:
| 入口 | 動作 |
|---|---|
claude agents(CLI サブコマンド) | daemon が有効な場合は fast path に進み、Commander が argv を解析する前にインターセプトして FleetView TUI をマウントします。daemon が無効な場合は、Commander に登録された agentsHandler にフォールバックしますが、それは 設定済みサブエージェント(subagent)定義 のプレーンテキスト一覧にすぎません。 |
claude --bg [task] / --background | 新規セッションは on-demand daemon によってそのままバックグラウンドで起動され、親プロセスは短いプロンプトを表示して終了します。 |
/bg(スラッシュコマンド、/background の別名) | 現在の REPL セッションをバックグラウンドに fork し、フォアグラウンドプロセスは終了します。インタラクティブ REPL では、空入力で左方向キーを押すことでも同じ fork パスがトリガーされ、さらにプロセス内で FleetView を再マウントします。 |
3つのパスはいずれも最終的に ~/.claude/bg-jobs/<short>/state.json ファイル + Unix ドメインソケット daemon(つまり "on-demand daemon")へ収束します。FleetView は本質的には、そのディレクトリをポーリングして読み取るリーダーであり、daemon に接続する RPC クライアントでもあります。
以下で使う命名規則:難読化されたシンボルを意味のある名前に改名し、初出のときに /* <元の名前> */ を付けます。改名していないもの(oqH、pT、Q7、B9、Q、yH、xH など)は、ホストプロセスが共有するヘルパー関数(状態ファイル IO、テレメトリ、優雅な終了)です。ほかのプロジェクトに移植する際は、1つずつ置き換える必要があります。
1. データモデル
// state.json —— バックグラウンド job ごとに1つ、~/.claude/bg-jobs/<short>/ に保存される
interface JobState {
proto: number; // I1、schema バージョン番号
short: string; // 8桁の16進プレフィックス、CLI / FleetView でのアドレス指定に使う
sessionId: string; // 完全な uuid、transcript ファイル名に対応
createdAt: string; // ISO
updatedAt: string; // ISO、書き込むたびに bump
firstTerminalAt?: string; // 初めて終止状態に入った時刻
// 身元 / 表示
template: { name: string; description: string; initialPrompt?: string };
agent?: string; // --agent を使った場合、ここは subagent 型
intent: string; // 最初のユーザーメッセージ、≤200 文字
name?: string; // 明示的な -n/--name。なければ自動派生
nameSource?: "user" | "auto";
detail?: string; // 直近の assistant 出力の抜粋、≤120 文字
color?: string;
// 活動状態
state: "running" | "stopped" | "errored" | "done" | ...;
tempo?: "active" | "idle" | "blocked";
needs?: string; // agent が待っているもの
block?: { questions?: Question[] }; // tempo === "blocked" のとき
suggestedReply?: string; // PeekPane で Tab を押すと補完できる事前入力返信
output?: Record<string, string>; // インライン表示するいくつかの headline 行
inFlight?: unknown;
resumeSessionId?: string;
// ソート / ピン留め
sortOrder?: number;
stateSortOrder?: number;
pinned?: boolean;
// 位置
cwd: string;
originCwd?: string;
worktreePath?: string;
worktreeBranch?: string;
worktreeHookBased?: boolean;
// リンク
children?: Array<id: string; kind: "frame">;
source?: "shell" | "slash" | "fleet" | "spare" | "respawn" | "left_arrow";
// その他の帳簿付け
respawnFlags?: string[];
env?: Record<string, string>;
}
interface Question { text: string; options: string[] }
// FleetView のメモリ内で1つの JobState から構築される行構造
interface JobRow id: string; // === short
state: JobState;
activity: "flowing"
// FleetView はこの action を通じて mountFleetView に報告する
type FleetAction =
| { type: "done" }
| type: "open"; job: JobRow; query?: string; collapsed: string[];
groupMode: "state";
agentsHandler のフォールバック処理器が見る「agents」は別物です——subagent 定義 の一覧(uk() が返す activeAgents)で、出所(user / project / local / plugin / built-in)ごとにグループ化されています。これは FleetView 内の「session 行」とは関係ありません。
2. 定数と文言
// 全体スイッチ
const ENV_DISABLE = "CLAUDE_CODE_DISABLE_AGENT_VIEW";
const SETTING_DISABLE = "disableAgentView"; // 管理対象設定
const SETTING_DEFAULT_TO_FLEET = "defaultToAgentsView";// ユーザー設定(462620 行)
// fork 内で `claude agents` を起動するときに job を事前選択
const ENV_PRESELECT = "CLAUDE_AGENTS_SELECT";
// フォーカスを失った fleet view を自動再起動(ハートビート)
const ENV_AUTORELAUNCH = "CLAUDE_AGENTS_AUTO_RELAUNCHED_AT";
const AUTO_RELAUNCH_UNFOCUSED_MS = 3_600_000; // 1 時間
const AUTO_RELAUNCH_MIN_INTERVAL_MS = 21_600_000; // 6 時間
// daemon プロトコルバージョン
declare const I1: number;
// `claude --bg < piped` 時の stdin 上限
const zB8 = 16 * 1024; // バイト
// bg dispatch のたびに表示されるヒントブロック
function formatPostBackgroundHints(short: string, idle?: string) {
return [
`backgrounded · ${cyan(short)}${idle ? dim(" " + idle) : ""}`,
dim(" claude agents".padEnd(28) + "list sessions"),
dim(` claude attach ${short}`.padEnd(28) + "open in this terminal"),
dim(` claude logs ${short}`.padEnd(28) + "show recent output"),
dim(` claude stop ${short}`.padEnd(28) + "stop this session"),
].join("\n");
}
バックグラウンド化に関する重要な文言:
"Backgrounding…"—— バックグラウンドで立ち上げている最中の spinner。"<N> tasks running — the forked session won't carry live processes."—— 確認ダイアログのサブタイトル。"Background anyway (tasks will be abandoned)"/"Stay"—— 確認 / キャンセルボタン。"Cannot background — session persistence is disabled, so the forked job would have nothing to resume."
3. Host-Framework 契約
次の表は、agent view モジュールが依存している、cli.js 内で他モジュールから提供される補助関数です。別のプロジェクトへ移植する場合は、各項目をそちら側の対応する実装に置き換える必要があります。
| シンボル | シグネチャ | 行番号 | 用途 |
|---|---|---|---|
oqH | (seed) => JobState | 542886 で使用 | デフォルト値から新しい JobState を構築する。 |
pT | (jobDir, partial) => Promise<void> | 542884 で使用 | state.json を atomic に merge 書き込みする。 |
Q7 | (jobDir) => Promise<JobState|null> | 542964 | state.json を読む。 |
W4 | (short) => string | 542879 | ~/.claude/bg-jobs/<short>/。 |
rW | () => string | 543233 | jobs のルートディレクトリ。prefix 検索ではここを readdir で走査する。 |
k_ | () => string | 542814 | 現在プロセスの session id(fork 内で設定される)。 |
L7 | () => boolean | 543959 | CLAUDE_JOB_DIR が存在するか → つまり「自分がすでにバックグラウンド fork かどうか」。 |
getCurrentJobDir / Xv3 | () => string | undefined | 544011 | process.env.CLAUDE_JOB_DIR を返す。 |
V$ | (req, opts?) => Promise<DaemonResp> | 543031 | daemon との Unix socket RPC(op: "list" | "dispatch")。 |
mW_ | () => Promise<{ok,reason?}> | 543277 | daemon のヘルスチェック(claude attach 用)。 |
MQ | (short) => Promise<{outcome,msg?}> | 543286 | 「自分をこの job に接続する」RPC。 |
PGK | (short, {alreadyInAlt}) => Promise<AttachResult> | 606723 | attach 中にローカル PTY ↔ daemon 内 job の双方向ブリッジを担当する。 |
fW6 | (jobId, {force?,knownState?,knownAlive?}) => Promise<RespawnResult> | 606700 | worker がすでに死んでいれば起動する(Re·spawn)。 |
B9 | (exitCode, reason, opts?) => Promise<never> | 543885 | フォアグラウンドプロセスの graceful exit。 |
aH7.setupGracefulShutdown | — | 651511 | SIGINT/SIGTERM を B9 に接続する。 |
cH.createRoot / Mu | (opts) => Promise<InkRoot> | 651518 | stdout を共有する Ink ルートノードを作成する。 |
v3_ | ({exitOnCtrlC}) => Promise<InkRoot> | 606748 | PTY 引き渡し後に Ink ルートを再構築する。 |
Q5.get(process.stdout)?.handoffAltScreen | — | 606681 | attach 用に alt-screen の制御権を PTY に渡す。 |
Q5.get(process.stdout)?.unmount | — | 606926 | in-process で fleet view を再マウントする前に Ink をアンマウントする。 |
Q("tengu_*") / yH / xH / j6 | テレメトリ sink | 各所 | tengu_bg_dispatch、tengu_background_fork、fleet_view_*。 |
IK("allow_remote_sessions") | ポリシーチェック | 644468 | 組織レベルのスイッチ。 |
Xc = isAgentsFleetEnabled() | — | 149497 | 総合ゲート:managed config、env、fast-path policy を統合する。 |
fleetGateRejected(verb) | (string) => Promise<never> | 651424 | 拒否理由を出力して終了する。 |
EAH("claude agents") | — | 604574 | ターミナルウィンドウのタイトルを設定する。 |
vh() / zAH() | 文字列 | 606686/606747 | alt-screen に入る / 退出する ANSI シーケンス。 |
b_() / H6(updater) | 設定リポジトリ | 604558/605650 | fleetViewGroupMode などを永続化する。 |
B26(cwd, {q,collapsed}) / ebK / qxK | bg-view UI 状態 | 604647 | cwd ごとに「前回の検索と折りたたみ状態」を記憶する。 |
s6() | () => {columns, rows} | 604583 | ターミナルサイズ。 |
pA() | () => boolean | 604578 | ターミナルが現在フォーカスされているか。 |
un_() | alt-screen flag | 606680 | alt-screen を所有しているか。 |
lT() | () => WorktreeInfo | null | 606956 | 現在の worktree 情報。 |
g$() / qp() | name 検索 | 543840 | ユーザー設定 / 自動派生された job 名。 |
WE(msg) / oI(msg) | transcript 補助 | 543828 | assistant/user メッセージ本文を抽出する。 |
viH(text) | メタ情報テキスト判定 | 543834 | intent を選ぶ際にスラッシュコマンドのエコーをスキップするために使う。 |
LD(state) | (JobState) => boolean | 544019 | job が終了状態にある。 |
npH(state) | activity 分類器 | 602954 | "success"/"failure"/"stopped" または undefined。 |
daemon プロトコル(V$)は、少なくとも次のリクエストを受け付ける:
type DaemonReq =
| { proto: number; op: "list" }
| { proto: number; op: "dispatch"; d: JobDispatch; timeoutMs?: number };
type JobDispatch = { // 543001-543027
proto: number; short: string; sessionId: string; createdAt: number;
source: "shell"|"slash"|"fleet"|"spare"|"respawn"|"left_arrow";
cwd: string;
launch:
| { mode: "prompt"; args: string[] }
| { mode: "resume"; sessionId: string; fork: boolean; flagArgs: string[] };
respawnFlags: string[]; env: Record<string,string>; reattachEnv?: unknown;
worktree?: { path: string; ownershipToken: string };
isolation: "worktree" | "none";
agent?: string; routine?: string;
seed: { intent: string; name?: string };
cols?: number; rows?: number;
};
4. 登録
4a. /bg スラッシュコマンド(543995 行)
const backgroundCommand = {
type: "local-jsx",
name: "background",
aliases: ["bg"],
description: "Continue this session in the background and free the terminal",
immediate: (raw) => !raw.trim(), // 追加の prompt がない場合は確認をスキップ
isEnabled: () => true,
load: () => Promise.resolve().then(() => (_loadBgImpl(), _bgImplExports)),
};
load() を解決すると、{ default: handleBgSlashCommand } をエクスポートするモジュールが得られる。その中身は次のとおり。
// 543958 行 —— Yv3
const handleBgSlashCommand = async (
onDone: (err?: string) => void,
ctx: { messages: Message[] },
args: string
) => {
if (L7() /* 自分自身が bg session */) {
Q("tengu_background_already_bg", {});
onDone(); q4H();
return null;
}
if (YDH() /* セッション永続化が無効 */)
return (onDone(
"Cannot background — session persistence is disabled, so the forked job would have nothing to resume."
), null);
const extraPrompt = (args ?? "").trim();
const seed = extractSeedFromTranscript /*Bf6*/ (ctx.messages, extraPrompt);
if (seed === null)
return (onDone("Nothing to background yet — send a message first."), null);
return React.createElement(BackgroundConfirmDialog /*Jv3*/, {
onDone, prompt: extraPrompt, seed, messages: ctx.messages,
});
};
4b. /stop スラッシュコマンド(bg job 内でのみ有効、544066 行)
const stopJsx = {
type: "local-jsx", name: "stop", immediate: true, isEnabled: L7,
description: "Stop this background session; transcript and worktree are kept",
requires: { ink: true },
load: () => ({ call: (done) => (done(), markSelfStoppedAndExit("stop_command"), null) }),
};
const stopNonInteractive = {
type: "local", name: "stop", supportsNonInteractive: true,
isEnabled: L7,
load: () => ({ call: () => (markSelfStoppedAndExit("bridge"), { type: "skip" }) }),
};
4c. claude agents Commander サブコマンド(647846 行、fallback のみ)
H.command("agents")
.description("Manage background and configured agents")
.option("--setting-sources <sources>",
"Comma-separated list of setting sources to load (user, project, local).")
.action(async () => {
const [{ agentsHandler }, { createSubcommandRoot }] = await Promise.all([
import("./agents-handler.js"),
import("./subcommand-root.js"),
]);
await agentsHandler(await createSubcommandRoot());
process.exit(0);
});
4d. 総合スイッチ設定項目(53308 行)
disableAgentView: {
type: "boolean",
description:
"Disable agent view (`claude agents`, `--bg`, /background, the on-demand daemon). " +
"Typically set in managed settings. Equivalent to CLAUDE_CODE_DISABLE_AGENT_VIEW=1.",
};
5. エントリ / ディスパッチ —— mainCliEntry()(CLI の main)
mainCliEntry(難読化名 je3)は Commander より前に走り、claude agents が main() の重い import 経路を通らず、直接 FleetView に入れるようにしている。
// 651265
async function mainCliEntry /*je3*/ () {
const argv = process.argv.slice(2);
// … --version / --daemon-worker / --bg-pty-host / --bg-spare / bridge / daemon などの早期 return 経路
// ───────── bg 動詞 fast path(651387 行) ─────────
if (
argv[0] === "logs" || argv[0] === "attach" || argv[0] === "stop" ||
argv[0] === "kill" || argv[0] === "respawn" || argv[0] === "rm" ||
argv.includes("--bg") || argv.includes("--background")
) {
if (!isAgentsFleetEnabled()) return fleetGateRejected(verb);
const bg = await import("./bg-cli.js"); // QGK
switch (argv[0]) {
case "logs": return bg.logsHandler(argv[1]);
case "attach": return bg.attachHandler(argv[1]);
case "stop": case "kill": return bg.stopHandler(argv[1]);
case "rm": return bg.rmHandler(argv[1]);
case "respawn": return bg.respawnHandler(argv[1]);
default: {
// --bg / --background はまだ argv 内にあり、最初の positional が prompt になる
Q("tengu_background", { via_flag: true, via: "flag" });
await bg.handleBgFlag(argv);
return process.exit(process.exitCode ?? 0);
}
}
}
// ───────── fleet-view fast path(651485 行) ─────────
const wantsAgents = argv[0] === "agents" && argsAreOnlyDebugFlags(argv.slice(1));
const wantsDefault = argsAreOnlyDebugFlags(argv) && process.stdin.isTTY && process.stdout.isTTY;
if ((wantsAgents || wantsDefault)) {
enableConfigs();
const defaultsToAgents = getGlobalConfig().defaultToAgentsView === true;
if (wantsAgents || defaultsToAgents) {
const policyErr = await loadFastPathPolicy();
if (policyErr) exitWithError(policyErr);
await ensureFleetGateHydrated();
if (isAgentsFleetEnabled()) {
initSinks(); setupGracefulShutdown(); setIsInteractive(true);
Q("tengu_fleetview", { defaultToAgentsView: defaultsToAgents });
const root = await createRoot({ exitOnCtrlC: false });
consumeEarlyInput();
await mountFleetView(root);
await gracefulShutdown(0, "other", { suppressResumeHint: true });
return;
}
}
}
// …それ以外 → main()(つまり Commander。agents サブコマンドの fallback を含む)
}
function argsAreOnlyDebugFlags /*caK*/ (argv) {
for (let i = 0; i < argv.length; i++) {
const a = argv[i];
if (["--debug","-d","--debug-to-stderr","-d2e"].includes(a)
|| a.startsWith("--debug=") || a.startsWith("--debug-file=")) continue;
if (a === "--debug-file" && i + 1 < argv.length) { i++; continue; }
return false;
}
return true;
}
重要な結論は 2 つ:
claude agentsは Commander サブコマンドの中で、parse 前に唯一横取りされる。そのため実際の実装はmountFleetViewであり、.action()内の body ではない —— 後者が実行されるのはisAgentsFleetEnabled()が false を返した場合だけ。- 引数なしの
claudeも、defaultToAgentsView: trueの場合は直接 FleetView に入る。それ以外の場合は通常どおりmain()に落ちて REPL に進む。
6. --bg フラグ → handleBgFlag → daemon dispatch
// 543089 行 —— sN3
async function handleBgFlag(argvWithoutFlag: string[]) {
// argvWithoutFlag は --bg / --background を取り除いた後の argv
const rest = argvWithoutFlag.filter((a) => !["--bg","--background"].includes(a));
const stdinTail = await readBgStdin(); // pGK;16 KiB を超えると切り詰められる
const merged = stdinTail ? withStdinPositional(rest, stdinTail) : rest;
const res = await spawnBgSession(merged); // pHH → aN3
if (!res.ok) {
process.stderr.write(res.error + "\n");
process.exitCode = 1;
return;
}
process.stdout.write(formatPostBackgroundHints(res.short, res.idle ? "(idle — waiting for…)" : undefined) + "\n");
}
6a. spawnBgSession の中核 —— spawnBgSessionAndDispatch / aN3(542926 行)
以下はそれを分解した後の擬似コード:
async function spawnBgSessionAndDispatch /*aN3*/ (
argv, source, cwd, seed /* Bf6 の結果 */, reattachEnv, t /*ids*/
) {
const { sessionId, short, jobDir, freshDir } = t;
const dashDash = argv.indexOf("--");
const flagArgs = dashDash >= 0 ? argv.slice(0, dashDash) : argv;
const agentName = readFlag("--agent", flagArgs);
const sessionName = readFlag("--name", flagArgs) ?? readFlag("-n", flagArgs);
const resumeId = parseResumeTarget(flagArgs); // FGK
const promptArg = dashDash >= 0
? argv.slice(dashDash + 1).join(" ")
: positionalArgFor(argv, resumeId);
const isResume = flagArgs.some(f => f === "-c" || f === "--continue" ||
f === "-r" || f === "--resume" ||
f.startsWith("--resume=") || f.startsWith("-r="));
const isFork = flagArgs.includes("--fork-session");
const respawnFlags = respawnableFlags(flagArgs); // Ov3
const dispatchFlagArgs = dashDash >= 0 ? respawnFlags : flagsWithoutPositional(respawnFlags);
const resumingSelf = resumeId !== undefined && resumeId === sessionId;
const forkFlag = isResume && !isFork ? ["--fork-session"] : [];
const sessionArgs = resumingSelf ? [] : ["--session-id", sessionId, ...forkFlag];
if (source === "shell" && readFlag("--session-id", flagArgs))
warn("--bg manages the session id; ignoring --session-id (use --resume <id> instead)");
const subagent = agentName
? (await uk(cwd ?? process.cwd())).activeAgents.find(a => a.agentType === agentName)
: undefined;
if (agentName && !subagent && source === "shell")
warn(`no agent named '${agentName}' — spawning with default template`);
const intent = seed?.intent ?? promptArg ?? "";
const isIdle = !agentName && intent === "" && !seed?.detail;
// state.json を事前書き込み(fleet / spare 由来はスキップ。これらは dispatch 経由で seed を運ぶ)
if (source !== "fleet" && source !== "spare") {
const prior = freshDir ? null : await readJobState(jobDir);
if (prior === null)
writeJobState(jobDir, oqH({
template: {
name: agentName ?? "bg",
description: subagent?.whenToUse ?? "",
initialPrompt: subagent?.initialPrompt,
},
respawnFlags: dispatchFlagArgs,
intent,
name: sessionName ?? seed?.name,
nameSource: sessionName ? "user" : seed?.nameSource,
detail: seed?.detail ?? (isIdle ? "(blocked)" : undefined),
tempo: isIdle ? "blocked" : undefined,
sessionId, cwd: cwd ?? process.cwd(),
worktreePath: seed?.worktree?.path,
worktreeBranch: seed?.worktree?.branch,
worktreeHookBased: seed?.worktree?.hookBased,
originCwd: seed?.worktree?.originCwd,
}));
else if (dispatchFlagArgs.length && !prior.respawnFlags.length)
writeJobState(jobDir, { ...prior, respawnFlags: dispatchFlagArgs });
}
// daemon に投げる
const dispatch = {
proto: I1, short, sessionId, createdAt: Date.now(),
source: source === "repl" ? "slash" : source,
cwd: cwd ?? process.cwd(),
launch: isResume && resumeId
? mode: "resume", sessionId: resumeId,
fork: !resumingSelf && (isFork
: { mode: "prompt", args: [...sessionArgs, ...positionalsAfterFlags(argv)] },
respawnFlags, env: bgEnvWhitelist(), reattachEnv,
worktree: seed?.worktree && { path: seed.worktree.path, ownershipToken: sessionId },
isolation: subagent?.isolation === "worktree" && subagent.source !== "built-in" ? "worktree" : "none",
agent: agentName, routine: undefined,
seed: { intent, name: sessionName ?? seed?.name },
cols: process.stdout.columns, rows: process.stdout.rows,
};
const r = await daemonRpcDispatch(dispatch); // AB8 → V$
if (r.ok) return { ok: true, short, sessionId, idle: isIdle };
// フォールバック分岐:
// - ack-timeout / enoconn だが op:"list" で worker がまだ見える → 成功と判定
// - ack-timeout かつ list に現れない → 1 回再投下
// - short-alive → "session is already running" という親切なエラー
// - stale-short → "前のセッションがまだ終了中なので、あとで再試行"
// - daemon-unreachable → 終了し、status の実行を促す
//(543030-543087 を参照)
}
6b. stdin を positional としてマージ —— withStdinPositional / BGK(543129 行)
ユーザーが prompt テキストを --bg にパイプし、かつ argv にすでに positional がある場合、stdin の末尾は最後の positional に追加される(間は改行で区切られる)。そのため daemon からは、マージ済みの prompt が 1 つだけ見える。argv に positional がない場合は、-- センチネルの後ろに新しい 1 件が追加される。
6c. 関連 CLI 動詞(543217 行以降)
claude logs <prefix>→logsHandler—— daemon に 500 ms の RPC 購読を開き、streamTailスナップショットを出力して終了する。claude attach <prefix>→attachHandler—— まずヘルスチェック(mW_)を行い、その後MQ(short)が PTY を引き継ぐ。ERESPAWNINGに遭遇した場合は最大 20 回リトライする。ENOJOBの場合は、より有用なエラー情報のためにstate.jsonから内容を読み取る。claude stop / kill / rm / respawn <prefix>→ 対応するハンドラ(542861 行でM_(QGK,…)によりまとめて登録)。
すべての prefix 引数は resolveJobByShortPrefix /* jB8 */(543217 行)で解決される:readdir で ~/.claude/bg-jobs/ を走査し、prefix が一意であることを確認する。
7. /bg スラッシュコマンド —— 確認ダイアログと seed 抽出
7a. extractSeedFromTranscript —— Bf6(543822 行)
transcript を下から上へ走査し、{ intent, name, nameSource, detail } を生成する:
function extractSeedFromTranscript(messages, override) {
let firstUserText = override, hasNonMetaUser = false, lastAssistant: string|undefined;
for (let i = messages.length - 1; i >= 0; i--) {
const m = messages[i];
if (m.type === "assistant" && lastAssistant === undefined) {
const text = WE(m); // 最下部の assistant メッセージ本文
if (text) lastAssistant = text.replace(/\s+/g," ").trim().slice(0,120);
}
if (m.type === "user" && !m.isMeta && !QY6(m)) {
const text = oI(m)?.trim();
if (text && viH(text)) continue; // スラッシュコマンドのエコーをスキップ
hasNonMetaUser = true;
if (!firstUserText && text) firstUserText = text;
}
if (hasNonMetaUser && firstUserText && lastAssistant !== undefined) break;
}
if (!hasNonMetaUser) return null; // "Nothing to background yet"
const userName = g$(jobSessionId());
const autoName = qp(jobSessionId());
return intent: (firstUserText;
}
7b. BackgroundConfirmDialog —— Jv3(543849 行)
J_を通じて 2 つの AppState フィールドを読む:effortValue(Dv3)taskCount(jv3——state.tasks)
- in-flight task 数が 0 の場合、コンポーネントの初回レンダー時に自動で確認済みにする(
React.useState(taskCount === 0))。 - それ以外の場合は
<ConfirmDialog>をレンダーし、タイトルは "Background this session?"、サブタイトルは"<N> tasks running — the forked session won't carry live processes."、ボタンはそれぞれ "Background anyway (tasks will be abandoned)" と "Stay"。 - 確認後:
ts const r = await forkCurrentSessionToBackground /*pf6*/ ( seed, extraPrompt, effortValue, "command", messages ); Q("tengu_background_fork", { confirmed: taskCount > 0, inflight_count: taskCount, had_prompt: extraPrompt.length > 0, had_worktree: r.hadWorktree, worktree_handed_off: r.handedOff, }); onDone(); await B9(0, "prompt_input_exit", { suppressResumeHint: true, finalMessage: formatPostBackgroundHints(r.short, r.handedOff ? "(worktree handed off)" : undefined), }); forkCurrentSessionToBackground(pf6、同モジュール)——source: "slash"でspawnBgSessionを呼び出し、アクティブな worktree を引き渡し、bridge / daemon リスナーを取り外す(DV())。
7c. markSelfStoppedAndExit —— Uf6(544013 行)
async function markSelfStoppedAndExit(source: "stop_command"|"bridge") {
Q("tengu_bg_agent_action", { action: "stop", source, jobSessionId: jobSessionId() });
const dir = process.env.CLAUDE_JOB_DIR;
if (L7() && dir) {
const now = new Date().toISOString();
const cur = await readJobState(dir);
if (cur && !isJobTerminal(cur))
await writeJobState(dir, {
...cur, state: "stopped", detail: "stopped from session",
tempo: "idle", needs: undefined, block: undefined, inFlight: undefined,
updatedAt: now,
firstTerminalAt: cur.firstTerminalAt ?? now,
});
if (isInteractive()) process.stdout.write(formatExitNotice("Session stopped."));
}
return B9(0, "prompt_input_exit", { suppressResumeHint: true });
}
8. FleetView —— TUI 部分
8a. mountFleetView —— uQ3(606636 行)
メインループは、描画 → アクション待ち → 必要なら attach → アンマウントして再マウントという流れ。agent view のライフサイクル全体を通して alt-screen を専有する。
async function mountFleetView(inkRoot) {
Q("tengu_bg_agent_action", { action: "list_open" });
setTerminalTitle("claude agents");
const preselectId = process.env.CLAUDE_AGENTS_SELECT;
delete process.env.CLAUDE_AGENTS_SELECT; // 1 回限りの使用
const persisted = await loadFleetUiState(currentCwd());// qxK
let query = persisted?.q;
let collapsed = persisted?.collapsed;
let jobId = preselectId;
let error: string | undefined;
let groupMode: "state"|"directory"|undefined;
let savedAppState: any;
for (;;) {
const action = await new Promise<FleetAction>((resolve) => {
inkRoot.render(
<ProcessTitleProvider>
<AppStateBoundary
initialState={savedAppState && {...savedAppState, notifications:{current:null,queue:[]}}}
onChangeAppState={({newState}) => savedAppState = newState}
>
<ContextProviders>
<ScrollFrame>
<FleetView
onAction={resolve}
initialJobId={jobId}
initialQuery={query}
initialCollapsed={collapsed}
initialError={error}
initialGroupMode={groupMode}
/>
</ScrollFrame>
</ContextProviders>
</AppStateBoundary>
</ProcessTitleProvider>,
);
});
const inAlt = ownsAltScreen(); // un_
if (inAlt && action.type === "open") handoffAltScreen();
if (!inAlt) inkRoot.render(null);
inkRoot.unmount();
if (action.type === "done") break;
// ── action.type === "open" → attach ──
if (isWindows() && process.stdin.isTTY) { process.stdin.setRawMode(true); process.stdin.ref(); }
const restoreCursor = inAlt
? batchUpdate(() => process.stdout.write(altScreenReentry()))
: () => {};
jobId = action.job.id;
query = action.query;
collapsed = action.collapsed;
groupMode = action.groupMode;
cachedJobs = action.jobs;
cachedLoopKicks = action.loopKicks;
cachedStatuses = action.statuses;
cachedStatusesTs = action.statusesTs;
cachedPrStatuses = action.prStatuses;
cachedFirstMount = true;
const respawn = action.respawnResult ?? await respawnJob(action.job.id, {
knownState: action.freshDispatch ? undefined : action.job.state,
knownAlive: (Date.now() - action.statusesTs < 1500
&& action.statuses.get(action.job.state.resumeSessionId
?? action.job.state.sessionId) !== undefined)
? true : undefined,
});
if (respawn.ok || respawn.alive) {
logFleetViewLifecycle("attach", action.job.state);
process.stdout.write(SET_TITLE(renderJobLabel(action.job.state, true)));
let attach = await attachJob(respawn.short ?? action.job.id, { alreadyInAlt: inAlt });
if (attach.kind === "error" && attach.orphaned) {
// 最後の手段:強制 respawn してから再試行
const re = await respawnJob(action.job.id, { force: true, knownState: action.job.state });
if (re.ok || re.alive) attach = await attachJob(re.short ?? action.job.id, { alreadyInAlt: inAlt });
else attach = { kind: "error", msg: re.error };
}
if (attach.kind === "error" && !attach.ended) {
error = attach.msg;
xH("fleet_view_open", "attach_failed");
} else {
if (attach.msg) error = attach.msg;
yH("fleet_view_open");
}
logFleetViewLifecycle("detach", action.job.state, { attachDurationMs: Date.now() - tStart });
} else {
error = respawn.error;
xH("fleet_view_open", "respawn_failed");
}
if (inAlt) process.stdout.write(restoreAltMargins());
inkRoot = await createRoot({ exitOnCtrlC: false });
restoreCursor();
}
}
8b. FleetView コンポーネント —— tmK(604467 行)—— 状態機械
コンポーネント内部の状態(おおむね useState / useRef の順):
| スロット | 用途 |
|---|---|
jobs / setJobs (A, z) | 現在マウントされている JobRow のリスト。bootstrap 中は null。スナップショットは $.current にミラーされ、コールバックから使われる。 |
incomingJobs (Y, w) | 今回のマウント内で新規開始され、まだ state.json の書き込みを待っている job。 |
prStatuses (F, g) | PR check 状態のキャッシュ。 |
cursorIndex (d, a) | フラット化後の行リスト h9 のインデックス。 |
peekJob (_H, t) | 現在 peek 中の job。 |
dispatchFolded (qH, i) | "queued/folded" セクションを折りたたむかどうか。 |
replyDrafts (e.current) | Map<jobId, draftText>:peek パネルのインライン返信ドラフト。 |
replyErr ($H, AH) | 直近の返信失敗情報。 |
helpOpen (s, o) / confirmDelete (HH, KH) / selectMenu (jH, YH) | 各種 overlay モーダル。 |
exiting (MH, kH) | 終了の二重確認。LH = ph(setExiting, () => onAction({type:"done"})) が Ctrl+C を連結する。 |
renaming (WH, DH) | リネーム中の job、または null。 |
attaching (XH, NH) | attach 前に respawn 中の job id。 |
error ($9, L6) | toast / エラー行。 |
groupMode (tH, v_) —— "state" | "directory" | b_().fleetViewGroupMode に永続化される。 |
collapsedGroups (UH, BH) —— Set<string> | cwd ごとに永続化される。 |
manuallyCollapsedPinned (FH, q_) | pinned セクションを手動で折りたたむが、永続化集合には影響させない場合に使う。 |
cwd (L) → 最終的に repo ルート(_Y(cwd))へ解決される。 | |
pinnedDir map (u, b) | cwd ごとの高速 pin マッピング。 |
query / cursorOffset(v0(...) 複数行入力 hook 由来) | 検索ボックス。 |
同じモジュール(602820–602850 行)から、repoGroup({cwd}) (Nd8)、pickIcon、jobLabel、childStatusColor などの補助関数もいくつかインポートしている。
8c. キーバインド —— handleFleetViewKeyDown / wS(605589 行)
```text
overlay 表示中(renaming / attaching):
Ctrl+C overlay をキャンセル
up/down overlay 自身のスクロールロジックへ素通し
その他 overlay 内部の入力処理へ委譲
グローバル:
Ctrl+C 入力をクリア → ヘルプ/削除確認が開いていれば閉じる → それ以外なら終了
Escape ヘルプを閉じる → 削除確認を閉じる → 折りたたみヘルプを閉じる → メニューを閉じる → 入力をクリア → armed アクションを取り消す → onAction("done")
Shift+up/dn グループ内で job を移動(Sy/SY)
Ctrl+R 選択中の job をリネーム (renaming = job.id)
Ctrl+S "state" ↔ "directory" のグルーピングモードを切り替え
Ctrl+G クエリ内で展開 / 置換
Ctrl+T 選択中の job を pin / unpin
up / Ctrl+P カーソルを移動(または補完候補をスクロール)
down / Ctrl+N カーソルを移動
Ctrl+X(ヘルプ表示中)→ armed アクションの対象を kill
Tab スラッシュ候補を受け入れる / dispatchFolded を切り替え
→ 現在行に attach(ブログでいう right-arrow)
⌘/Super+1-9 現在の cwd グループの N 行目に attach
Return 入力が空なら: /exit/quit を認識して終了 → それ以外なら候補を送信 → "<template> <intent>" を解析し、
新しい bg job を dispatch →(Shift+Return ⇒ さらに {type:"open", freshDispatch:true} で自動 attach)
空入力で Space 現在行の peek モードを切り替え(ブログでいう "peek")
FleetView の「左方向キー」は複数行入力 hook(v0 のデフォルト cancel)で処理される —— 入力が空のときは onCancel: setExiting にフォールバックし、ph が引き継いで「もう一度押すと終了確認」(LH)を行う。
8d. peek パネル —— PeekPane / WQ3(603580 行)
これはブログで説明されている "peek mode"、つまり特定の行を選択するが完全には attach しないモード。
q.state.updatedAtを読み、r7(now - updated)をレンダリングして "Last interaction X ago" を表示する。直近 1 分以内は 1 秒ごとに再レンダリングし、それ以外は 30 秒ごと。q.state.outputから情報行を 3 つ選ぶ(インライン子リストのラベルと重複するものは除外)。さらにstate.needsとstate.detailを加え、端末の高さに応じて自動調整する。- これは独自の
v0入力欄を持ち、replyDrafts.get(jobId)にバインドされる:onSpaceOnEmpty: onBack—— Space で peek を抜ける。onTabOnEmpty: () => setQuery(suggestedReply); Q("tengu_prompt_suggestion", { outcome: "accepted", source: "fleetview_peek" })—— Tab でstate.suggestedReplyを受け入れる(tempo === "blocked"かつ questions フォームがない場合にのみ存在)。onExit→onReply(formattedDraft)で下書きを送信する。成功後、daemon 側の worker が自動で再開する —— これがブログでいう「インライン返信の自動 resume」の実装。
state.tempo === "blocked"かつstate.block.questionsが存在する場合、<QuestionForm>をレンダリングする(jH = 2 + opts.length + 1行分の追加レイアウト領域)。
8e. 視覚インジケータのルール
- 行の色 / activity グリフ =
jobActivityForRow(jobRow, prStatuses)→"flowing" | "slowing" | "stuck" | "success" | "failure" | "stopped"。"slowing" / "stuck" のしきい値はupdatedAt由来:tempo === "active": 3 分 → "flowing"、15 分超 → "stuck"。- それ以外: 15 分 → "flowing"、75 分超 → "stuck"。
- "awaits input" =
tempo === "blocked"(block.questions が存在)またはstate.needsが空でない。 - "actively processing" =
tempo === "active"かつ終端状態ではない。 - "completed" =
LD(state)が真。 - "next run time"(ループ job)=
vd8(jobRow, loopNextRun)で、"in <時間>"と表示する。
## 9. 永続化
* **job ごとに 1 つの `state.json`** —— FleetView と `claude logs/attach/...` にとって唯一の真実の情報源。`pT(jobDir, partial)` によりアトミックに merge 書き込みされる。
* **`~/.claude/bg-jobs/`** —— `<short>/` サブディレクトリの集合。stdout/stderr は worker の PTY 履歴(daemon RPC 経由で読み取られ、ディスクは**経由しない**)。
* **FleetView UI 状態(cwd ごと)** —— `B26(cwd, { q, collapsed })` は repo パスを key とし、読み込み時は `qxK(repoRoot)` 経由、書き込みには 300 ms の debounce がある。
* **グループ化モード設定** —— `b_().fleetViewGroupMode` は `H6(updater)` 経由でユーザー設定リポジトリに書き込まれる。
* **Daemon socket / ハートビート** —— `.fleetview-heartbeat` ロックファイル(`Moq`、104916 行)。
---
## 10. エンドツーエンドの時系列
```text
A) `claude --bg "ship the redis fix"`
argv が mainCliEntry() に入る → `argv.includes("--bg")` に命中(651394 行)
│
▼
fleetGate ok → handleBgFlag(argv) [543089]
│ stdin (TTY) → "";rest = argv \ ["--bg"]
▼
spawnBgSession(rest) [pHH, 542905]
│ sessionId, short, jobDir を生成
▼
spawnBgSessionAndDispatch [aN3, 542926]
│ • state.json を書き込み(seed データ)
│ • JobDispatch を構築
▼
daemon V$ {op:"dispatch", d} [543031]
│ daemon は ok / ack-timeout / enoconn / short-alive / stale-short を返す可能性
▼ ack-timeout の再投下 / レスキューループ
stdout ← formatPostBackgroundHints(short, idle?) [543177]
exit 0
B) リアルタイムセッション内で `/bg` を入力
REPL が /bg を受け取る → handleBgSlashCommand をロード [Yv3, 543958]
│ ガード:すでに bg / 永続化が無効
▼
extractSeedFromTranscript が transcript をたどる → {intent,name,detail}
▼
<BackgroundConfirmDialog/> [Jv3, 543849]
│ task=0 は自動確認;それ以外は <ConfirmDialog> を表示
▼
forkCurrentSessionToBackground(seed, prompt, effort, "command", messages) [pf6]
│ --bg と同じ dispatch 経路だが、source="slash"、worktree を移譲し、bridge を切り離す
▼
Q("tengu_background_fork", …)
onDone() → B9(0, "prompt_input_exit", {finalMessage: formatPostBackgroundHints(short, …)})
C) `claude agents`(または REPL 中で ←)
REPL 中で ← → forkAndOpenFleetViewFromRepl(messages, effort, autoName) [KpK, 606947]
│ extractSeedFromTranscript(空を許可)→ wB8 が state.json を事前書き込み
│ forkCurrentSessionToBackground が bg fork を起動
│ Q("tengu_open_agents_via_left")
│ tengu_bg_leftarrow_inprocess スイッチが true の場合:
│ → remountFleetViewInProcess(この short を事前選択)
│ それ以外:
│ → FjH({ args:["agents"], env:{CLAUDE_AGENTS_SELECT: short} }) ← 新しい claude を exec
`claude agents` → mainCliEntry() → argsAreOnlyDebugFlags を通過 → isAgentsFleetEnabled()
│
▼
mountFleetView(root) [uQ3, 606636]
┌──────────────── ループ ────────────────┐
▼ │
<FleetView/> をレンダリング → ユーザーがナビゲート ────────────────┤
│ │
▼ onAction === "open" │
respawnJob → attachJob (PTY) │
│ ユーザー作業 → 最終的に detach │
▼ onAction === "done" │
gracefulShutdown(0) │
└──────────────────────────────────────┘
11. 落とし穴 / 校正メモ
claude agentsは 2 つのコマンドの合体。 647846 行で Commander に登録されているagentsHandler(Cs3)は、fleet view が 閉じている ときだけ有効になる。これは source ごとにグループ化された subagent 定義リストを出力する。ブログで言う "agent view" は、実際には 651485 行の fast-path FleetView。/bgはエイリアス。 本名は/background(543999 行)で、bgはaliasesにある。immediate: (raw) => !raw.trim()は、単に/bg<Enter>と入力した場合は JSX の確認をスキップする、という意味。prompt テキスト付きだと React ツリーで止まる。/bgと--bgは同じ dispatch を通る。 唯一の違いは、(a)state.jsonに書かれるsourceフィールドが異なること、(b) 親プロセス側で worktree / bridge を分解する必要があるかどうか。daemon 側はまったく区別しない。L7()(「bg 内かどうか」)≠「セッションがバックグラウンド化済み」。 これは本プロセスのCLAUDE_JOB_DIRが設定されている場合にだけ true を返す。つまり「自分がその fork である」という意味。fork を起動した直後の前景プロセスは、まだ「バックグラウンド化済み」には属さない。そのため/bgは dispatch 後にB9(0, "prompt_input_exit")を呼び、前景側を能動的に終了させる。CLAUDE_AGENTS_SELECTは使い捨て。mountFleetViewは受け取るとすぐ env からdeleteする(606640 行)。長寿命の fleet view が古い id に固定されるのを避けるため。- "Background anyway" の文言が指すのは tasks であって files ではない。 読んでいる
taskCount(jv3 = state => state.tasks)は AppState のtasksmap 由来、つまり bash / 長時間実行ツールのこと。in-flight リクエストではない。別概念だが、UI 文言では tasks としか言っていない。 - 左方向キーでの終了は入力が空のときだけ有効。 実際に発火するのは複数行入力 hook
v0のonCancel。入力内容がある場合、左キーは単にカーソルを移動するだけ。 disableAgentViewは唯一の一括遮断スイッチ。 いったん有効になると(管理設定または env)、claude agentsはagentsHandlerにフォールバックし、--bg/--backgroundは gate で止められ、/backgroundスラッシュコマンドはisEnabledが常に true でも内部のpHHがfleetGateRejectedで失敗し、daemon も起動を拒否する。spawnBgSessionは 2 種類の daemon 障害を救済する:ack-timeout(worker はまだ生きているが daemon の応答が失われた)とenoconn(RPC 途中で socket が消えた)。まずop:"list"を送って探り、成功宣言するか、1 回だけ再投入を試みる。- ポーリング間隔。 FleetView 内の peek タイムスタンプは、直近 1 分間は 1 秒ごと、それ以上は 30 秒ごとに再レンダリングされる。ただし file watcher / RPC poller の頻度は別モジュール内(602820 付近の
seedLastJobsと別 hook 群)。移植時はここを別途書き直す必要がある。 claudeはdefaultToAgentsView: trueと組み合わせるとデフォルトで FleetView に入る。 ユーザーが設定パネル(462620 行)で有効化したあとに素のclaudeを実行すると、そのまま入る。意図せず発火しやすい。
12. 他プロジェクトへ移植するための最小実装チェックリスト
「agent view + /bg」を別の shell(たとえば Anthropic CLI の fork、あるいは無関係なアシスタント CLI)へ持っていきたい場合:
- ディスクモデル。
~/.claude/bg-jobs/<short>/state.json:アトミックな merge 書き込み、readdirによる prefix 検索、第 1 節で定義したJobStateschema。 - Daemon。 Unix socket サービスを 1 つ用意し、最低限
{op:"list"}、{op:"dispatch", d:JobDispatch}、および job ごとの PTY attach RPC をサポートする。macOS/Linux はユーザーごとに 1 つのunix:プロトコル socket;Windows は同じ JSON 封筒の named pipe。管理設定でハードに無効化しやすいよう、fast_path_policyフラグを付ける。 - Worker。 daemon が
JobDispatch.env内の環境で起動する。環境にはCLAUDE_JOB_DIR=<dir>、--session-id <uuid>を含め、prompt は最後の positional にする。worker 自身がstate.json(intent、name、output、tempo、block)を update する。 /bgスラッシュコマンド。local-jsx、aliases: ["bg"]。seed は transcript を下から上へ走査して得る(meta とスラッシュコマンドのエコーはスキップ)。state.tasks > 0のときは確認を出し、それ以外は自動確認。--bgCLI フラグ。 この flag を取り除き、pipe stdin を読む(16 KiB で切り詰め)、同じ dispatcher に流し込む。成功時は 5 行のヒントブロックを表示する。claude logs / attach / stop / kill / rm / respawn <prefix>。 prefix を完全な short に解決して RPC を送る。attachは要注意:PTY を引き継ぎ、ERESPAWNINGに耐えられる必要がある。- FleetView TUI。
mountFleetView(inkRoot)ループ:レンダリング →type:"open"を待つ →openなら respawn + attachJob + detach 後に再レンダリング。- コンポーネント内部:cwd ごとに永続化される UI 状態(query + collapsed)、state/directory のグルーピング切り替え、二重用途の peek パネル(見る/返信する)、複数行入力、リネーム overlay、削除確認 overlay、ヘルプ overlay、グループ内ドラッグソート。
attachingref を別途維持し、同じ行のダブルクリックで 2 回発火しないようにする。
- ゲート。 管理設定
disableAgentView+ envCLAUDE_CODE_DISABLE_AGENT_VIEW+ 組織ポリシーが返すfast_path_policy。各入口の前で必ず同じisAgentsFleetEnabled()を通す。 - テレメトリ。 最低限:
tengu_background(flag/slash)、tengu_background_fork、tengu_bg_dispatch、tengu_bg_dispatch_fallback、tengu_bg_dispatch_rescued、tengu_open_agents_via_left、tengu_fleetview、tengu_bg_agent_action、tengu_prompt_suggestion、およびすべてのfleet_view_*アクション動詞(open / rename / pin_toggle / reorder / stop / attach / detach)。 - 左方向キー → agents。 REPL 入力欄の「空 + 左方向キー」を hook する:(a) 現在の transcript から fresh bg fork を seed、(b) 現在の worktree/bridge を分解、(c) in-process で fleet view を再マウントするのを優先;失敗時は
execにフォールバック。 - リスク声明の gating。
--bgとbypassPermissionsまたはauto権限モードを組み合わせる場合、先に対話式で 1 回確認が必要 —— 543643–543646 行のチェックを迂回してはならない。headless dispatch が UX ゲートをすり抜けるのを避けるため。
13. シンボル対応早見表
bg-cli とスラッシュコマンドモジュール(QGK / gW_)
| リネーム後 | 難読化名 | 行番号 |
|---|---|---|
preSeedReplBgJob | wB8 | 542877 |
spawnBgSession | pHH | 542905 |
spawnBgSessionAndDispatch | aN3 | 542926 |
markBgDispatchSucceeded | IGK | 542802 |
markBgDispatchFellThrough | OB8 | 542820 |
handleBgFlag | sN3 | 543089 |
readBgStdin | pGK | 543108 |
withStdinPositional | BGK | 543129 |
humanizeDispatchFailureReason | tN3 | 543163 |
formatPostBackgroundHints | mf6 | 543177 |
bgVerbExtraArgsNote | UGK | 543188 |
resolveJobByShortPrefix | jB8 | 543217 |
logsHandler | eN3 | 543246 |
attachHandler | Hv3 | 543271 |
stopHandler | qv3 | (export は 542863 を参照) |
rmHandler | Kv3 | (export は 542865 を参照) |
respawnHandler | _v3 | (export は 542866 を参照) |
parseResumeTarget | FGK | (export は 542869 を参照) |
flagsWithoutPositional | gGK | (export は 542873 を参照) |
extractSeedFromTranscript | Bf6 | 543822 |
BackgroundConfirmDialog | Jv3 | 543849 |
handleBgSlashCommand | Yv3 | 543958 |
BG_CLI_FLAG_ALIASES(["--bg","--background"]) | oN3 | 543733 |
forkCurrentSessionToBackground | pf6 | (呼び出しは 543875、606981 を参照) |
BG_SLASH_COMMAND_DEF | Mv3 / fv3 | 543999 |
getCurrentJobDir | Xv3 | 544011 |
markSelfStoppedAndExit | Uf6 | 544013 |
stopSlashJsxCall | Pv3 | 544047 |
stopBridgeNonInteractiveCall | Wv3 | 544055 |
FleetView モジュール(bd8 / xd8)
| リネーム後 | 難読化名 | 行番号 |
|---|---|---|
FleetView(コンポーネント) | tmK | 604467 |
mountFleetView | uQ3 | 606636 |
remountFleetViewInProcess | emK | 606921 |
forkAndOpenFleetViewFromRepl | KpK | 606947 |
PeekPane(コンポーネント) | WQ3 | 603580 |
renderJobLabel | ZW6 | 602868 |
formatJobAgeOrCountdown | vd8 | 602863 |
classifyJobActivity | jW6 | 602953 |
deriveStateBand | IvH | 602971 |
repoGroupKey | Nd8 | 604497 |
parseDispatchInput | Zd8 | 603040 |
parseSearchQuery | BmK | 603002 |
pruneMap | Pd8 | 602855 |
logFleetViewLifecycle("peek"|"attach"|"detach") | Rd8 | 603598 |
handleFleetViewKeyDown | wS | 605589 |
swapAdjacentJobsInGroup | SY | 605532 |
attachJobOrSurfaceError | YS | 605454 |
runRowKeyAction | lj | 605425 |
CLI エントリと Commander
| リネーム後 | 難読化名 | 行番号 |
|---|---|---|
mainCliEntry | je3 | 651265 |
argsAreOnlyDebugFlags | caK | 651245 |
agentsHandler(fallback —— agent 定義を列挙するもので、jobs ではない) | Cs3 | 644428 |
AgentsList(fallback コンポーネント) | FoK | 644390 |
formatAgentDefinitionRow | BoK | 644383 |
renderAgentSourceGroup | Es3 | 644416 |
isAgentsFleetEnabled | Xc | 149497 |
fleetGateRejected | (同名で re-export) | 651424 |
設定 / 定数
| リネーム後 | 難読化名 | 行番号 |
|---|---|---|
SETTING_DISABLE("disableAgentView") | y | 53308 |
SETTING_DEFAULT_TO_FLEET("defaultToAgentsView") | — | 462620 |
ENV_PRESELECT("CLAUDE_AGENTS_SELECT") | — | 606639 |
ENV_AUTORELAUNCH("CLAUDE_AGENTS_AUTO_RELAUNCHED_AT") | Xd8 | 606765 |
AUTO_RELAUNCH_UNFOCUSED_MS | YW6 | 606763 |
AUTO_RELAUNCH_MIN_INTERVAL_MS | umK | 606764 |
FLEETVIEW_HEARTBEAT(.fleetview-heartbeat) | Moq | 104916 |

WeChat Pay
Alipay
コメント
コメントは即時公開されますが、ポリシー違反時は非表示になる場合があります。