Home / All Skills / Content & Media Creation / ralph-wiggum-marketer
Content & Media Creation · muratcankoylan/ralph-wiggum-marketer

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.

L4 High-Privilege · Graded auth & full auditV1 Commit-PinnedExecute CommandInstall DepsNetwork AccessWrite FileDual-pass Audit
Author muratcankoylanGitHub muratcankoylan/ralph-wiggum-marketer ↗Stars 775License MIT(仓库内无 LICENSE 文件,仅 README 末节 '## License / MIT' 与 package.json、.claude-plugin/plugin.json、marketplace.json 的 "license": "MIT" 声明;GitHub API license=null,与此一致)Commit 4c1f5f3bdf
Control levels describe skill capabilities and suggested control measures; they do not represent quality or safety certifications, nor do they dictate usability. Usage decisions rest entirely with users and administrators. Different agent hosts apply varying constraints; do not assume hosts provide automatic protection. Third-party skill copyrights belong to their original authors. Use at your own risk.

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.

hooks/hooks.json
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/stop-hook.sh",
What is happening here: stop-hook.sh is hooked into every stop action. The intercept point occurs the moment the agent actively tries to conclude a turn, not via a timer; if the state file is absent, the script exits 0 (normal pass-through), and only blocks and reinjects when a state file exists.

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.

hooks/stop-hook.sh
if [ ! -f "$LOOP_STATE_FILE" ]; then
Parsing pipeline: grep -E "^iteration:" | sed 's/iteration: *//' | tr -d ' '; the iteration field is updated in-place each turn. This is also the simplest mechanism for cancelling the loop (simply delete the file).

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.

hooks/stop-hook.sh
"decision": "allow", "message": "Ralph loop completed successfully!"
All three exit points are accompanied by rm -f "$LOOP_STATE_FILE"; when max_iterations is invalid, it falls back to MAX_ITERATIONS=999999, effectively setting no upper limit.

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.

hooks/stop-hook.sh
"decision": "block",
Reinjected payload = fixed header (round + prompt + completion signal) + PROMPT extracted verbatim from after the state file's frontmatter (sed -n '/^---$/,/^---$/!p'); if PROMPT is empty, it falls back to cat scripts/ralph/prompt.md, and then to a single-line default text.

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.

scripts/src/db/init.js
status TEXT DEFAULT 'planned' CHECK(status IN ('planned', 'researching', 'writing', 'critiquing', 'iterating', 'review', 'published', 'cancelled')),
The content_plan.status enum corresponds directly to the README's PLANNED→…→PUBLISHED pipeline; drafts tracks version/critique_passed/voice_match_score/uniqueness_score, and critique_results turns the 7-item quality checklist into boolean columns, making 'iterating to acceptable quality' queryable, resumable, and auditable.

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.

