This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is a modular Node.js webhook server for Gogs (Git service) that receives webhooks and executes configurable shell commands based on events. The main server uses zero external dependencies - only built-in Node.js modules.
NEW: The project now includes a TypeScript-based Claude Agent SDK client (client/) for AI-powered issue automation, replacing the previous bash-based implementation.
The codebase follows a clean separation of concerns with five main modules:
index.js (Entry Point)
├── server.js (HTTP Server)
│ └── webhookHandler.js (Event Management)
├── jobQueue.js (Job Queue & Persistence)
│ └── commandExecutor.js (Command Execution)
├── config.js (Configuration)
└── logger.js (Logging)
WebhookServer (src/server.js)
http and crypto modulesPOST /webhook, GET /healthgetHandler() to access WebhookHandler for callback registrationWebhookHandler (src/webhookHandler.js)
handler.on(eventType, callback) - Event-specific callbackshandler.onAny(callback) - Global callbacks for all events(payload, headers) or (eventType, payload, headers) for globalJobQueue (src/jobQueue.js)
queue.json for crash recoveryCommandExecutor (src/commandExecutor.js)
{{variable}} syntax/bin/bash with timeout protection{{repo}}, {{branch}}, {{pusher}}, {{commit}}, {{pr_number}}, {{issue_number}}, {{issue_title}}, etc.COMMAND_TIMEOUTConfig (src/config.js)
.env file synchronously on startupprocess.env (env vars take precedence)get(), getBoolean(), getNumber()Logger (src/logger.js)
info(), error(), webhook()index.js loads config and creates WebhookServerqueue.json (if exists)WEBHOOK_PUSH_ENABLED=true, etc.)npm start # Start production server
npm run dev # Start with auto-reload (--watch flag)
node examples/with-callbacks.js # Run callback example
Testing endpoints:
curl http://localhost:3000/health
curl -X POST http://localhost:3000/webhook \
-H "Content-Type: application/json" \
-H "X-Gogs-Event: push" \
-H "X-Gogs-Delivery: 12345" \
-d '{"ref":"refs/heads/main","repository":{"name":"test-repo","full_name":"user/test-repo"},"pusher":{"username":"testuser"},"commits":[{"id":"abc123","message":"Test commit"}]}'
Configuration is split between two files:
.env)Copy from .env.example and configure:
Server Configuration:
HOST=0.0.0.0 # Network interface (0.0.0.0=all, 127.0.0.1=localhost only)
PORT=3000 # Port number
WEBHOOK_PATH=/webhook # Webhook endpoint path
WEBHOOK_SECRET= # Optional webhook verification secret
Event Enable/Disable:
WEBHOOK_{EVENT_TYPE}_ENABLED=true|false
Supported Event Types: push, pull_request, create, delete, release, issues, issue_comment, global
MCP Server Configuration:
GOGS_MCP_PATH=/data/gogs-mcp/dist/index.js # Path to gogs-mcp server for headless Claude Code
The GOGS_MCP_PATH environment variable configures the location of the gogs-mcp server used by Claude Code in headless mode for issue automation. The handle-issue.sh script dynamically generates the MCP configuration file (.mcp-gogs.json) using this path at runtime, making it environment-specific.
commands.json)Copy from commands.json.example and configure commands for each event type.
Command Structure:
{
"commands": {
"event_type": [
{
"name": "command-identifier",
"description": "Human-readable description",
"type": "shell" | "node",
"command": "path/to/command",
"args": ["arg1", "{{variable}}", "arg3"],
"cwd": "/working/directory" | null,
"timeout": 300000,
"filterBranch": "branch-name" | null,
"filterActions": ["action1", "action2"] | null,
"filterAssignee": "username" | null
}
]
}
}
Command Types:
shell - Execute bash shell command/scriptnode - Execute Node.js script (automatically prepends node command)Per-Command Settings:
name - Unique identifier for the commanddescription - Human-readable descriptiontype - Execution type (shell or node)command - Command or script pathargs - Array of arguments (supports variable substitution)cwd - Working directory (null = project root)timeout - Timeout in millisecondsfilterBranch - Only execute for specific branch (null = all branches)filterActions - Only execute for specific actions (array, optional). Examples: ["assigned", "opened"], ["synchronize"]. If omitted or empty, execute for all actionsfilterAssignee - Only execute when issue/PR is assigned to specific user (string, optional). If omitted, execute regardless of assigneeimport/export syntax ("type": "module" in package.json)server.stop() before exit.env for enable/disable flags, commands.json for command definitionsfilterBranch - Execute only for specific branchesfilterActions - Execute only for specific actions (e.g., ["assigned", "opened"])filterAssignee - Execute only when assigned to specific user (e.g., "claude")The server supports custom callback registration beyond .env command execution:
import { WebhookServer } from './src/server.js';
const server = new WebhookServer({ port: 3000 });
const handler = server.getHandler();
// Event-specific callback
handler.on('push', async (payload, headers) => {
console.log('Push to:', payload.repository.name);
// Custom logic here
});
// Global callback (all events)
handler.onAny(async (eventType, payload, headers) => {
console.log('Event:', eventType);
// Send to analytics, logging, etc.
});
server.start();
See examples/with-callbacks.js for complete example.
Commands in commands.json and legacy .env commands support variable substitution using {{variable}} syntax.
Available variables:
{{event}} - Event type (push, create, delete, etc.){{repo}} - Repository name only{{full_repo}} - User/repository format{{repo_owner}} - Repository owner username{{branch}} - Branch name (extracted from refs){{pusher}} - Username of person who triggered event{{commit}} - Short commit hash (push events){{commit_msg}} - Last commit message (push events){{pr_number}} - Pull request number (PR events){{pr_action}} - PR action (opened, closed, etc.){{tag}} - Tag name (create/delete tag events){{ref_type}} - "branch" or "tag"{{issue_number}} - Issue number (issue events){{issue_title}} - Issue title (issue events){{issue_action}} - Issue action (opened, closed, reopened, etc.){{issue_body}} - Issue description/body (issue events){{issue_assignee}} - Username of person assigned to issue (issue events){{comment_body}} - Comment text (issue_comment events)Variables are extracted in commandExecutor.js via extractVariables() and substituted via substituteVariables().
The webhook server uses a persistent job queue to ensure reliable command execution:
queue.json after every change{
"queue": [
{
"id": 1,
"eventType": "push",
"status": "pending",
"createdAt": "2025-10-28T12:00:00.000Z",
"config": { "command": "...", "enabled": true }
}
],
"jobIdCounter": 5,
"lastSaved": "2025-10-28T12:05:00.000Z"
}
The client/ directory contains a TypeScript-based Claude Agent SDK client that provides AI-powered issue automation:
Location: client/
Key Files:
src/claude-client.ts - Main Claude Agent SDK clientsrc/config.ts - Configuration managementsrc/types.ts - TypeScript type definitionssrc/index.ts - CLI entry pointdist/ - Compiled JavaScript (auto-generated by npm run build)Usage:
cd client
npm install
npm run build
Integration: The TypeScript client is called from scripts/handle-issue-ts.sh, which is configured in commands.json for issues and issue_comment events.
Features:
.envSee client/README.md for detailed documentation.
The client supports flexible MCP server configuration with three priority levels:
~/.config/agent-manager/mcp/{owner}/{repo}.json~/.config/agent-manager/mcp/default.jsonKey Features:
${VAR_NAME} syntaxConfiguration Structure:
{
"mcpServers": {
"gogs": {
"type": "stdio",
"command": "node",
"args": ["/data/gogs-mcp/dist/index.js"],
"env": {}
},
"custom-mcp": {
"type": "stdio",
"command": "node",
"args": ["/path/to/custom-mcp/dist/index.js"],
"env": {
"API_KEY": "${MY_API_KEY}"
}
}
}
}
Security: Configuration files are stored outside repositories in ~/.config/agent-manager/mcp/ with restricted permissions to protect secrets like API keys and JWT tokens.
See examples/MCP-CONFIG.md for detailed configuration guide and examples.
The client automatically discovers available tools from all configured MCP servers at runtime:
How It Works:
ToolDiscovery class connects to each configured MCP servertools/list methodmcp__{server-name}__{tool-name}allowedToolsBenefits:
Built-in Tools (always available):
Read, Write, Edit - File operationsBash, Grep, Glob - Shell operationsTodoWrite, Task - Task managementWebFetch, WebSearch - Web accessNotebookEdit, BashOutput, KillShell - Advanced operationsSkill, SlashCommand - Extension pointsExample: If you configure a custom MCP server named "github" with tools "create_pr" and "list_repos", they will automatically be available as mcp__github__create_pr and mcp__github__list_repos.
Main Server (src/):
.env.example when adding new configuration optionsexport default new ClassName()No formal test suite currently. Test manually using:
npm run dev