ActionD
# ActionD
**Local AI Action Execution Engine for LGH (Local Git Hub)**
[](https://github.com/JoeGlenn1213/ActionD)
[](LICENSE)
[](https://glama.ai/mcp/servers/JoeGlenn1213/ActionD)
[English](README.md) | [็ฎไฝไธญๆ](README_CN.md)
ActionD is a lightweight local CI/CD engine designed for AI agents. It listens to Git events from [LGH](https://github.com/JoeGlenn1213/lgh) and automatically triggers plugins to run code checks, tests, builds, and more.
## Features
- ๐ **Dynamic plugin discovery** โ plugins are discovered automatically via `manifest.json`, no code changes required
- ๐ค **MCP integration** โ a built-in MCP server lets AI assistants query and control CI/CD directly
- ๐ก **Event-driven** โ reacts to `git.push`, `git.tag`, and other LGH events
- ๐ฅ๏ธ **Web console** โ live monitoring dashboard at `http://localhost:3000`
- ๐ **Real-time streaming output** โ live logs over SSE (Server-Sent Events)
- โก **Hot reload** โ reload plugins without a restart
- ๐ **End-to-end workflow** โ `dev_cycle_run` does it all in one call: commit โ CI โ results
- โฎ๏ธ **Rollback** โ automatically roll back to the previous commit on failure
## Terminology
- **Job** โ one CI/CD execution record produced when a plugin reacts to a Git event. Some tool names (`actiond_action_get`, `actiond_actions_list`) carry the legacy word *action*; it refers to the same object as the `actiond_job_*` tools.
- **Plugin** โ the executable unit registered via `manifest.json` that runs on trigger events (`git.push`, `git.tag`); one plugin run yields one job.
- **Profile** โ the CI scope preset (`fast` / `full` / `release`) controlling which plugins run per push.
## Installation
### macOS / Linux (Homebrew)
```bash
brew install JoeGlenn1213/tap/actiond
```
Install LGH the same way (`brew install JoeGlenn1213/tap/lgh`) โ ActionD listens to its git events.
> The builtin plugin bundle (30+ check plugins) is **embedded in the binary**. `actiond setup` materializes it into `~/.localgithub/plugins`, so a Homebrew or release-download install runs the same checks as a source checkout โ no extra asset download needed. The web console is optional: without it the API still runs (see [actiond-web](https://github.com/JoeGlenn1213/actiond-web) or pass `--web-dir`).
### Prerequisites
- Go 1.25+ (only when building from source)
- Python 3.8+ (used by plugins)
- [LGH](https://github.com/JoeGlenn1213/lgh) running locally
### Build from source
```bash
git clone https://github.com/JoeGlenn1213/ActionD.git
cd ActionD
make build
```
> Note: ActionD uses SQLite for storage (the pure-Go modernc.org/sqlite build) โ no CGO is needed and `make build` cross-compiles freely.
## Quick Start
### 1. One-command setup (recommended)
If this is your first run, use `setup` to install the builtin plugin bundle (embedded in the binary), check dependencies, create the directory layout, and verify the environment:
```bash
actiond setup
```
Re-running setup is safe: already-installed plugins and your local edits are never overwritten (use `--force` to reset them to the bundled version, `--skip-plugins` to skip installation entirely).
To also start the daemon in the background right after setup:
```bash
actiond setup --start
```
### 2. Start the service and check it
```bash
# 1. Make sure LGH is running
lgh serve -d
# 2. Start ActionD (daemon mode; reads LGH mappings automatically)
actiond start -d
# 3. Check status
actiond doctor
```
### 3. Open the web console
```bash
open http://localhost:3000
```
### Default directories and auto-detection
- Plugin directories are probed in order:
`<binary dir>/plugins` โ `<working dir>/plugins` โ `~/.localgithub/plugins`
- Web static assets are auto-detected in common locations; the recommended target is publishing `ActionD-Web`'s `out/` to:
`~/.localgithub/actiond-web/out`
- Without an explicit `--repo-root`, ActionD resolves repositories through LGH mappings first, then falls back to current-directory semantics.
## CLI Commands
| Command | Description |
|------|------|
| `actiond setup` | One-command environment initialization (v1.2+) |
| `actiond start` | Start in the foreground |
| `actiond start -d` | Start as a background daemon |
| `actiond stop` | Stop the daemon |
| `actiond restart` | Restart the service (v1.2+) |
| `actiond status` | Show run status + directory info (v1.2+) |
| `actiond plugins restore-go` | Re-enable the Go verification plugins |
| `actiond log` | View server logs |
| `actiond doctor` | Diagnose system dependencies (tiered checks) |
| `actiond version` | Print version information |
| `actiond mcp` | Start the MCP server |
### One-command setup (v1.2+)
Recommended on first install:
```bash
actiond setup
```
This automatically:
- Creates the directory layout (`~/.localgithub/*`)
- Checks dependencies (Git, Python, Go, Node)
- Detects web assets and plugin directories
- Verifies the LGH connection
### Doctor
```bash
actiond doctor
```
Runs 8 categories of tiered checks:
- ๐ฆ System environment (home/base directories)
- ๐ง Dependencies (Git, Python, Go, Node, golangci-lint)
- ๐ Service status (LGH, ActionD)
- ๐ Ports (3000, 8080)
- ๐ Directories (repos, actions, plugins, web, artifacts)
- ๐พ Storage (DB writability, config files)
- ๐ Plugins (directories, core plugin status)
- ๐ Web assets
Results come in three levels:
- **FATAL** โ the system cannot work
- **WARN** โ some functionality is affected
- **INFO** โ informational only
If `doctor` reports that the Go plugins are disabled, restore them directly:
```bash
actiond plugins restore-go
```
### Status (v1.2+)
```bash
actiond status
```
Shows:
- Service run status and PID
- All directory paths and their status
- LGH connection status
- Web assets and plugin directories
## Build Notes
`make build` uses `CGO_ENABLED=0` (SQLite is the pure-Go modernc.org/sqlite build), so binaries cross-compile freely.
`make release` defaults to the current host platform; override `RELEASE_PLATFORMS="linux/amd64 linux/arm64 darwin/arm64"` to build for other targets.
### Start options
```bash
actiond start --help
Flags:
-d, --daemon run in the background
--repo-root string repository root directory (optional; LGH mappings take priority when omitted)
--web-dir string web console static file directory (optional; auto-detected by default)
```
## Dynamic Plugin Discovery (V1.0.7+)
ActionD supports adding new plugins with **zero code**. Just create a `manifest.json` in a plugin directory:
### Plugin directories
ActionD scans plugins in this order:
1. **System plugins**: `./plugins/` (next to the binary)
2. **Development plugins**: `./plugins/` (current working directory)
3. **User plugins**: `~/.localgithub/plugins/`
### manifest.json format
```json
{
"apiVersion": "actiond.dev/v1",
"name": "my-plugin",
"version": "1.0.0",
"description": "My custom plugin",
"command": "python3",
"args": ["run.py"],
"triggers": ["git.push"],
"languages": ["python"],
"timeout": "5m",
"artifacts": ["report.json"]
}
```
### Field reference
| Field | Required | Description |
|------|------|------|
| `name` | โ
| Unique plugin identifier |
| `command` | โ
| Command to execute |
| `args` | - | Command arguments |
| `triggers` | โ
| Trigger events: `git.push`, `git.tag` |
| `languages` | - | Supported languages: `go`, `java`, `python`, `web`, `node`, `typescript`, `javascript`, `nextjs`, `*` |
| `timeout` | - | Timeout: `5m`, `30s` |
| `refFilter` | - | Ref matching: `refs/tags/*` |
### Creating a custom plugin
```
plugins/
โโโ my-plugin/
โโโ manifest.json # plugin metadata
โโโ run.py # execution script
```
**Example run.py**:
```python
#!/usr/bin/env python3
import json
import sys
# Read stdin input
input_data = json.load(sys.stdin)
event = input_data["event"]
repo_path = input_data["repo_path"]
artifact_dir = input_data.get("artifact_dir")
# Do the work...
print(f"Processing {event['type']} for {repo_path}", file=sys.stderr)
# Output the result (stdout)
result = {
"status": "success", # or "error"
"artifacts": ["report.json"]
}
print(json.dumps(result))
```
## Structured Result Protocol โ ActionResult (v1.2+)
Plugins can return a standardized `ActionResult` structure that enables deep AI understanding and downstream decision gating (for example, the Policy Gate plugin reads signals produced by other plugins):
```json
{
"action_id": "act_8a9b2c1d",
"plugin_id": "go-test-fast",
"capability": "test",
"language": "go",
"status": "success",
"decision": "pass",
"timing": {
"started_at": "2025-03-16T10:30:00Z",
"finished_at": "2025-03-16T10:30:02Z",
"duration_ms": 2300
},
"summary": {
"message": "All 25 tests passed",
"counts": {
"tests_run": 25
}
},
"signals": {
"tests_passed": true
},
"hints": [],
"artifacts": [{"name": "test-report.xml", "path": "test-report.xml"}]
}
```
### Result fields
| Field | Type | Description |
|------|------|------|
| `action_id` | string | Unique execution ID |
| `status` | string | `success`, `failed`, `skipped` |
| `decision` | string | `pass`, `deny` (used for gating and AI decisions) |
| `summary.message` | string | One-line summary |
| `signals` | object | **Core extracted features**, e.g. `tests_passed`, `lint_error_count` |
| `hints` | []string | AI/user-friendly fix suggestions |
| `artifacts` | []object | Artifact file list |
### Returning structured results from a plugin
A plugin can print JSON to stdout โ ActionD parses and stores it automatically:
```python
result = {
"status": "failure",
"summary": "Test suite failed",
"hints": ["Run tests locally to reproduce"]
}
print(json.dumps(result))
```
Alternatively, write to `$ARTIFACT_DIR/result.json`.
## Failure Interpreter (v1.2+)
ActionD has built-in failure-pattern recognition that automatically analyzes common errors and suggests fixes:
### Recognized error patterns
| Category | Pattern | Description |
|------|------|------|
| **Dependencies** | `npm_install_failed` | npm install failure |
| | `npm_lockfile_mismatch` | package-lock.json out of sync |
| | `npm_module_not_found` | module not found |
| | `go_mod_tidy` | go.mod needs tidying |
| | `python_module_not_found` | missing Python module |
| | `maven_build_failed` | Maven build failure |
| **Build** | `go_build_failed` | Go compile error |
| | `gradle_build_failed` | Gradle build failure |
| **Tests** | `jest_test_failed` | Jest test failure |
| | `go_test_failed` | Go test failure |
| | `pytest_failed` | pytest failure |
| **Generic** | `permission_denied` | permission error |
| | `timeout` | operation timed out |
| | `out_of_memory` | out of memory |
| | `command_not_found` | command not found |
### Analysis API
Failure analysis is implemented on the Go side in `internal/interpreter` (`failure.go`) and produces the `category`/`type` classification shown above. **There is no `actiond` Python package** โ use one of these real interfaces instead:
- **AI side (recommended)**: the MCP tool `actiond_diagnose(job_id=...)`, which returns root cause and fix suggestions.
- **HTTP side**: the REST API (see the "API endpoints" section below).
### Hot-reloading plugins
```bash
# Option 1: API
curl -X POST http://localhost:3000/api/plugins/reload
# Option 2: MCP
# An AI assistant can call the actiond_plugins_reload tool
```
## Built-in Plugins
| Plugin | Trigger | Language | Description |
|------|--------|------|------|
| `echo` | all | * | Debug plugin, echoes event info |
| `go-lint` | `git.push` | Go | golangci-lint code checks |
| `go-test-fast` | `git.push` | Go | Fast unit tests |
| `go-build` | `git.tag` | Go | Cross-platform builds |
| `java-quicktest` | `git.push` | Java | Smart test selection |
| `java-checkstyle` | `git.push` | Java | Checkstyle code style |
| `python-pytest` | `git.push` | Python | pytest + coverage |
| `web-lint` | `git.push` | Web/Node | Frontend lint checks |
| `web-test` | `git.push` | Web/Node | Frontend test script |
| `web-build` | `git.push` | Web/Node | Frontend build validation |
## MCP Server Integration
ActionD ships with an MCP (Model Context Protocol) server so AI assistants (such as Claude) can query and control CI/CD directly.
### Starting the MCP server
```bash
actiond mcp
```
To let the AI start/stop/restart ActionD itself over MCP, set this before launching:
```bash
ACTIOND_MCP_ALLOW_LIFECYCLE=1 actiond mcp
```
### Available tools
| Tool | Description |
|------|------|
| `actiond_status` | Get server status and statistics |
| `actiond_plugins_list` | List all plugins and their configuration |
| `actiond_actions_list` | List recent CI/CD jobs |
| `actiond_action_get` | Get details of a single job |
| `actiond_plugins_reload` | Hot-reload plugins |
| `actiond_plugins_recommend` | Recommend plugins by project profile (language/framework detection + confidence) |
| `actiond_plugin_enable` | Enable a plugin for the current project |
| `actiond_plugin_disable` | Disable a plugin for the current project |
| `actiond_log` | View server logs, filterable by job_id and plugin_name |
| `actiond_profile_get` | Get the current execution profile (fast/full/release) |
| `actiond_profile_set` | Set the execution profile, controlling which plugins each push triggers |
| `actiond_server_start` | Start the ActionD service (requires the lifecycle switch) |
| `actiond_server_stop` | Stop the ActionD service (protects running jobs by default) |
| `actiond_server_restart` | Restart the ActionD service (protects running jobs by default) |
| `actiond_job_wait` | Block until a job finishes and return its result; supports a timeout parameter |
| `actiond_job_cancel` | Cancel a job (validates state; terminal jobs are rejected) |
| `actiond_job_retry` | Retry a failed job |
| `actiond_diagnose` | **AI failure diagnosis**: root-cause analysis + classification + fix suggestions (the first tool to reach for when CI fails) |
| `actiond_cleanup` | Delete old completed/failed jobs and their artifact directories (retention window or all terminal jobs) |
| `actiond_run_report` | **Goal run report**: what changed / why / verified or not (pass-fail-unknown per job) / how trustworthy / handoff status / rollback evidence โ one honest JSON, unknowns explicit |
| `actiond_handoff_pack` | **Handoff package**: everything the next human or agent needs to continue (envelope + payload + self-contained Markdown) |
| `dev_cycle_run` | **End-to-end dev loop**: commit โ CI โ results (V1.0.8+; tri-state `verification` since v1.3.0) |
> To approve a blocked job, use the CLI `actiond approve <job_id>` or REST `POST /api/actions/{id}/approve` (there is no MCP tool for this).
### The dev_cycle_run end-to-end workflow (V1.0.8+)
`dev_cycle_run` is an aggregate tool that completes the full development loop in a single MCP call:
```
edit code โ lgh up โ wait for CI โ return structured results
```
**Parameters:**
| Parameter | Required | Description |
|------|------|------|
| `message` | โ
| Git commit message |
| `path` | - | Repository path (defaults to the current directory) |
| `timeout` | - | Wait timeout in seconds (default 300 = 5 minutes) |
| `profile` | - | fast/full/release for this run (restored afterwards) |
| `auto_rollback` | - | On a confirmed **failed** verdict, reset to the recorded pre-push commit (default false). Never fires on unverified (`unknown`) runs, and is skipped if HEAD moved or the working tree changed while CI ran |
**Returns:**
```json
{
"success": true,
"verification": "pass",
"commit": "abc123",
"jobs": [
{"id": "job-1", "plugin": "go-test-fast", "status": "done", "duration": "2.3s"}
],
"summary": "โ
ๅ
จ้จ้่ฟ (2 ไธชๆไปถ)"
}
```
`verification` is tri-state and `success` is true **only** on `pass` โ the same vocabulary and aggregation rule as `actiond_run_report`:
- `pass` โ at least one job ran and all of them passed
- `fail` โ at least one job was observed failing
- `unknown` โ zero jobs, a CI query error, or a timeout; never a pass
Jobs are associated to this run by LGH event id, or (fallback) by exact repo name **plus commit sha** โ a green job from an older commit or a similarly named repo is never adopted as verification for the current change.
**Typical usage:**
```
User: AI, please fix the code and test it
AI: [edits the code...]
[calls dev_cycle_run(message="fix: address the failing case")]
Result: โ
All passed (2 plugins)
- go-lint: โ
0.5s
- go-test-fast: โ
2.3s
```
### Available resources
- `actiond://status` โ server status
- `actiond://plugins` โ plugin list
- `actiond://actions` โ execution records
### Connecting your AI client
ActionD speaks standard MCP over stdio, so any MCP-capable client works (Claude Desktop/Code, Cursor, ZCode, Windsurf, ...). For clients that take a stdio command, the server is simply:
```bash
actiond mcp
```
Example for Claude Code โ add to `~/.claude/claude_desktop_config.json`:
```json
{
"mcpServers": {
"actiond": {
"command": "/path/to/actiond",
"args": ["mcp"],
"env": {
"ACTIOND_MCP_ALLOW_LIFECYCLE": "1"
}
}
}
}
```
### AI usage example
```
User: Check the recent CI jobs
AI: [calls actiond_actions_list]
There are 3 recent jobs:
- test-python (python-pytest): โ
success (1.3s)
- ActionD (go-lint): โ disabled
- demo-app (java-quicktest): โ
success (45s)
```
## Configuration
Runtime config file: `~/.localgithub/actions/config.json`
### Disabling a plugin
```json
{
"plugins": {
"java-quicktest": {
"enabled": false
}
}
}
```
The core Go verification chain can also be restored directly via CLI:
```bash
actiond plugins restore-go
```
### Overriding triggers
```json
{
"plugins": {
"go-lint": {
"triggers": ["git.tag"]
}
}
}
```
### Adding a custom plugin (no manifest.json needed)
```json
{
"plugins": {
"my-custom-plugin": {
"enabled": true,
"type": "exec",
"command": "/usr/local/bin/my-script",
"args": ["--verbose"],
"triggers": ["git.push"]
}
}
}
```
## Architecture
```
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
โ LGH โโโโโโถโ ActionD โโโโโโถโ Plugins โ
โ (Events) โ โ (Engine) โ โ (Actions) โ
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโ
โผ โผ โผ
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
โ Web Console โ โ MCP Server โ โ API โ
โ (Dashboard) โ โ(AI-integration)โ โ (RESTful) โ
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
```
## API Endpoints
| Endpoint | Method | Description |
|------|------|------|
| `/api/plugins` | GET | List all plugins |
| `/api/plugins` | POST | Create a custom plugin |
| `/api/plugins/reload` | POST | Hot-reload plugins |
| `/api/plugins/{name}/toggle` | POST | Enable/disable a plugin |
| `/api/actions` | GET | List execution records |
| `/api/actions/{id}` | GET | Get job details |
| `/api/actions/{id}/stream` | GET | SSE live log stream |
| `/api/actions/{id}/artifacts/{file}` | GET | Download an artifact |
| `/api/actions/{id}/cancel` | POST | Cancel a running job (V1.0.8+) |
| `/api/actions/{id}/retry` | POST | Retry a failed job (V1.0.8+) |
| `/api/actions/{id}/approve` | POST | Manually approve a blocked job |
## Layered Logging (v1.2+)
ActionD uses a layered logging architecture that targets different audiences with different formats:
| Layer | Purpose | Example |
|------|------|------|
| `event` | Event log | `๐จ Received: git.push [my-repo]` |
| `dispatch` | Dispatch log | `โ Dispatching to: go-lint` |
| `plugin` | Plugin execution | plugin stdout/stderr output |
| `user` | User summary | `โ
All 3 plugins passed (5.2s)` |
| `ai` | AI structured summary | JSON, consumed by AI |
### AI summary format
```json
{
"timestamp": "2025-03-16T10:30:00Z",
"layer": "ai",
"level": "info",
"job_id": "abc123",
"repo": "my-project",
"plugin": "go-test-fast",
"message": "Tests passed",
"data": {
"status": "success",
"summary": "All 25 tests passed in 2.3s",
"hints": [],
"artifacts": ["test-report.xml"]
}
}
```
## File Locations
| Path | Description |
|------|------|
| `~/.localgithub/actions/` | Data directory |
| `~/.localgithub/actions/actiond.db` | SQLite job database |
| `~/.localgithub/actions/actiond.pid` | Daemon PID file |
| `~/.localgithub/actions/config.json` | User configuration |
| `~/.localgithub/plugins/` | User-defined plugin directory |
| `~/.localgithub/actions/actiond.log` | Daemon log |
## Development
```bash
# Build
go build ./...
# Run tests
go test ./...
# Install to GOPATH
go install ./cmd/actiond
```
## License
MIT License โ see [LICENSE](LICENSE)
## Related Projects
- [LGH](https://github.com/JoeGlenn1213/lgh) โ Local Git Hub
- [actiond-web](https://github.com/JoeGlenn1213/actiond-web) โ Web console UI
TDQS
Scored across 22 tools
Most tools are clearly distinct resource+action pairs (enable/disable plugins, cancel/retry/wait jobs, get/set profiles, list/get actions), and descriptions cross-reference each other to guide selection. The one genuine overlap is actiond_handoff_pack vs actiond_run_report, which aggregate nearly identical sources (git log, CI/CD verdicts, task report) and differ mainly by intent rather than content.
The actiond_ prefix plus noun_verb pattern (actiond_job_cancel, actiond_plugins_list, actiond_profile_set) dominates and is highly predictable. Deviations include verb-only names (actiond_log, actiond_status, actiond_cleanup, actiond_diagnose), inconsistent singular/plural nouns (actiond_plugin_enable vs actiond_plugins_list), and dev_cycle_run, which breaks both the prefix and the word-order convention.
22 tools sits in the heavy range, but the scope genuinely spans server lifecycle, plugin registry management, job lifecycle, execution profiles, diagnostics, cleanup, and reporting. Each tool has a distinct role with little redundancy, so the count feels justified for a full CI/CD management surface rather than bloated.
The surface covers the full CI/CD lifecycle with no dead ends: configure (plugin enable/disable, profile_set), execute (dev_cycle_run), observe (actions_list, action_get, job_wait, status, log), diagnose, recover (retry, cancel), maintain (plugins_reload, server_restart), clean up, and report/handoff. Minor gaps like downloading artifacts or registering plugins via API are by design (filesystem manifests, event-driven triggers), and every workflow has a defined exit path.