This document explains how Claude Code runs automatically in response to Gogs webhook events (issues and comments).
1. Gogs Webhook → HTTP Server (accepts immediately, returns 200)
↓
2. Webhook Handler → Enqueues job (non-blocking)
↓
3. Job Queue → Processes ONE job at a time (FIFO)
↓
4. Command Executor → Runs handle-issue.sh (BLOCKS queue)
↓
5. Claude Code → Runs non-interactively (completes task)
↓
6. Job Complete → Next job starts
src/server.js)http://0.0.0.0:3000/webhooksrc/jobQueue.js)queue.jsonscripts/handle-issue.sh)claude user--print --dangerously-skip-permissions.env:
# Issues Event Configuration
WEBHOOK_ISSUES_ENABLED=true
# Issue Comment Event Configuration
WEBHOOK_ISSUE_COMMENT_ENABLED=true
commands.json:
{
"commands": {
"issues": [
{
"name": "claude-issue-handler",
"description": "Automatically handle issues assigned to Claude",
"type": "shell",
"command": "/home/claude/agent-manager/scripts/handle-issue.sh",
"args": [
"{{repo_owner}}",
"{{repo}}",
"{{pusher}}",
"{{issue_number}}",
"{{issue_title}}",
"{{issue_body}}",
"{{issue_action}}",
"",
"{{issue_assignee}}"
],
"cwd": null,
"timeout": 600000,
"filterBranch": null
}
],
"issue_comment": [
{
"name": "claude-comment-handler",
"description": "Handle issue comments for Claude-assigned issues",
"type": "shell",
"command": "/home/claude/agent-manager/scripts/handle-issue.sh",
"args": [
"{{repo_owner}}",
"{{repo}}",
"{{pusher}}",
"{{issue_number}}",
"{{issue_title}}",
"{{issue_body}}",
"{{issue_action}}",
"{{comment_body}}",
"{{issue_assignee}}"
],
"cwd": null,
"timeout": 600000,
"filterBranch": null
}
]
}
}
By default, Claude Code runs in interactive mode and expects:
When run from a script, Claude would hang waiting for input.
Use these flags to run Claude non-interactively:
claude --print --dangerously-skip-permissions "$PROMPT"
Flags explained:
--print: Non-interactive mode - print output and exit--dangerously-skip-permissions: Skip all permission dialogs (safe in controlled environment)Alternative (safer in some contexts):
claude --print --permission-mode bypassPermissions "$PROMPT"
Multiple webhooks arrive quickly:
Queue processes sequentially:
New webhooks accepted during processing:
The system forces Claude to read all issue comments before starting work. This is achieved through:
Explicit Instructions in Prompt
Gogs MCP Tools Used
mcp__gogs__get_issue - Fetches complete issue detailsmcp__gogs__list_issue_comments - Fetches ALL comments chronologicallyWhy This Matters
When an issue or comment is received, Claude gets this prompt:
IMPORTANT: Issue #42 was opened in repository owner/repo.
=== MANDATORY FIRST STEPS - READ ALL MESSAGES ===
Before you start any work on this issue, you MUST complete these steps IN ORDER:
1. Use the mcp__gogs__get_issue tool to fetch the complete issue details:
- owner: owner
- repo: repo
- number: 42
2. Use the mcp__gogs__list_issue_comments tool to fetch ALL comments:
- owner: owner
- repo: repo
- number: 42
3. Read and understand:
- The complete issue description
- ALL comments in the discussion thread
- The full context
4. ONLY AFTER reading everything above, proceed with your work
=== ISSUE CONTEXT ===
[Issue details here]
=== YOUR TASK ===
[Instructions here]
DO NOT skip reading the comments using the MCP tools. This is critical.
This ensures Claude always has the complete conversation context.
User creates issue in Gogs
claudeGogs sends webhook → POST /webhook
issuesopenedServer accepts webhook → Returns 200 OK (instant)
Job enqueued:
{
"id": 5,
"eventType": "issues",
"status": "pending",
"config": { "command": "handle-issue.sh", ... }
}
Queue starts processing:
handle-issue.shBuilds prompt:
Issue #5 was opened in owner/repo by user.
Issue Title: Add dark mode to settings
Issue Description: [description here]
This issue is assigned to you. Please review and handle it accordingly.
Claude Code runs:
claude --print --dangerously-skip-permissions "$PROMPT"
mcp__gogs__get_issue to fetch issue detailsmcp__gogs__list_issue_comments to fetch ALL commentsmcp__gogs__create_issue_comment to comment on issueJob completes → Next job starts (if any)
./scripts/test-claude-noninteractive.sh
Expected output:
✓ SUCCESS: Claude ran non-interactively
Send multiple webhooks rapidly:
./test-queue-blocking.sh
Monitor queue:
watch -n1 'cat queue.json | jq .queue'
You should see:
status: "running"status: "pending"cat queue.json | jq
watch -n1 'cat queue.json | jq .queue'
# If running with systemd
journalctl -u agent-manager -f -n100
# If running with npm run dev
# Check terminal where server is running
Problem: Claude is waiting for user input
Solution: Ensure you're using --print and --dangerously-skip-permissions flags
Problem: Jobs are being queued but not processing
Solution:
cat queue.json | jq '.queue[] | select(.status=="running")'Problem: Gogs webhook delivery fails with timeout
Solution: This shouldn't happen - webhooks return immediately. Check:
curl http://localhost:3000/healthProblem: Claude runs but doesn't commit anything
Solution:
claude user--dangerously-skip-permissions FlagThis flag bypasses all permission checks. It's safe in this context because:
claude userDo NOT use if:
If you need more control, use permission modes:
claude --print --permission-mode acceptEdits "$PROMPT"
This auto-accepts edits but may still prompt for other actions.
To track Claude's output, modify the script:
# Create logs directory
mkdir -p "$HOME/agent-manager/logs"
# Log file with timestamp
LOG_FILE="$HOME/agent-manager/logs/issue${ISSUE_NUMBER}_$(date +%Y%m%d_%H%M%S).log"
# Run Claude with logging
claude --print --dangerously-skip-permissions "$PROMPT" 2>&1 | tee "$LOG_FILE"
Typical execution times:
Queue handles bursts: