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.
1How It Works & Mechanism
The stop hook is the actual engine of the loop, not the SKILL text: The plugin registers Claude Code's Stop event with matcher "*", pointing the command to stop-hook.sh (timeout 30s) inside the plugin. Every time the agent attempts to conclude a turn, the host executes this script, which outputs JSON via stdout to either permit completion or pull the agent back into the next iteration—the 'non-stop' behavior is implemented via the host hook protocol.
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/stop-hook.sh",
The loop state resides in .claude/ralph-marketer-loop.local.md within the project: YAML frontmatter holds iteration / max_iterations / completion_promise; the script reads and increments iteration using grep+sed (trying BSD sed -i '' first, falling back to GNU sed -i). If the state file does not exist, it is treated as having no active loop and allowed through directly.
if [ ! -f "$LOOP_STATE_FILE" ]; then
Termination conditions have a three-tier fallback: (1) The previous assistant message (read via jq from stdin's .transcript[-1].content) matches <promise>COMPLETION_PROMISE</promise> → deletes the state file and returns {"decision":"allow"}; (2) When NEXT_ITERATION exceeds max_iterations, it likewise deletes the file and allows completion; (3) If the iteration field is non-numeric, the state file is deemed corrupted, triggering a warning, deletion, and pass-through to prevent bad state from permanently locking the session.
"decision": "allow", "message": "Ralph loop completed successfully!"
When not terminating, the script outputs the assembled SYSTEM_MSG as {"decision": "block", "message": "$SYSTEM_MSG"}. Based on this, the host rejects termination and feeds the message in as the next turn's input: the message specifies turn N of M, prompts that 'the same task is fed to you again, with previous turn results in files and git history', lists paths for prd.json and progress.txt, and appends the completion signal format.
"decision": "block",
State and assets are persisted in SQLite (./data/content.db): init.js runs mkdirSync(dirname(DB_PATH), { recursive: true }), enables foreign_keys, and creates 14 tables covering the full DISCOVER/LEARN/RESEARCH/IDEATE/WRITE-CRITIQUE-ITERATE-PUBLISH lifecycle (founder_content, competitor_content, trends, research, communications, voice_profile, content_patterns, research_briefs, content_ideas, content_plan, drafts, critique_results, published, agent_log), with state fields using CHECK constraints to encode the pipeline state machine directly into the schema.
status TEXT DEFAULT 'planned' CHECK(status IN ('planned', 'researching', 'writing', 'critiquing', 'iterating', 'review', 'published', 'cancelled')),
seed.js provides ready-to-use input out of the box: it clears tables with DELETE FROM, then injects sample founder content, 1 voice DNA profile (tone/sentence structure/catchphrases/avoidance patterns), competitor gaps, trends, research, company comms, and patterns, finally writing an agent_log entry 'database_seeded'—all fictional samples (example.com / TechCorp) meant to bootstrap the pipeline rather than represent real client data.
INSERT INTO voice_profile ( profile_name, tone, formality, sentence_patterns, paragraph_style,
Task breakdown follows a PRD: templates/prd.json provides 12 stories across a userStories array (SETUP/PLAN/WRITE/REVIEW/PUBLISH/SOCIAL/NEWSLETTER/METRICS), each containing description, acceptanceCriteria list, priority, passes, and notes; each turn, the agent picks the story with lowest priority and passes:false, executes it, updates passes:true upon completion, and appends a log entry to progress.txt.
"Create draft in drafts table linked to plan", "Draft is at least 800 words", "Includes all key messages from the communication",
Cross-turn memory relies exclusively on files and git: every turn starts with a fresh context window, with memory preserved across SQLite + prd.json + progress.txt + git commits; the progress.txt template mandates appending 'Date / STORY-ID / Word count / DB changes / Output files / Learnings' after each completed story, while keeping the Codebase Patterns section reusable.
Each iteration is a fresh context window. Memory persists through files.
Four slash commands handle distinct duties with tiered model/tool permissions: ralph-init sets up directories, copies scripts and templates from ${CLAUDE_PLUGIN_ROOT}, and runs npm install + npm run db:reset; ralph-marketer defines the iteration protocol (model sonnet, allowed-tools includes WebFetch); ralph-status provides read-only reporting (model haiku, Bash/Read only); ralph-cancel deletes the state file to halt the loop.
rm -f .claude/ralph-marketer-loop.local.md 2>/dev/null && echo "Loop state cleared" || echo "No active loop found"
2Core Capabilities
3External Dependencies
| 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 决定) |
4Recommended Governance & Evidence
- scripts/: Executes local scripts or commands: hooks/stop-hook.sh (Claude Code Stop event matcher "*", timeout 30s; executed whenever the agent attempts to conclude a turn)
- scripts/ 或 SKILL.md: Declares dynamic installation of external language package dependencies
- 网络端点: Declares external network access: Indirect network surface 1: dependency installation fetching packages—package-lock.json records resolved https://registry.npmjs.org/...
- 本地工作目录: File read/write and artifact output: data/content.db (SQLite): init.js uses const DB_PATH = './data/content.db' along with mkdirSync
5Second-Pass Verification
- [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。
6Verdict & Scenarios
525719530027f17d…