The state file lives at .claude/ralph-loop.local.md — project-scoped,
not session-scoped. The plugin's Stop hook fires in every Claude Code
session open in that project directory. So if session A starts a loop,
session B's Stop events also find the state file and block, feeding A's
prompt into B and consuming A's iteration budget.
This was masked by the transcript-parsing bug fixed in the previous
commit: that bug deleted the state file on the first Stop in any
session, so neither session looped. Fixing it exposed the leak.
Fix: setup writes CLAUDE_CODE_SESSION_ID into the frontmatter; the hook
compares against .session_id from its stdin JSON and exits silently on
mismatch. State files without session_id (written by old setup scripts)
fall through to preserve existing behavior.
Claude Code writes each assistant content block (text/tool_use/thinking)
as its own JSONL line. The hook's `grep role:assistant | tail -1` would
grab whichever block happened to be last — often tool_use — then jq's
text filter returned empty string, triggering the 'no text content' path
which deletes the state file and exits without blocking.
Net effect: the loop silently never fires. In one observed session, 62%
of assistant lines were tool_use-only; the hook deleted state on the
very first Stop event every time.
Fix: slurp all assistant lines with jq -rs, flatten to text blocks only,
take the last. Empty result is now non-fatal — no text means no <promise>
tag, so the loop continues. Also absorbs jq parse errors (control chars
in text) via || fallback instead of aborting under set -e.
Skills and commands are now merged, so SKILL.md name fields surface
in the UI as slash commands. Upper Space names like 'Hook Development'
become '/Hook Development', which doesn't work since user-invoked
commands don't support spaces.
Rename all affected SKILL.md name fields to lower-kebab-case:
- hookify: Writing Hookify Rules -> writing-hookify-rules
- plugin-dev: Agent Development -> agent-development
- plugin-dev: Command Development -> command-development
- plugin-dev: Hook Development -> hook-development
- plugin-dev: MCP Integration -> mcp-integration
- plugin-dev: Plugin Settings -> plugin-settings
- plugin-dev: Plugin Structure -> plugin-structure
- plugin-dev: Skill Development -> skill-development
Also update references in plugin-dev/README.md.
Use YAML block scalars (|) for multi-line description fields that contain
<example> blocks with colons and special characters. Without block scalars,
the YAML parser fails because it interprets lines like 'user:' and
'assistant:' as new key-value pairs.
Affected files:
- plugins/plugin-dev/agents/agent-creator.md
- plugins/plugin-dev/agents/skill-reviewer.md
- plugins/plugin-dev/agents/plugin-validator.md
- plugins/pr-review-toolkit/agents/code-simplifier.md
Adds a code simplification agent that:
- Simplifies and refines code for clarity and maintainability
- Preserves all functionality while improving code structure
- Follows project-specific best practices from CLAUDE.md
- Focuses on recently modified code unless instructed otherwise
- Add kotlin-lsp entry to marketplace.json with support for .kt and .kts files
- Create README with installation instructions (brew install kotlin-lsp)
- Configure LSP server to use --stdio mode with 120s startup timeout
- Rename plugin from "ralph-wiggum" to "ralph-loop" to avoid trademark concerns
- Update all internal references to use "Ralph Loop" as the prominent name
- Keep explanatory text noting it "implements the Ralph Wiggum technique" (allowed)
- Rename plugin directory from plugins/ralph-wiggum to plugins/ralph-loop
- Update marketplace.json with new plugin name and source path
- Update plugin-dev documentation references
This change follows legal's recommendation to replace "Wiggum" with "Loop"
in the plugin name while still explaining the technique origin.
Slack thread: https://anthropic.slack.com/archives/C09KU300P7F/p1767741142753959
Co-authored-by: Claude <noreply@anthropic.com>
The allowed-tools pattern was missing :* suffix, causing permission check
failures when arguments were passed to the setup script via ```! block.
Fixesanthropics/claude-code#16398
The hook scripts used absolute imports like `from hookify.core.config_loader`
but when installed, the plugin lives in a cache directory with a hash name,
not in a directory named `hookify/`. Python couldn't resolve the package.
Changed to local imports (`from core.X`) which work because PLUGIN_ROOT
is added to sys.path. Also simplified the sys.path setup.
Fixes: https://github.com/anthropics/claude-code/issues/14267🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Each README now includes:
- Supported file extensions
- Installation commands for different platforms/package managers
- Links to project websites and documentation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This adds 10 LSP plugins to the official marketplace, each with inline
lspServers configuration. This enables the LSP plugin recommendation
feature to detect available LSP plugins directly from the marketplace.
LSP plugins added:
- typescript-lsp: TypeScript/JavaScript (.ts, .tsx, .js, .jsx, etc.)
- pyright-lsp: Python (.py, .pyi)
- gopls-lsp: Go (.go)
- rust-analyzer-lsp: Rust (.rs)
- clangd-lsp: C/C++ (.c, .h, .cpp, .cc, .hpp, etc.)
- php-lsp: PHP (.php)
- swift-lsp: Swift (.swift)
- csharp-lsp: C# (.cs)
- jdtls-lsp: Java (.java)
- lua-lsp: Lua (.lua)
Each plugin uses strict: false since they only provide LSP configuration
(no plugin.json manifest required).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>