Compare commits

..

7 Commits

Author SHA1 Message Date
Tobin South
7c626d26bb Point posthog at consolidated PostHog/ai-plugin repo
Updates source URL from posthog-for-claude to ai-plugin per PostHog's
repo consolidation. Adds SHA pin and fixes homepage to proper docs URL.
2026-03-06 16:08:55 -08:00
Noah Zweben
205b6e0b30 Update webhook closed PR message link to clau.de (#500)
Replace the Google Forms link with the new plugin directory
submission link (https://clau.de/plugin-directory-submission).

https://claude.ai/code/session_01NxRDJKDvFR2d4wC4ppDEDT

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-03 17:08:52 -08:00
Sam Fishman
b7c995dd3c Merge pull request #496 from anthropics/sfishman/ralph-loop-tool-use-fix
fix(ralph-loop): stop hook fails on tool_use blocks; leaks across sessions
2026-03-03 16:35:13 -08:00
sfishman
028eccf544 address review: bound grep to tail -n 100; restore explicit error paths
- Split pipeline into two steps (extract lines, then parse) mirroring the
  original structure.
- set +e around the jq call so failures reach the $? check instead of
  aborting under set -e.
- The "no text content" branch remains removed (that was the original bug —
  all-tool-use turns now correctly yield empty text and the loop continues).
2026-03-04 00:25:42 +00:00
sfishman
8644df9ad5 fix(ralph-loop): isolate loop state to the session that started it
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.
2026-03-02 22:52:00 +00:00
sfishman
adfc379663 fix(ralph-loop): stop hook fails when last assistant block is tool_use
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.
2026-03-02 20:39:50 +00:00
Kenshiro Nakagawa
55b58ec6e5 Merge pull request #457 from anthropics/kenshiro/export-plugins-20260224
chore(skill-creator): update to latest skill-creator
2026-02-24 20:28:20 -08:00
4 changed files with 39 additions and 23 deletions

View File

@@ -626,13 +626,14 @@
},
{
"name": "posthog",
"description": "Connect Claude Code to your PostHog analytics platform. Query insights, manage feature flags, run A/B experiments, track errors, and analyze LLM costs all through natural language. The plugin provides 10 slash commands for common workflows and full access to PostHog's MCP tools. Ask questions like \"What are my top errors?\" or \"Create a feature flag for 50% of users\" and Claude handles the API calls. Supports OAuth authentication, EU and US cloud regions, and self-hosted instances.",
"description": "Access PostHog analytics, feature flags, experiments, error tracking, and insights directly from Claude Code.",
"category": "monitoring",
"source": {
"source": "url",
"url": "https://github.com/PostHog/posthog-for-claude.git"
"url": "https://github.com/PostHog/ai-plugin.git",
"sha": "f2f37954ecef9f1afce4fa81b6a612454a96c410"
},
"homepage": "https://github.com/PostHog/posthog-for-claude.git"
"homepage": "https://posthog.com/docs/model-context-protocol"
},
{
"name": "coderabbit",

View File

@@ -36,7 +36,7 @@ jobs:
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: `Thanks for your interest! This repo only accepts contributions from Anthropic team members. If you'd like to submit a plugin to the marketplace, please submit your plugin [here](https://docs.google.com/forms/d/e/1FAIpQLSdeFthxvjOXUjxg1i3KrOOkEPDJtn71XC-KjmQlxNP63xYydg/viewform).`
body: `Thanks for your interest! This repo only accepts contributions from Anthropic team members. If you'd like to submit a plugin to the marketplace, please submit your plugin [here](https://clau.de/plugin-directory-submission).`
});
await github.rest.pulls.update({

View File

@@ -24,6 +24,16 @@ MAX_ITERATIONS=$(echo "$FRONTMATTER" | grep '^max_iterations:' | sed 's/max_iter
# Extract completion_promise and strip surrounding quotes if present
COMPLETION_PROMISE=$(echo "$FRONTMATTER" | grep '^completion_promise:' | sed 's/completion_promise: *//' | sed 's/^"\(.*\)"$/\1/')
# Session isolation: the state file is project-scoped, but the Stop hook
# fires in every Claude Code session in that project. If another session
# started the loop, this session must not block (or touch the state file).
# Legacy state files without session_id fall through (preserves old behavior).
STATE_SESSION=$(echo "$FRONTMATTER" | grep '^session_id:' | sed 's/session_id: *//' || true)
HOOK_SESSION=$(echo "$HOOK_INPUT" | jq -r '.session_id // ""')
if [[ -n "$STATE_SESSION" ]] && [[ "$STATE_SESSION" != "$HOOK_SESSION" ]]; then
exit 0
fi
# Validate numeric fields before arithmetic operations
if [[ ! "$ITERATION" =~ ^[0-9]+$ ]]; then
echo "⚠️ Ralph loop: State file corrupted" >&2
@@ -77,35 +87,39 @@ if ! grep -q '"role":"assistant"' "$TRANSCRIPT_PATH"; then
exit 0
fi
# Extract last assistant message with explicit error handling
LAST_LINE=$(grep '"role":"assistant"' "$TRANSCRIPT_PATH" | tail -1)
if [[ -z "$LAST_LINE" ]]; then
echo "⚠️ Ralph loop: Failed to extract last assistant message" >&2
# Extract the most recent assistant text block.
#
# Claude Code writes each content block (text/tool_use/thinking) as its own
# JSONL line, all with role=assistant. So slurp the last N assistant lines,
# flatten to text blocks only, and take the last one.
#
# Capped at the last 100 assistant lines to keep jq's slurp input bounded
# for long-running sessions.
LAST_LINES=$(grep '"role":"assistant"' "$TRANSCRIPT_PATH" | tail -n 100)
if [[ -z "$LAST_LINES" ]]; then
echo "⚠️ Ralph loop: Failed to extract assistant messages" >&2
echo " Ralph loop is stopping." >&2
rm "$RALPH_STATE_FILE"
exit 0
fi
# Parse JSON with error handling
LAST_OUTPUT=$(echo "$LAST_LINE" | jq -r '
.message.content |
map(select(.type == "text")) |
map(.text) |
join("\n")
# Parse the recent lines and pull out the final text block.
# `last // ""` yields empty string when no text blocks exist (e.g. a turn
# that is all tool calls). That's fine: empty text means no <promise> tag,
# so the loop simply continues.
# (Briefly disable errexit so a jq failure can be caught by the $? check.)
set +e
LAST_OUTPUT=$(echo "$LAST_LINES" | jq -rs '
map(.message.content[]? | select(.type == "text") | .text) | last // ""
' 2>&1)
JQ_EXIT=$?
set -e
# Check if jq succeeded
if [[ $? -ne 0 ]]; then
if [[ $JQ_EXIT -ne 0 ]]; then
echo "⚠️ Ralph loop: Failed to parse assistant message JSON" >&2
echo " Error: $LAST_OUTPUT" >&2
echo " This may indicate a transcript format issue" >&2
echo " Ralph loop is stopping." >&2
rm "$RALPH_STATE_FILE"
exit 0
fi
if [[ -z "$LAST_OUTPUT" ]]; then
echo "⚠️ Ralph loop: Assistant message contained no text content" >&2
echo " This may indicate a transcript format issue." >&2
echo " Ralph loop is stopping." >&2
rm "$RALPH_STATE_FILE"
exit 0

View File

@@ -141,6 +141,7 @@ cat > .claude/ralph-loop.local.md <<EOF
---
active: true
iteration: 1
session_id: ${CLAUDE_CODE_SESSION_ID:-}
max_iterations: $MAX_ITERATIONS
completion_promise: $COMPLETION_PROMISE_YAML
started_at: "$(date -u +%Y-%m-%dT%H:%M:%SZ)"