1仕組みと動作原理
Stop hook は SKILL テキストではなく、ループを駆動する真のエンジンです。プラグインは matcher "*" で Claude Code の Stop イベントを登録し、コマンドはプラグイン内の stop-hook.sh(timeout 30s)を指定します。エージェントがターンを終了しようとするたびにホストがこのスクリプトを実行し、スクリプトは stdout の JSON で完了を許可するか次ラウンドへ引き戻すかを決定します——「止まらない動作」はホスト hook プロトコルによって実現されています。
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/stop-hook.sh",
ループ状態はプロジェクト内の .claude/ralph-marketer-loop.local.md に保存されます。YAML frontmatter が iteration / max_iterations / completion_promise を保持し、スクリプトは grep+sed で読み取って iteration をインクリメントします(BSD の sed -i '' を試行後、GNU sed -i へフォールバック)。状態ファイルが存在しない場合はアクティブなループなしとみなされ、そのまま通過します。
if [ ! -f "$LOOP_STATE_FILE" ]; then
終了条件には3重のセーフティネットがあります:① 直前のアシスタントメッセージ(jq で stdin の .transcript[-1].content を読み取り)が <promise>COMPLETION_PROMISE</promise> に一致 → 状態ファイルを削除して {"decision":"allow"} を返却;② NEXT_ITERATION が max_iterations を超えた場合も同様にファイルを削除して通過;③ iteration フィールドが数値でない場合は状態ファイルの破損と判定し、警告を出力・削除・通過させて、異常な状態によるセッションの永久ロックを防ぎます。
"decision": "allow", "message": "Ralph loop completed successfully!"
終了しない場合、スクリプトは構築した SYSTEM_MSG を {"decision": "block", "message": "$SYSTEM_MSG"} として出力します。ホストはこれを受けて停止を拒否し、message を次ラウンドの入力として注入します。メッセージには第 N ラウンド/全 M ラウンドである旨、「同一タスクが再投入され、前ラウンドの成果はファイルと git 履歴にある」というプロンプト、prd.json と progress.txt のパス、完了シグナルのフォーマットが記載されます。
"decision": "block",
状態と素材は SQLite(./data/content.db)に永続化されます:init.js が mkdirSync(dirname(DB_PATH), { recursive: true }) を実行し、foreign_keys を有効化してから、DISCOVER/LEARN/RESEARCH/IDEATE/WRITE-CRITIQUE-ITERATE-PUBLISH の全プロセスをカバーする14のテーブル(founder_content、competitor_content、trends、research、communications、voice_profile、content_patterns、research_briefs、content_ideas、content_plan、drafts、critique_results、published、agent_log)を作成。状態フィールドには CHECK 制約を用いてパイプラインの状態遷移マシンをスキーマに直接組み込んでいます。
status TEXT DEFAULT 'planned' CHECK(status IN ('planned', 'researching', 'writing', 'critiquing', 'iterating', 'review', 'published', 'cancelled')),
seed.js により、プロジェクトは即座に入力データを持った状態で開始できます:各テーブルを DELETE FROM でクリアした後、サンプルの創業者コンテンツ、1件の voice DNA(トーン/文構造/口癖/回避パターン)、競合のギャップ、トレンド、リサーチ、社内広報、パターンを注入し、最後に agent_log へ 'database_seeded' を記録します——これらはすべてパイプラインを立ち上げるための架空のサンプル(example.com / TechCorp)であり、実際のクライアントデータではありません。
INSERT INTO voice_profile ( profile_name, tone, formality, sentence_patterns, paragraph_style,
タスクの分解は PRD を通じて行われます:templates/prd.json が userStories 配列として12のストーリー(SETUP/PLAN/WRITE/REVIEW/PUBLISH/SOCIAL/NEWSLETTER/METRICS)を提供し、各ストーリーには description、acceptanceCriteria リスト、priority、passes、notes が含まれます。エージェントは各ターンで priority が最小かつ passes:false のタスクを取り出して実行し、完了後に passes:true を書き戻して progress.txt にログを追記します。
"Create draft in drafts table linked to plan", "Draft is at least 800 words", "Includes all key messages from the communication",
ターンを跨ぐ記憶はファイルと git のみに依存します:各ターンは完全に新しいコンテキストウィンドウとなり、記憶は SQLite + prd.json + progress.txt + git コミットによって保持されます。progress.txt のテンプレートはストーリー完了ごとに「日付 / STORY-ID / 文字数 / DB変更 / 成果物ファイル / 学び」を追記することを要求し、Codebase Patterns セクションを再利用可能に維持します。
Each iteration is a fresh context window. Memory persists through files.
4つのスラッシュコマンドが役割を分担し、モデルとツールの権限を階層化しています:ralph-init はディレクトリ作成、${CLAUDE_PLUGIN_ROOT} からのスクリプトとテンプレートのコピー、npm install + npm run db:reset を実行;ralph-marketer は反復プロトコルを定義(model sonnet、allowed-tools に WebFetch を含む);ralph-status は読み取り専用レポート(model haiku、Bash/Read のみ);ralph-cancel は状態ファイルを削除してループを停止します。
rm -f .claude/ralph-marketer-loop.local.md 2>/dev/null && echo "Loop state cleared" || echo "No active loop found"
2コア機能
3外部依存関係
| Kind | Dependency / Endpoint |
|---|---|
| package | better-sqlite3 ^11.0.0(唯一运行时依赖,同步 SQLite 驱动) |
| network | npm registry(安装依赖时拉包) |
| package | prebuild-install / node-gyp 链路(better-sqlite3 安装期原生模块构建,hasInstallScript=true) |
| cli | node |
| cli | npm(install / run db:reset / test) |
| cli | jq(stop-hook.sh 解析宿主传入的 transcript JSON;缺失时静默降级为空串) |
| cli | sed/grep/tr/rm(POSIX 工具链,hook 内解析与改写状态文件) |
| cli | git(每轮 add/commit,兼作记忆与回滚点) |
| network | WebFetch(宿主工具,仅 /ralph-marketer 命令显式授权;插件脚本自身无任何网络调用,竞品/趋势研究抓哪些公开网页由 agent 决定) |
4推奨統制措置と判定根拠
- scripts/: ローカルスクリプトまたはコマンドの実行: hooks/stop-hook.sh(Claude Code の Stop イベント matcher "*"、timeout 30s。エージェントがターンを終了しようとするたびに実行)
- scripts/ 或 SKILL.md: 外部言語パッケージ依存の動的インストールの宣言
- 网络端点: 外部ネットワークアクセスの宣言: 間接的なネットワーク面 1:依存関係インストール時のパッケージ取得——package-lock.json に resolved https://registry.npmjs.org/... の記録あり
- 本地工作目录: ファイル読み書きおよび成果物の出力: data/content.db(SQLite):init.js の const DB_PATH = './data/content.db' と mkdirSync の併用
5二重目の独立検証
- [ok] Stop hook 注册与执行时机 — hooks/hooks.json 仅一条 hook:事件 Stop、matcher "*"、command ${CLAUDE_PLUGIN_ROOT}/hooks/stop-hook.sh、timeout 30;stop-hook.sh 逐行确认接收 stdin JSON、无状态文件即 exit 0、有状态文件才介入。
- [discrepancy] 「循环如何被启动」——第一遍默认成立,第二遍证伪 — 全仓 grep ralph-marketer-loop 只命中 3 处:stop-hook.sh 读取/更新、ralph-cancel.md 删除、.gitignore 忽略。没有任何命令或脚本创建该状态文件(ralph-marketer.md 全文仅描述迭代协议与 $ARGUMENTS,无写入指令)。结论:按仓库代码,/ralph-marketer 执行后若无外部手段创建状态文件,hook 不会拦停、循环不会启动——插件缺『arm loop』这一步,使用前需自行创建 .claude/ralph-marketer-loop.local.md(或依赖未入库的 ralph.sh,见下条)。
- [discrepancy] progress.txt 引用的 scripts/ralph/ralph.sh — templates/progress.txt 第 37 行 'Loop script: `scripts/ralph/ralph.sh`' 与 README 的『Ralph is a Bash loop』叙事互相印证,但 find 全量文件确认仓库内不存在 ralph.sh;实际循环由 Stop hook 实现。该引用是遗留/未入库说明,照模板找脚本会扑空。
- [unlocatable] 脚本内是否有网络调用或凭证读取 — 对 .js/.sh 检索 fetch/axios/http./API_KEY/api_key/process.env/curl/wget/token/secret 零命中——即『未发现任何一处』;但无法据此证明运行期绝对无网络(npm 安装与 agent 的 WebFetch 属外部工具面),故按 unlocatable 记录,并在 security.reason 中以这两条外部面说明档位。
- [discrepancy] jq 依赖未声明,缺失时完成信号静默失效 — stop-hook.sh 用 jq 提取 .transcript[-1].content,但 README/命令文档均未提 jq 依赖,脚本以 '2>/dev/null || echo ""' 静默降级——jq 缺失时 LAST_OUTPUT 恒为空,<promise>COMPLETE</promise> 永不触发,循环只能靠 max_iterations 或 /ralph-cancel 结束。属真实使用陷阱(安全上无害,安全上是『无法自停』的风险)。
- [ok] 状态文件容错与 sed -i 跨平台兼容 — iteration 非数字 → 告警 + rm 状态文件 + exit 0(防坏状态锁死);max_iterations 非数字 → 回退 999999;sed -i 先试 BSD 写法(sed -i '')再回退 GNU 写法;set -e 与 `||` 组合不会误退。
- [ok] 回注内容来源与拼装是否如实 — PROMPT=$(sed -n '/^---$/,/^---$/!p' "$LOOP_STATE_FILE") 取 frontmatter 之后全文;空则 cat scripts/ralph/prompt.md;再空则一行默认文案。最终 cat << EOF 输出 {"decision": "block", "message": "$SYSTEM_MSG"} —— 与 Claude Code Stop hook 协议相符(协议本身由宿主定义,不在本仓可校验范围)。
- [ok] SQLite 表数与 schema 完整性 — init.js 内共 14 个 CREATE TABLE(founder_content、competitor_content、trends、research、communications、voice_profile、content_patterns、research_briefs、content_ideas、content_plan、drafts、critique_results、published、agent_log),与其 console.log 自述一致;content_plan/drafts 有 CHECK 枚举与外键,foreign_keys 已开启,DB 路径相对 cwd。
6結論
525719530027f17d…