AI の返答に出てくる URL に「どのアカウントで開くか」を必ず書かせる Stop hook の作り方
管理画面・課金ページの URL をアカウント名なしで人に渡すと別アカウントで開いて 404 になる。返答の送信直前に止める Stop hook(判定ロジック・免除条件・逃がし弁・テスト・配線)を依存なしの Node.js で作る手順。
約2.1万トークンの節約 (API料金換算で約32円分)。 要件定義・技術調査・試行錯誤ぶんのトークンがまるごと不要になります。※ 出品者申告とレビューに基づく推定値。モデル・タスク内容により変動します。
この巻物について
「AI の返答に出てくる URL に「どのアカウントで開くか」を必ず書かせる Stop hook の作り方」は、AIのしつけカテゴリのAI指示書(MDファイル)です。管理画面・課金ページの URL をアカウント名なしで人に渡すと別アカウントで開いて 404 になる。返答の送信直前に止める Stop hook(判定ロジック・免除条件・逃がし弁・テスト・配線)を依存なしの Node.js で作る手順。この巻物をAIに読み込ませると、ゼロから設計・調査する場合に比べて 約2.1万トークン(API料金換算で約32円)・84%のトークンを節約できます。
- カテゴリ
- AIのしつけ
- 対応AI
- claude-code、cursor、codex-cli
- ライセンス
- 商用利用可 (再販不可)
- 価格
- 無料
- ゼロから開発時
- 約2.5万トークン
- この巻物使用時
- 約4,000トークン
- 節約量
- 約2.1万トークン (約32円)
- 更新日
- 2026-10-08
使い方 (AIに渡す3つの方法)
いちばん簡単なのはワンライナー。Claude Code のターミナルに貼るだけです。
claude "https://makimono-md.vercel.app/api/v1/files/ai-url-stop-hook/raw を読み込んで、この指示書どおりに実装して"
中身
AI の返答に出てくる URL に「どのアカウントで開くか」を必ず書かせる Stop hook の作り方
何を解決するか
AI エージェント(Claude Code など)が管理画面や課金ページの URL を人に渡すとき、「どの Google / GitHub アカウントで開くか」が書かれていないと、受け取った人は既定のアカウントで開いて 404 やログイン画面に飛ばされ、別アカウントのまま操作して事故になる。実例: 課金ページの URL をアカウント名なしで渡し、受け取った人は別アカウントのブラウザで 11 回ログイン画面に戻された。
ルール文を書くだけでは守られない。返答を送る直前に機械で止める(Stop hook)。
完成形
tools/url-account-gate.mjs: 返答本文を判定し、アカウントが要る URL にアカウント名が無ければブロック理由を返す- 既存の Stop hook ランナーに 1 行登録(無ければ単体で Stop hook として登録)
- テスト(node:test)で「止まる文/通る文」を固定
判定ロジック(この順で実装する)
- コードフェンス(
…)内は判定しない。行の境界は保つ(フェンス内を空にするだけ)。 - 逃がし弁: 本文に
[URL-ACCOUNT-OK]があれば通す。 - 全体宣言: 「以下の URL はすべて <メール> で開く」のような行(
すべて|全部|いずれもと<メール> で開く)があれば、本文の全 URL を合格にする。 - 対象 URL だけを抽出する:
- ドメイン一致(サブドメイン含む):
aistudio.google.com,console.cloud.google.com,admin.google.com,mail.google.com,github.com,vercel.com,supabase.com,npmjs.com,x.com,discord.com,dash.cloudflare.com,platform.openai.com,console.anthropic.com,notion.so,slack.com,figma.com,canva.comなど、自社で使う「ログインが要るサービス」を列挙する - パス一致:
/admin/console/dashboard/settings/login/signin/accountを含む URL - 常に対象外:
localhost,127.0.0.1,.local,raw.githubusercontent.com - Google Workspace(docs/drive/sheets/script)は、URL に
authuser=か/a/<自社ドメイン>/が含まれていれば「アカウントが URL に埋め込まれている」とみなして免除
- ドメイン一致(サブドメイン含む):
- 対象 URL ごとに「同じ行か前後 2 行」に次のどれかがあれば合格:
- メールアドレス
で開くまたはアカウントを含み、かつ太字の名前**…**がある(例:(**team@<自社ドメイン>** で開く))- 自社のブラウザ起動ヘルパー呼び出し(例:
open-url-as.ps1 -Account)
- 1 つでも不合格があれば
{ triggered: true, missing: [URL…] }を返す。
ブロック理由の文面(人が直せるように、直し方を含める)
[URL-ACCOUNT] 次の URL に「どのアカウントで開くか」が書かれていません。
形式: <URL>(**<アカウント名>** で開く)。既定アカウントと違うなら「シークレットウィンドウで開く」も併記。
本文の URL が全部同じアカウントなら「以下の URL はすべて **x@y** で開く」の 1 行でよい。例外は [URL-ACCOUNT-OK]。
<URL 1>
<URL 2>
実装の骨子(Node.js ESM・依存なし)
const accountDomains = ['aistudio.google.com', 'console.cloud.google.com', 'github.com', /* … */];
const workspaceDomains = ['docs.google.com', 'drive.google.com', 'sheets.google.com', 'script.google.com'];
const email = /[\w.+-]+@[\w-]+\.[\w.-]+/;
const matchesDomain = (host, d) => host === d || host.endsWith('.' + d);
function needsAccount(raw) {
let url; try { url = new URL(raw); } catch { return false; }
const host = url.hostname.toLowerCase();
if (host === 'localhost' || host === '127.0.0.1' || host.endsWith('.local')) return false;
if (matchesDomain(host, 'raw.githubusercontent.com')) return false;
if (workspaceDomains.some(d => matchesDomain(host, d)))
return !url.searchParams.has('authuser') && !url.pathname.includes('/a/<自社ドメイン>/');
return accountDomains.some(d => matchesDomain(host, d))
|| /\/(?:admin|console|dashboard|settings|login|signin|account)/i.test(url.pathname);
}
export function judge(text) {
const body = text.replace(/```[\s\S]*?```/g, b => b.replace(/[^\r\n]/g, ''));
if (body.includes('[URL-ACCOUNT-OK]')) return { triggered: false, missing: [] };
const lines = body.split(/\r?\n/);
const found = [];
lines.forEach((line, i) => {
for (const m of line.matchAll(/https?:\/\/[^\s<>"'()「」、。]+/g)) {
const url = m[0].replace(/[.,;:!?]+$/, '');
if (needsAccount(url)) found.push({ url, i });
}
});
const global = lines.some(l => /(すべて|全部|いずれも)/.test(l) && /[\w.+-]+@[\w.-]+(\*\*)?\s*で開く/.test(l));
if (global) return { triggered: true, missing: [] };
const ok = i => lines.slice(Math.max(0, i - 2), i + 3).some(l =>
email.test(l) || (/(で開く|アカウント)/.test(l) && /\*\*[^*\n]+\*\*/.test(l)) || /open-url-as\.ps1.*-Account/.test(l));
const missing = [...new Set(found.filter(f => !ok(f.i)).map(f => f.url))];
return { triggered: found.length > 0, missing };
}
Stop hook としての配線
- Claude Code の
settings.json→hooks.Stopにこのスクリプトを登録する。stdin に JSON(transcript_path,stop_hook_active)が来るので、stop_hook_activeが true なら何もしない(ループ防止)。 - 最新の assistant 発話を transcript から取り出して
judge()に渡し、missingがあれば理由を stderr に出して exit 2(ブロック)。 - 既に複数の gate を 1 本のランナーで回している場合は、ランナーの gate 配列に
['url-account-gate', () => …]を 1 行足すだけでよい。
テスト(最低限)
- 課金ページの URL にアカウント名なし → ブロック、
missingにその URL - 同じ文に
(**team@<自社ドメイン>** で開く)→ 通る - 「以下の URL はすべて team@<自社ドメイン> で開く」+ GitHub の URL 複数 → 通る
- 出典リンク(公開ブログ・raw.githubusercontent.com)だけ → 発火しない
docs.google.com/…?authuser=1→ 対象外/authuserなし → ブロック- コードフェンス内の URL → 無視
http://localhost:3000/admin→ 対象外try { … } catchの中で assert を呼ばない(握りつぶして常に pass になる)
併せて作ると効くもの
- 「そのアカウントのブラウザプロファイルで URL を開く」ヘルパー(Chrome の
Local Stateのprofile.info_cache[*].user_nameからメール→プロファイル名を引き、chrome.exe --profile-directory=<名前> <URL>で起動)。手で保守する対応表を持たない。 - 所有アカウントが分からないときは、各プロファイルの
History(SQLite)をコピーして対象ドメインの訪問履歴を引く(last_visit_timeはCAST(... AS TEXT)で読む。人に聞く前に自分で引ける)。
導入後に起きること
- アカウント名のない URL を含む返答は送信前に止まり、AI が自分で書き直す。人への手戻り(別アカウントで開いて 404)が消える。
- 誤検知は
[URL-ACCOUNT-OK]で逃がせるが、逃がし弁を使った返答はレビューで目立つようにしておく。
よくある質問
+「AI の返答に出てくる URL に「どのアカウントで開くか」を必ず書かせる Stop hook の作り方」とは何ですか?
管理画面・課金ページの URL をアカウント名なしで人に渡すと別アカウントで開いて 404 になる。返答の送信直前に止める Stop hook(判定ロジック・免除条件・逃がし弁・テスト・配線)を依存なしの Node.js で作る手順。
+どれくらいトークン(費用)を節約できますか?
ゼロから開発すると約2.5万トークンかかりますが、この巻物を使えば約4,000トークンで済みます。差し引き約2.1万トークン(API料金換算で約32円)・84%の節約です。
+どうやって使いますか?
無料です。MDファイルを Claude Code などのAIに読み込ませるだけ。ワンライナーをターミナルに貼れば実装が始まります。要件定義や技術調査を省いて実装だけにトークンを使えます。
+どのAIツールに対応していますか?
claude-code、cursor、codex-cli に対応しています。
+商用利用できますか?
ライセンスは「商用利用可 (再販不可)」です。
🤝 自分でAIを動かすのは、まだ不安…という方へ
この巻物の内容を、AIを使うプロに丸ごと任せることもできます。姉妹サービスAI代行堂なら「LINEで頼むだけで、仕事が完成」。
関連する巻物
AIっぽくない提案書を作る — ハイブリッド企画書モデル(コードAI組版×画像モデル写真×スライドAI配置参照)
コード生成AIの組版・画像編集モデルの写真合成・スライド生成AIの配置文法を分担させ、経営者の差し戻し3回→0回にした提案書パイプラインの作り方と失敗パターン
AI運用ルールを機械的に守らせる hook 設計 — ルール文が守られない本当の理由
チームでAIエージェントを使うと運用ルールが必ず守られなくなる。真因は「読んでいない」ではなく hook がそのマシンで登録されていない/委譲先が沈黙して壊れていること。禁止=実行前拒否・誘導=依頼時の具体コマンド注入・担保=セッション開始時の自己修復の3層、明示例外の短命トークン、warn→blockの段階昇格、BOM/サンドボックス/timeout など失敗が沈黙する罠と、環境依存で落ちないテストの作り方までを実測ベースでまとめた導入手順。
AIの応答を止める番人hookを1ランナーに統合し、書き直しを最大1回にする(誤爆率をfixtureで先に測る)
Stop hook を9本積んだら Stop の65%が書き直し・最多ゲートの91%が誤爆だった。誤爆測定→否定文除外→1プロセス合流→再試行上限統一→全PC移行→KPIで効果確認までの手順。
この巻物、誰かのトークンも救えます
𝕏 で節約レシートをシェア