scripts/src/db/seed.js
INSERT INTO voice_profile ( profile_name, tone, formality, sentence_patterns, paragraph_style,
voice_profile.avoid_patterns explicitly lists AI clichés such as "In today's rapidly evolving...", "It's important to note...", "In conclusion...", corresponding 1:1 with the Red Flags list in SKILL.md—turning writing discipline into structured, queryable data.

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.

templates/prd.json
"Create draft in drafts table linked to plan", "Draft is at least 800 words", "Includes all key messages from the communication",
Acceptance criteria are strictly verifiable (minimum word count thresholds, mandatory database storage, mandatory status updates), forming a closed loop with npm test so that 'stories not meeting criteria are not marked true'; summarized in README as Small stories / Explicit criteria / Fast feedback.

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.

README.md
Each iteration is a fresh context window. Memory persists through files.
The template explicitly states 'Ralph appends entries below after each completed story:' for append-only logging; templates/prompt.md Step 5 mandates running git add -A && git commit -m "content: [STORY-ID] - [Story Title]" each turn, making git history a rollback checkpoint.

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.

commands/ralph-cancel.md
rm -f .claude/ralph-marketer-loop.local.md 2>/dev/null && echo "Loop state cleared" || echo "No active loop found"
The stop pathway is straightforward and reliable (the hook allows completion if no state file exists); in contrast, the start pathway has no corresponding file-writing action within the repo—see the discrepancy entry in verification.second_pass.

2Core Capabilities

01Hook-driven autonomous loop writing: agent is intercepted and reprompted upon trying to stop, until it outputs the completion signal or hits limits
02PRD-driven task scheduling and resumption: picks tasks by priority + passes:false, updating passes and progress logs upon completion
03Marketing content pipeline database and state machine: trends/research/company comms inputs → plan/drafts/scoring/publishing/activity logs
04Voice cloning (Voice DNA): extracts tone, sentence patterns, catchphrases, and taboos from existing founder content into a queryable profile
05Seven-phase quality loop and self-critique gate: DISCOVER→LEARN→RESEARCH→IDEATE→WRITE→CRITIQUE→ITERATE, including Founder Test and Red Flags triggering rewrites
06Quality verification test suite: npm test validates database integrity, non-empty drafts, word count alignment with database records, and existence of progress/PRD files
07Asset and progress inspection CLI: db:status for pipeline overview, content/list.js to list available assets and recommend topic priorities, query.js to query tables by command
08One-click scaffolding and kill switch: /ralph-init copies script templates and initializes the database; /ralph-cancel deletes the state file to terminate the loop

3External Dependencies

KindDependency / Endpoint
packagebetter-sqlite3 ^11.0.0(唯一运行时依赖,同步 SQLite 驱动)
networknpm registry(安装依赖时拉包)
packageprebuild-install / node-gyp 链路(better-sqlite3 安装期原生模块构建,hasInstallScript=true)
clinode
clinpm(install / run db:reset / test)
clijq(stop-hook.sh 解析宿主传入的 transcript JSON;缺失时静默降级为空串)
clised/grep/tr/rm(POSIX 工具链,hook 内解析与改写状态文件)
cligit(每轮 add/commit,兼作记忆与回滚点)
networkWebFetch(宿主工具,仅 /ralph-marketer 命令显式授权;插件脚本自身无任何网络调用,竞品/趋势研究抓哪些公开网页由 agent 决定)

4Recommended Governance & Evidence

Suggested Controls for Safe Operation
For Agent Runtime
Graded/per-use authorization, sandbox recommended
For Administrators
Approval workflow, rollback support, full audit
Evidence & Triggers (File Paths & Excerpts)
  • 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
Audit Notes: Risk Assessment: Yellow (standard local automation + predictable network surface), not Green. Rationale and source evidence: (1) Verifiable strong behavioral footprint—hooks/hooks.json hooks stop-hook.sh to every Stop event with matcher "*", modifying in-project state files via rm/sed (stop-hook.sh lines 21-24, 46-51, 57-65), meaning 'executes shell whenever the agent tries to stop'; (2) Extensive local file writes relative to cwd (data/content.db, content/**, scripts/ralph/**, git add -A), with pollution scope determined by the working directory; (3) Loop is unlimited by default (commands/ralph-marketer.md '--max-iterations <n>: Stop after N iterations (default: unlimited)'), with cost caps relying solely on completion signals, posing a resource/cost risk rather than a privilege risk; (4) Predictable but non-zero network surface: dependency installation uses official npm registry, and the loop command explicitly authorizes WebFetch (allowed-tools line), targeting public web pages and official registries with no evidence of disabled TLS verification, anti-scraping bypasses, or targeted scraping—falling short of Orange; (5) No credential/secret reading, no custom LLM/API calls—falling short of Orange's credential tier; (6) All behaviors are traceable in source code with sufficient evidence—falling short of Grey. Note: The missing LICENSE file in the repo represents a compliance gap, but does not affect behavioral tiering.

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

  • Fully auditable loop mechanism: continuous execution logic is concentrated in 101 lines of bash (the only non-md file handling behavior), with exit 0 / allow / block paths, completion signal format, iteration ceilings, and corrupted state cleanup checkable line-by-line without black boxes.
  • State design genuinely supports resume-from-break: 14 tables solidify the seven-phase pipeline (content_plan.status CHECK enum, drafts.version, critique_results boolean list, agent_log), paired with prd.json's passes and progress.txt so any interrupted turn can recover from DB and files.
  • Quality gates go beyond slogans: SKILL.md specifies Founder Test / Red Flags / Draft 1-5 ladder, backed by test.js for machine verification (draft word count matching DB records, mandatory meta description on publish, no orphan drafts), requiring npm test to pass before marking a story complete.
  • Zero credentials, zero custom networking: no API keys, no business process.env reading, no HTTP clients; writing capabilities fully leverage the host agent—users need not provide secrets, avoiding leaks of content to third-party services.
  • Tiered cost and permission levels: status command uses haiku restricted to Bash/Read, init uses a restricted toolset, and only the long-running marketer gets sonnet + WebFetch—reflecting intentional least-privilege scoping.
  • Best for: Ideal for users already running Claude Code who need 'long-horizon, interruptible, auditable' content production: SaaS founders or content leads who feed trends, research, and company comms into SQLite, use a PRD to queue up blog posts, case studies, social content, and newsletters for this round, letting it advance turn by turn overnight for review next morning via progress.txt and git history. Also fits developers seeking to learn Ralph loop engineering—this is a minimal, complete sample integrating 'hook interception + PRD tracking + progress logging + state database', with a directly reusable hook script. Requires accepting that 'model output must be human-reviewed before publishing'.
    Not for: Not suitable for users wanting 'finished output in a single command': it does not self-terminate by default (requires --max-iterations or manual state file creation), lacks an arming step, depends on jq, and has documentation discrepancies with code, requiring some troubleshooting capability. Not suitable for token-cost-sensitive users or unattended marathon runs (no cost ceiling). Not suitable for workflows requiring 'one-click publishing to CMS/social media'—the repo contains no platform API integrations; publishing merely writes to content/published/*.md and updates DB records. Not suitable for clean-freak workflows unwilling to run npm install for native modules or git add -A in the project directory; nor for those expecting 'analyze my Twitter/LinkedIn' to mean automated scraping—that is an expectation placed on the host agent, not an implementation in this repo.
    Install Agent-Ready
    ① Verified Mirror 2026-09-15
    Option A · Direct DownloadDownload ralph-wiggum-marketer.tar.gz
    sha256: 525719530027f17d…
    Option B · Copy Guide for Agent
    install.json
    Agents automatically download and verify mirrors from this JSON guide.
    ② Upstream GitHub
    GitHub Repo ↗
    Pinned to commit 4c1f5f3bdf.