無人自動化が「成功ログを出して何も起きない」3つの理由(Windows・外部CLI経由でURIを撃つ型)
exit 0 を返して実際には何も起きない無人ジョブの3つの構造的原因と回避策。cmd.exe が未引用の & でコマンドを分断して一部パラメータだけ消える/spawn イベントは配送完了ではなく親が先に終了して届かない/更新したプラグインは再読み込みまで旧版が動きディスク版で分岐すると恒久的に詰む。2信号ゲートと判定表、end-to-end を実行できない時の誠実な報告の型まで含む。
約11.6万トークンの節約 (API料金換算で約170円分)。 要件定義・技術調査・試行錯誤ぶんのトークンがまるごと不要になります。※ 出品者申告とレビューに基づく推定値。モデル・タスク内容により変動します。
この巻物について
「無人自動化が「成功ログを出して何も起きない」3つの理由(Windows・外部CLI経由でURIを撃つ型)」は、業務自動化カテゴリのAI指示書(MDファイル)です。exit 0 を返して実際には何も起きない無人ジョブの3つの構造的原因と回避策。cmd.exe が未引用の & でコマンドを分断して一部パラメータだけ消える/spawn イベントは配送完了ではなく親が先に終了して届かない/更新したプラグインは再読み込みまで旧版が動きディスク版で分岐すると恒久的に詰む。2信号ゲートと判定表、end-to-end を実行できない時の誠実な報告の型まで含む。この巻物をAIに読み込ませると、ゼロから設計・調査する場合に比べて 約11.6万トークン(API料金換算で約170円)・97%のトークンを節約できます。
- カテゴリ
- 業務自動化
- 対応AI
- claude-code、cursor、codex-cli
- ライセンス
- 商用利用可 (再販不可)
- 価格
- 無料
- ゼロから開発時
- 約12万トークン
- この巻物使用時
- 約4,200トークン
- 節約量
- 約11.6万トークン (約170円)
- 更新日
- 2026-09-12
使い方 (AIに渡す3つの方法)
いちばん簡単なのはワンライナー。Claude Code のターミナルに貼るだけです。
claude "https://makimono-md.vercel.app/api/v1/files/3-windows-cli-uri/raw を読み込んで、この指示書どおりに実装して"
中身
無人自動化が「成功ログを出して何も起きない」3つの理由(Windows / 外部CLI経由でURIを撃つ型)
対象: Node/PowerShell から Windows の外部 CLI(*.cmd ラッパー等)を起動し、カスタム URI スキームでアプリに指示を送る無人ジョブ。VSCode 拡張への URI 送出が典型だが、**「cmd.exe を経由して引数を渡す」「起動しっぱなしで待たない」「更新したプラグインがまだ有効化されていない」**という構造は他のアプリでも同じ。
3つとも exit 0 を返し、ログに成功と書き、実際には何も起きない。個別には既知でも、重なると原因が特定できなくなる。
罠1: 未引用の & を cmd.exe がコマンド区切りとして食う
URI のクエリは ?a=1&b=2&c=3 の形になる。これを素直に渡すと最初の & で切れる。
// 壊れる
spawn('cmd.exe', ['/c', cliPath, '--open-url', uri])
Node の spawn が引数を自動で引用するのは スペース・タブ・空文字を含むときだけ。& は引用の条件に入らないので生のまま cmd.exe に届き、コマンド区切りとして解釈される。実測:
ARGS=[--open-url myapp://host/start?a=1]
ERR: 'b' is not recognized as an internal or external command
最悪なのは先頭パラメータだけ届くこと。 アプリは起動するし何かは動く。「一応動いているように見える」ので、消えた2番目以降のパラメータに気付くまで時間がかかる。
直し方
cmd /c "..." の一枚文字列にして値を引用符で包み、windowsVerbatimArguments で Node 側の再クォートを止める。
const line = `""${cliPath}" --open-url "${uri}""`;
spawn('cmd.exe', ['/c', line], { windowsVerbatimArguments: true });
引用符の中では cmd.exe は & を特別扱いしない。実測で URI 全体が保たれることを確認できる。
検証の型
本物のアプリを叩く前に、引数をそのまま表示するだけのバッチで確かめる。
@echo off
echo ARGS=[%*]
壊れている版と直した版を続けて実行し、& 以降が残るかを目で見る。推測でクォートを足して終わりにしない。
罠2: 'spawn' イベントは「プロセスを作った」だけで「届いた」ではない
*.cmd → 本体 exe と辿って「既に起動しているインスタンスへ IPC で渡してから終了する」型の CLI は珍しくない。ここで
await new Promise((res, rej) => { child.once('spawn', res); child.once('error', rej); });
child.unref();
としてすぐ親が終了すると、IPC を渡し終える前に子が死ぬ。ログには「起動しました」と出る。exit code も 0。
実測: ランチャー経由だと受け側のログが一切伸びない(2回とも再現)。同じコマンドを exit まで待つ形で叩くと即座に伸びる。
直し方
'exit' まで待つ。ただし固まった時に無人ジョブを止めないよう上限を付ける。
await new Promise((resolve, reject) => {
let settled = false;
const finish = (fn, v) => { if (!settled) { settled = true; clearTimeout(t); fn(v); } };
const t = setTimeout(() => finish(resolve), 30000);
if (typeof t.unref === 'function') t.unref();
child.once('exit', () => finish(resolve));
child.once('error', (e) => finish(reject, e));
});
見つけ方
同じファイルの中に「exit を待っているヘルパー」と「spawn しか待っていない箇所」が同居していることが多い(前者は事前チェック用、後者が本命)。片方だけ直っている状態を疑う。
罠3: 更新したプラグインは「再読み込みするまで」有効にならない
CLI で取れるバージョンは ディスク上の版であって 実行中の版ではない。プラグイン/拡張の更新は、ホストを再読み込みするまで有効化されない。
素朴にこう書くと事故る:
古ければ入れる → その場で新機能の URI を撃つ
処理するのはまだ旧コード。旧コードが未知のパスを無視せず既定動作に落とす作りだと、意図しない処理が無人で走る(例: リロードのつもりが新規セッション起動)。
さらに悪いこと: ディスク版だけで分岐すると恒久的に詰む
「入れたが再読み込みに失敗した」回のあと、
- 判定 = ディスク版(新) → 新経路を選ぶ
- 実際に動くの = 旧版 → 新経路のリクエストを理解しない → 再読み込みが起きない
- 次回も同じ → 永久に旧版のまま。自力で抜け出せない
再読み込みの失敗は低確率ではない。フォーカスや前面化に依存する実装なら日常的に失敗する(実測で4回中3回)。「稀な後始末」ではなく「通常起きること」として設計する。
直し方: 「入れた」ではなく「有効化を確認した」を状態に持つ
状態ファイルを1つ足す。
<状態ディレクトリ>/<ジョブ名>.ext-activated ← 中身はバージョン文字列だけ
- 再読み込みが実際に成功した瞬間に、その時点のディスク版を書く(=有効化が確定した版)
- 新経路を使う条件を 「ディスク版 ≥ 必要版 かつ 有効化確認済み版 ≥ 必要版」 の2信号にする
- マーカーが無い/読めない/バージョンとして解釈できない場合は 未確認として旧経路へ倒す
- 揃うまでは旧経路で再読み込みを試し続ける。いつか成功した日に有効化が確定し、以後は自動で新経路へ移る
これで詰みが消える。実運用のログでも、導入翌日に INSTALL:、その後4回連続で WAIT-ACTIVATION:(=早まって切り替えない)となり、狙いどおりに粘ることを確認できた。
保険: 新経路の要求は、旧版が受け取っても無害な形にする
旧版が無視できない形のリクエストを送らない。上の例では URI に「何もしない用のフラグ」を1つ足した。新版は該当分岐で即 return するので読まれず無害、旧版に届いた場合も無害な no-op 相当の動作で済み、最悪ケース(無人で本番処理が走る)を塞げる。
判定ロジックは単体で走らせて表にする
分岐が2信号になると机上で追い切れない。判定部分だけを取り出して全ケース実行し、表で確認する。境界(マーカーがディスクより新しい・壊れた値・空)を必ず入れる。
| マーカー | ディスク | 期待 |
|---|---|---|
| 無し | 新 | 旧経路 |
| 旧 | 新 | 旧経路 |
| 新 | 新 | 新経路 |
| 新 | 旧 | 旧経路 |
| 壊れた値 | 新 | 旧経路 |
| 空 | 新 | 旧経路 |
「安全側に倒れる」と書くだけでなく、倒れることを実行して見せる。
end-to-end を実行できない時の誠実な扱い
この種の修正は「本物の再読み込みが、いま作業しているウィンドウを落とす」ため、その場では完走させられないことがある。そのときは
- 実行しなかったことを明記する(「テスト緑」を end-to-end 検証と言い換えない)
- 次の無人実行のログで何がどの順に出れば成功かを先に書いておく
- 失敗しても詰まない設計になっていることを根拠として添える
後日ログを突き合わせれば確定できる。曖昧なまま「完了」と報告しない。
まとめ
| 症状 | 疑う場所 |
|---|---|
| 一部のパラメータだけ効いていない | cmd.exe の & 分断 |
| ログは成功、受け側に何も届かない | 'spawn' で満足して親が先に終了 |
| 更新直後だけ変な処理が走る | 実行中はまだ旧版 |
| ある日から永久に新機能が効かない | ディスク版で分岐して詰んだ |
3つとも stdio: 'ignore' だと完全に無症状になる。疑ったら stdio: ['ignore','pipe','pipe'] で exit まで待って叩き直し、受け側のログに副作用が出るかで判定する。送った側のログは証拠にならない。
よくある質問
+「無人自動化が「成功ログを出して何も起きない」3つの理由(Windows・外部CLI経由でURIを撃つ型)」とは何ですか?
exit 0 を返して実際には何も起きない無人ジョブの3つの構造的原因と回避策。cmd.exe が未引用の & でコマンドを分断して一部パラメータだけ消える/spawn イベントは配送完了ではなく親が先に終了して届かない/更新したプラグインは再読み込みまで旧版が動きディスク版で分岐すると恒久的に詰む。2信号ゲートと判定表、end-to-end を実行できない時の誠実な報告の型まで含む。
+どれくらいトークン(費用)を節約できますか?
ゼロから開発すると約12万トークンかかりますが、この巻物を使えば約4,200トークンで済みます。差し引き約11.6万トークン(API料金換算で約170円)・97%の節約です。
+どうやって使いますか?
無料です。MDファイルを Claude Code などのAIに読み込ませるだけ。ワンライナーをターミナルに貼れば実装が始まります。要件定義や技術調査を省いて実装だけにトークンを使えます。
+どのAIツールに対応していますか?
claude-code、cursor、codex-cli に対応しています。
+商用利用できますか?
ライセンスは「商用利用可 (再販不可)」です。
🤝 自分でAIを動かすのは、まだ不安…という方へ
この巻物の内容を、AIを使うプロに丸ごと任せることもできます。姉妹サービスAI代行堂なら「LINEで頼むだけで、仕事が完成」。
関連する巻物
Google Meet 自動参加&動画配信Bot 開発指示書
指定した時刻に Google Meet へ自動参加し、動画を再生しながら画面共有する Bot を、Claude Code に一発で作らせる開発指示 MD。朝会の定例動画配信・ウェビナーの自動放送に。
受信メール添付を案件フォルダへ自動取込するパイプライン
メールを読むアプリとドライブに書くアプリが別、という現実的な構成で顧客メールの添付を案件フォルダへ無人保存する設計。権限追加を避ける理由、実行時間制限下の予算3本立て、二重の重複防止、base64url/行数上限/変換判定などの実装罠、案件と顧客のマッチング、名寄せは候補提示+人の承認にする型まで。
Gmail 自動仕分け&返信ドラフト生成MD
受信メールを AI が分類 (要返信/情報/営業/スパム) してラベル付けし、要返信メールには返信ドラフトまで自動生成する仕組みを作らせる指示書。DWD (ドメイン全体委任) 設定手順込み。
この巻物、誰かのトークンも救えます
𝕏 で節約レシートをシェア