ralph-wiggum-marketer
Use this skill when the user asks to "analyze my content", "learn my writing style", "research competitors", "find content angles", "improve my blog", "write like me", "embody my brand voice", or mentions content strategy, voice analysis, competitive research, or iterative content improvement.
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…