skills/forge-admin/SKILL.md
[writes] Manage forge state and configuration: recovery, abort, config edits, session handoff, automations, playbooks, output compression, knowledge graph maintenance. Use when you need to recover from broken pipeline state, edit settings, or manage long-lived state.
npx skillsauth add quantumbitcz/dev-pipeline forge-adminInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Two-level dispatch: top-level <area> (recover, abort, config, handoff, automation, playbooks, refine, compress, graph) and per-area <action> where applicable. No NL fallback — unknown areas print help and exit 2.
See shared/skill-contract.md for the standard table.
Positional, no NL fallback.
Dispatch rules:
$ARGUMENTS.AREA="$1"; shift; ACTION="$1"; shift; REST="$*".$AREA is empty OR matches -*: print usage and exit 2.$AREA == --help or help: print usage and exit 0.$AREA is in {recover, abort, config, handoff, automation, playbooks, refine, compress, graph}: dispatch to ### Subcommand: <AREA> with $ACTION and $REST.Unknown area '<AREA>'. Valid: recover | abort | config | handoff | automation | playbooks | refine | compress | graph. Try /forge-admin --help. and exit 2./forge-admin <area> [<action>] [args]
Areas:
recover <action> State recovery (diagnose | repair | reset | resume | rollback | rewind | list)
abort Stop active pipeline run gracefully
config [<action>] Config editor (wizard | <key=val>)
handoff [<action>] Session handoff (list | show | resume | search | <text>)
automation [<action>] Event-driven triggers (list | add | remove | test)
playbooks [<action>] Reusable recipes (list | run <id> | create | analyze)
refine [<playbook-id>] Apply playbook refinement proposals
compress [<action>] Token compression (agents | output <mode> | status | help)
graph <action> Knowledge graph (init | status | query <cypher> | rebuild | debug)
Flags:
--help Show this message
--dry-run Preview only (where applicable)
--json Structured output (status-like subcommands)
Before any subcommand:
git rev-parse --show-toplevel. If fails: STOP..claude/forge.local.md exists. If absent: report "Forge not initialized. Run /forge first." and STOP. (This skill does NOT auto-bootstrap; bootstrap is a /forge concern.)State diagnostics and repair. Actions: diagnose | repair | reset | resume | rollback | rewind | list.
Default action when none provided: diagnose (read-only, safe default).
Read .forge/state.json, .forge/.lock, recent events; report stuck stage, missing checkpoints, lock holders, score history. No mutations.
Step 0: Delegate state read. Run /forge-ask status --json and parse the JSON
output. Embed the result as a top-level state field in the diagnose
report. Do NOT read .forge/state.json directly — the orchestrator and
/forge-ask status own that path; recovery consumes the parsed snapshot.
Recovery recommendations (the "what to do next" logic) remain /forge-admin recover's responsibility; only the raw state read is delegated.
Reset counters, clear stale .forge/.lock (>24h), normalize state to a known-good shape. Preserves explore-cache, plan-cache, code-graph.db, run-history.db, wiki, learnings.
Clear .forge/state.json and worktree state. Preserves: .forge/explore-cache.json, .forge/plan-cache/, .forge/code-graph.db, .forge/trust.json, .forge/events.jsonl, .forge/playbook-analytics.json, .forge/run-history.db, .forge/playbook-refinements/, .forge/consistency-cache.jsonl, .forge/plans/candidates/, .forge/runs/<id>/handoffs/, .forge/wiki/. Confirms via AskUserQuestion unless --autonomous.
Continue from last checkpoint. Reads .forge/state.json.head_checkpoint, validates checkpoint integrity, dispatches fg-100-orchestrator with resume context.
Roll back worktree commits to last good checkpoint. Confirms via AskUserQuestion (destructive).
Rewind to any prior checkpoint in the DAG (time-travel). Lists candidates if no <checkpoint-id> given.
Flags:
PLAN.-.003) or sha256. Required.Exit codes:
--force to override)Print checkpoint DAG with timestamps, scores, and stage labels.
All recover actions dispatch fg-100-orchestrator with recovery_op set to the action name. See agents/fg-100-orchestrator.md §Recovery op dispatch for routing details. The orchestrator reads the current .forge/state.json, routes to the appropriate recovery strategy (per shared/recovery/recovery-engine.md), and applies the operation atomically via shared/forge-state-write.sh. Rewind and list are backed by hooks/_py/time_travel/ (invoked as python3 -m hooks._py.time_travel; see shared/recovery/time-travel.md).
/forge-admin recover # diagnose (read-only default)
/forge-admin recover diagnose --json # JSON output for scripting
/forge-admin recover repair --dry-run # preview repairs
/forge-admin recover reset # prompts confirmation via AskUserQuestion
/forge-admin recover resume # continue from last checkpoint
/forge-admin recover rollback --target main # revert main branch
/forge-admin recover list # show DAG with HEAD marked
/forge-admin recover list --json # machine-readable
/forge-admin recover rewind --to=PLAN.-.003 # time-travel restore
/forge-admin recover rewind --to=a3f9c1 --force # override dirty worktree guard
Stop active pipeline run gracefully. Writes ABORT marker to state, releases .forge/.lock, preserves checkpoints. Compatible with /forge-admin recover resume.
.forge/state.json exists. If not: "No active pipeline to abort." STOP.story_state from state.json. If COMPLETE or ABORTED: "Pipeline already finished (state: {story_state})." STOP.story_state, convergence.phase, total_iterationsbash shared/forge-state.sh transition user_abort_direct --forge-dir .forge
b. Release .forge/.lock if held: rm -f .forge/.lock
c. Do NOT delete worktree (preserves work for resume)
d. Report: "Pipeline aborted at {stage}. State preserved. Run /forge-admin recover resume to continue."/forge-admin recover resetImportant: Never write directly to state.json. Always use forge-state.sh transition to maintain state machine integrity.
story_state: ABORTEDprevious_state: preserved for /forge-admin recover resume| Condition | Action |
|-----------|--------|
| Prerequisites fail | Report specific error message and STOP |
| state.json missing | Report "No active pipeline to abort." and STOP |
| Pipeline already finished | Report "Pipeline already finished (state: {story_state})." and STOP |
| State transition fails | Report "Could not transition to ABORTED state. State machine error: {error}." Suggest /forge-admin recover repair |
| Lock file removal fails | Log WARNING. Lock file will be detected as stale on next run |
| state.json write fails | Report error. State may be partially updated. Suggest /forge-admin recover repair |
| State corruption | Attempt abort anyway via state machine. If that fails, suggest /forge-admin recover reset |
Interactive config editor. Actions: wizard (full multi-question setup) or <key=val> (single-key edit).
| Command | Action |
|---------|--------|
| /forge-admin config | Show current config summary |
| /forge-admin config set <key> <value> | Set a config value |
| /forge-admin config add <key> <value> | Add to list field (e.g., code_quality) |
| /forge-admin config remove <key> <value> | Remove from list field |
| /forge-admin config validate | Run validation (delegates to /forge-ask status) |
| /forge-admin config show <section> | Show specific section (components, scoring, convergence, caveman) |
| /forge-admin config diff | Show changes since last pipeline run |
| /forge-admin config wizard | Run the full bootstrap wizard |
.claude/forge.local.md or .claude/forge-config.md exists. If neither: "No forge configuration found. Run /forge first (auto-bootstraps if missing .claude/forge.local.md)." STOP.Run the full bootstrap wizard (lifted from old /forge-init). Detects stack via bootstrap-detect.py, asks for overrides, writes .claude/forge.local.md.
.claude/forge.local.md and .claude/forge-config.md$ARGUMENTS (e.g., set components.testing vitest)${CLAUDE_PLUGIN_ROOT}/shared/validate-config.sh with the proposed changecomponents.* → forge.local.mdscoring.*, convergence.*, caveman.* → forge-config.md$ARGUMENTScode_quality)add: append value if not already presentremove: delete value if present, warn if not foundParse <key>=<val>, validate against shared/preflight-constraints.md, write to .claude/forge.local.md. Surfaces validation errors.
Delegates to /forge-ask status (Config validation section). Shows results inline.
forge.local.md and forge-config.md.forge/state.json (if exists).forge/state.json: show "No previous run to compare against"<!-- locked --> fences in forge-config.md cannot be modified. Show: "This value is locked. Remove the <!-- locked --> fence to unlock."set/add/remove operation runs validation before applying| Condition | Action |
|-----------|--------|
| Config file missing | Suggest: "Run /forge first (auto-bootstraps if missing .claude/forge.local.md)" |
| Invalid key path | Show valid keys from config-schema.json |
| Invalid value | Show valid values with fuzzy suggestion |
| Locked section | Refuse edit, explain how to unlock |
Session handoff. Default action (no args, <text> arg) = write. Actions: list | show | resume | search | <text>.
Manage forge session handoffs — structured artefacts that preserve run state for continuation in a fresh Claude Code session.
| Invocation | Behaviour |
|---------------------------------------------------------|---------------------------------------------------------------------------|
| /forge-admin handoff | Write a full-variant manual handoff for the current run |
| /forge-admin handoff list [--run <id>] | List handoff chain for the current or specified run |
| /forge-admin handoff show <path\|latest> | Print a handoff's contents (latest = most recent for current run) |
| /forge-admin handoff resume [<path>] | Structured resume — parses, checks staleness, seeds state, delegates |
| /forge-admin handoff search <query> | FTS5 full-text search across all handoffs in run-history.db |
Write a structured handoff artefact to .forge/runs/<run_id>/handoffs/<timestamp>.md capturing run state, conversation context, and resume instructions.
Writes a full-variant handoff for the current run (if any). In interactive mode, uses AskUserQuestion to confirm slug and variant. In autonomous mode, silently writes.
Calls: python3 -m hooks._py.handoff.cli write --level manual
List handoff artefacts in reverse chronological order.
Calls: python3 -m hooks._py.handoff.cli list [--run <id>]
Display the handoff artefact body. latest picks the most recent handoff for the current run.
Calls: python3 -m hooks._py.handoff.cli show <path|latest>
Pre-fill the current Claude Code session with the handoff context (memory + state restoration).
Structured resume. Parses handoff, checks staleness, seeds state.json, delegates to /forge-admin recover resume <run_id>. With no args, picks the most recent un-SHIPPED handoff.
Calls: python3 -m hooks._py.handoff.cli resume [<path>]
FTS5 search over .forge/runs/*/handoffs/*.md.
Calls: python3 -m hooks._py.handoff.cli search "<query>"
Route the user invocation to the matching action via python3 -m hooks._py.handoff.cli. Surface the CLI's stdout to the user. When resume returns a run_id, delegate to /forge-admin recover resume <run_id> so the orchestrator picks up from the seeded checkpoint.
.forge/runs/<run_id>/handoffs/YYYY-MM-DD-HHMMSS-<level>-<slug>.mdsoft, hard, milestone, terminal, manual/forge-admin recover resetshared/preflight-constraints.md#handoff for defaultsdocs/adr/0012-session-handoff-as-state-projection.md| Condition | Action | |-----------------------------------------------|---------------------------------------------------------------------| | No active forge run | Report "No active run. Nothing to hand off." STOP | | Handoff file missing (show/resume) | CLI exits non-zero; surface "Handoff not found: <path>" and STOP | | Stale handoff (git_head drift, checkpoint gap)| Resumer returns STALE verdict; ask user to confirm or abort | | Rate limit hit (manual writes) | CLI emits "Rate limited — 15min window"; STOP unless terminal level | | Redaction pattern match | Handoff is written with secret redacted inline; no user prompt | | FTS5 index corrupt | Search returns empty result set with stderr warning; STOP |
# Write a handoff now
/forge-admin handoff
# List all handoffs for current run
/forge-admin handoff list
# Resume from a specific handoff
/forge-admin handoff resume .forge/runs/20260421-a3f2/handoffs/2026-04-21-143022-soft-add-health.md
# Resume from latest (auto-pick)
/forge-admin handoff resume
# Find past discussions
/forge-admin handoff search "cache layer decision"
Event-driven trigger management. Actions: list | add | remove | test.
Backed by hooks/automation_trigger.py. Triggers: cron, CI failure, PR event, file change.
Manage event-driven pipeline automations configured in forge-config.md. Full contract: shared/automations.md.
Before any action, verify:
git rev-parse --show-toplevel 2>/dev/null. If fails: report "Not a git repository." and STOP..claude/forge.local.md exists. If not: report "Forge not initialized. Run /forge first (auto-bootstraps if missing .claude/forge.local.md)." and STOP..claude/forge-config.md exists. If not: report "No forge-config.md found. Run /forge first to auto-bootstrap, or /forge-admin config wizard to configure manually." and STOP.Parse $ARGUMENTS for sub-action:
list (default if no argument) — show all configured automationsadd — interactive wizard to create a new automationremove — select and remove an existing automationtest <name> — simulate a trigger for the named automationlog — show recent automation log entriesRead .claude/forge-config.md and parse the automations: YAML array.
If the array is empty or missing: report "No automations configured. Use /forge-admin automation add to create one." and STOP.
Display as a table:
## Configured Automations
| # | Name | Trigger | Action | Filter | Cooldown | Enabled |
|---|------|---------|--------|--------|----------|---------|
| 1 | ci-failure-fix | ci_failure | forge-fix | branch: main | 30m | yes |
| 2 | pr-review | pr_opened | forge-review | base_branch: main | 5m | yes |
| 3 | scheduled-health | cron | codebase-health | cron: 0 3 * * 1 | 1440m | yes |
For each automation, show the filter as a compact key: value summary (multiple filters comma-separated). Show enabled as "yes" or "no" (default "yes" if field is absent).
Use AskUserQuestion for each required field in sequence:
Step 1 — Trigger type:
Ask: "What event should trigger this automation?"
Options:
cron — Run on a schedule (cron expression)ci_failure — Run when CI fails (requires GitHub Actions workflow)pr_opened — Run when a PR is opened (requires GitHub Actions workflow)dependabot_pr — Run when Dependabot opens a PR (requires GitHub Actions workflow)linear_status — Run when a Linear ticket changes status (requires Linear MCP)file_changed — Run when a file matching a pattern is edited (PostToolUse hook)Step 2 — Action:
Ask: "Which forge skill should this automation invoke?"
Options (list the most common, allow free text):
forge-fix — Auto-fix the issueforge-review — Review changed filescodebase-health — Full codebase analysis (read-only)security-audit — Run security scannersforge-run — Full pipelineforge-admin recover diagnose — Read-only diagnosticStep 3 — Filter:
Based on the selected trigger type, ask for the relevant filter fields per shared/automations.md Filter Fields by Trigger table:
| Trigger | Prompt |
|---------|--------|
| cron | "Enter cron expression (5-field, e.g. 0 3 * * 1 for Monday 3 AM):" |
| ci_failure | "Filter to branch (glob, e.g. main, release/*; leave blank for all):" then "Filter to workflow filename (e.g. ci.yml; leave blank for all):" |
| pr_opened | "Filter to base branch (e.g. main; leave blank for all):" then "Exclude authors (comma-separated logins, e.g. dependabot[bot]; leave blank for none):" |
| dependabot_pr | "Filter to base branch (leave blank for all):" then "Dependency type (production or development; leave blank for all):" |
| linear_status | "From status (e.g. In Progress; leave blank for any):" then "To status (e.g. Done):" |
| file_changed | "File glob pattern (e.g. src/**/*.ts):" then "Exclusion glob (leave blank for none):" |
Step 4 — Cooldown:
Ask: "Cooldown in minutes between consecutive firings (minimum 1):"
Default suggestion based on trigger type: cron = 1440, ci_failure = 30, pr_opened = 5, dependabot_pr = 10, linear_status = 15, file_changed = 5.
Step 5 — Name:
Ask: "Automation name (unique identifier, e.g. nightly-health, ci-fix-main):"
Validate: name must be unique among existing automations. If duplicate, ask again.
Step 6 — Confirm:
Display the complete automation definition as YAML and ask: "Add this automation to forge-config.md?"
On confirmation, append the new entry to the automations: array in .claude/forge-config.md. If the automations: key does not exist, create it with the new entry. Preserve all other content in the file.
If the trigger is cron, also register it via CronCreate with the cron expression. Report success.
If the trigger is ci_failure, pr_opened, or dependabot_pr, remind the user: "This trigger requires a GitHub Actions workflow. See shared/automations.md CI Integration section for the workflow template."
AskUserQuestion: "Which automation do you want to remove?" with each automation name as an option.automations: array in .claude/forge-config.md. Preserve all other content.cron, note: "If a cron job was registered for this automation, it remains active until the session ends. Use /schedule to manage active cron jobs."Simulate what would happen if a trigger fires for the named automation, without actually dispatching the skill.
$ARGUMENTS (e.g. /forge-admin automation test ci-failure-fix).forge-config.md. If not found: report "Automation '{name}' not found." and STOP.enabled: false: report "Automation '{name}' is disabled. It would not fire." and STOP..forge/automation-log.jsonl (if it exists) for the most recent entry with automation == name and result in (success, failure). Compute elapsed time since ts.
cooldown_minutes: report "COOLDOWN ACTIVE. {remaining} minutes remaining. Trigger would be dropped with result: skipped_cooldown."cooldown_minutes: report "Cooldown clear.".forge/automation-log.jsonl with result == "success" and no corresponding completion (heuristic: entries within the last 10 minutes without a subsequent entry for the same automation). If >= 3: report "CONCURRENT LIMIT. 3 automations already running. Trigger would queue."forge-run, forge-fix, deep-health, migration), note: "This is a destructive action. Would pause for user confirmation before executing."## Trigger Simulation: {name}
| Check | Result |
|-------|--------|
| Enabled | yes/no |
| Cooldown | clear / active ({remaining}m remaining) |
| Concurrency | ok / queued (3/3 slots in use) |
| Safety gate | none / would require confirmation |
**Would execute:** `{action}` with trigger context from `{trigger}` type.
**Would NOT actually dispatch.** Use the real trigger mechanism to execute.
Read .forge/automation-log.jsonl. If missing or empty: report "No automation log entries. Automations have not fired yet." and STOP.
Parse each line as JSON. Display the most recent 20 entries (newest first):
## Automation Log (last 20 entries)
| Timestamp | Automation | Trigger | Action | Result | Duration |
|-----------|------------|---------|--------|--------|----------|
| 2026-04-12 03:00 | scheduled-health | cron | codebase-health | success | 45.2s |
| 2026-04-12 02:15 | ci-failure-fix | ci_failure | forge-fix | skipped_cooldown | - |
Format duration_ms as seconds with one decimal. Omit duration for skipped entries (show -). Truncate timestamp to minutes.
If entries have error field (non-null), append a section:
### Recent Errors
- **{automation}** ({ts}): {error}
/schedule to manage cron jobs manually."add and remove modify forge-config.md. All other sub-actions are read-only.test sub-action simulates only. Actual dispatch is handled by the automation engine (hooks/automation_trigger.py)..forge/automation-log.jsonl. That file is append-only, written by the automation engine.forge-config.md, only modify the automations: section. Never alter other configuration.Reusable pipeline recipes. Actions: list | run <id> | create | analyze.
Backed by .forge/playbooks/ YAML and .forge/playbook-analytics.json.
List all available playbooks (project-specific and built-in) with their descriptions, parameter details, and usage analytics.
Before any action, verify:
git rev-parse --show-toplevel 2>/dev/null. If fails: report "Not a git repository. Navigate to a project directory." and STOP..claude/forge.local.md exists. If not: report "Forge not initialized. Run /forge first (auto-bootstraps if missing .claude/forge.local.md)." and STOP.playbooks.enabled from forge-config.md. If false: report "Playbooks are disabled. Set playbooks.enabled: true in forge-config.md." and STOP.Discover playbooks:
playbooks.directory from forge-config.md (default: .claude/forge-playbooks). Glob for *.md files in that directory.*.md files in ${CLAUDE_PLUGIN_ROOT}/shared/playbooks/.For each discovered playbook:
name, description, version, parameters, tags, acceptance_criteria.name matches the filename (sans .md). If mismatch, note it as a warning.Load analytics:
.forge/playbook-analytics.json exists.run_count, success_count, avg_score, last_used.Display playbooks grouped by source (project first, then built-in):
## Available Playbooks
### Project Playbooks (.claude/forge-playbooks/)
| Playbook | Description | Params | Runs | Avg Score | Last Used |
|----------|-------------|--------|------|-----------|-----------|
| `{name}` | {description} | {required}/{total} | {run_count} | {avg_score} | {last_used or "never"} |
### Built-In Playbooks
| Playbook | Description | Params | Runs | Avg Score | Last Used |
|----------|-------------|--------|------|-----------|-----------|
| `{name}` | {description} | {required}/{total} | {run_count} | {avg_score} | {last_used or "never"} |
---
### Usage
To run a playbook:
/forge run --playbook={name} param1=value1 param2=value2
To see playbook details:
/forge-admin playbooks {name}
If $ARGUMENTS contains a playbook name, show the detailed view for that specific playbook:
## Playbook: {name}
**Description:** {description}
**Version:** {version}
**Mode:** {mode}
**Tags:** {tags | join:", "}
**Source:** {project or built-in}
### Parameters
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `{name}` | {type} | {yes/no} | {default or "-"} | {description} |
### Acceptance Criteria
1. {ac_1}
2. {ac_2}
...
### Review Focus
- Categories: {focus_categories | join:", "}
- Min score: {min_score}
- Review agents: {review_agents | join:", "}
### Analytics
| Metric | Value |
|--------|-------|
| Total runs | {run_count} |
| Success rate | {success_count}/{run_count} ({pct}%) |
| Average score | {avg_score} |
| Average iterations | {avg_iterations} |
| Average duration | {avg_duration_seconds}s |
| Average cost | ${avg_cost_usd} |
| Last used | {last_used} |
### Common Findings
| Category | Occurrences |
|----------|-------------|
| {category} | {count} |
### Example
/forge run --playbook={name} {example_params}
.claude/forge-playbooks/ or enable built-in playbooks with playbooks.builtin_playbooks: true."| Condition | Action | |-----------|--------| | Prerequisites fail | Report specific error message and STOP | | No playbooks found | Report "No playbooks available" with instructions to create or enable built-ins | | Playbook frontmatter invalid | Skip playbook, log WARNING with filename and parse error | | Analytics file corrupt | Report "Analytics data unavailable (corrupt file)" and list playbooks without stats | | Playbook name mismatch | Log WARNING: "Playbook {filename} has name={name} in frontmatter (should match filename)" | | Requested detail view for nonexistent playbook | Report "Playbook '{name}' not found" and list available playbooks |
Apply playbook refinement proposals from .forge/playbook-refinements/. Optional <playbook-id> filter. Interactive review/apply via AskUserQuestion.
Review and apply improvement proposals generated from pipeline run data. Proposals are evidence-backed suggestions for making playbooks produce better code.
.claude/forge.local.md exists.forge/run-history.db exists.forge/playbook-refinements/ has at least one fileIf prerequisites fail, STOP with guidance:
$ARGUMENTS = optional playbook_id. If omitted, list playbooks with pending proposals.
.forge/playbook-refinements/*.json files.forge/playbook-refinements/{playbook_id}.jsonstatus: ready proposals only## Proposal: {id}
**Type:** {type}
**Target:** {target}
**Confidence:** {confidence} ({agreement})
**Current:** {current_value}
**Proposed:** {proposed_value}
**Evidence:** {evidence}
**Expected Impact:** {impact_estimate}
Options:
.claude/forge-playbooks/{playbook_id}.mdshared/playbooks/{playbook_id}.md.claude/forge-playbooks/ first (project override)scoring_gap / acceptance_gap → append to acceptance_criteria: liststage_focus → modify stages.focus arrayparameter_default → modify parameters[].defaultversion in playbook frontmatter.forge/playbook-refinements/{playbook_id}.json:
status: appliedstatus: rejectedstatus: deferredforge-log.md: [REFINE-APPLIED] {playbook_id} v{old}→v{new}: {proposal_ids}<!-- locked --> fences in playbook files — skip proposals targeting locked sectionspass_threshold, concerns_threshold, or scoring weightsToken-cost compression controls. Actions: agents | output <mode> | status | help.
Single entry point for compression. Replaces /forge-compress (previous agent-only surface), /forge-caveman, and /forge-compression-help.
| Sub-action | Read/Write | Purpose |
|---|---|---|
| agents | writes | Compress agent .md files via terse-rewrite (30–50% reduction) |
| output <mode> | writes | Set output compression. mode ∈ {off, lite, full, ultra}. Writes .forge/caveman-mode |
| status (default) | read-only | Show current agent-compression ratio and output-mode |
| help | read-only | Reference card (flags, modes, token savings by mode, tips) |
agents/ directory present (for agents sub-action).forge/ directory writable (for output sub-action, which persists .forge/caveman-mode)Compress agent .md files via terse rewriting (30-50% reduction). Confirms via AskUserQuestion.
Compress all agents/fg-*.md files via terse rewriting; preserves code blocks, YAML frontmatter, and all technical rules. See shared/output-compression.md and shared/agent-ui.md.
Set runtime output compression. <mode> is off | lite | full | ultra. Writes .forge/caveman-mode.
Write the mode string (off|lite|full|ultra) to .forge/caveman-mode. The runtime reads this at agent dispatch time to select the per-stage compression level.
Print current compression settings (read-only).
Read current agent file sizes and .forge/caveman-mode, report ratios.
Print compression reference card. Emit the reference card inline.
Exit codes per shared/skill-contract.md:
output)/forge-admin compress # default: status
/forge-admin compress output lite # set lite mode
/forge-admin compress output ultra --dry-run # preview ultra without writing
/forge-admin compress agents # compress all agent .md
/forge-admin compress agents --dry-run # preview compression
/forge-admin compress help # reference card
/forge-admin compress status --json # JSON for scripting
| Mode | Token savings | Description | |------|---------------|-------------| | off | 0% | Full verbose output (default) | | lite | ~30% | Strip redundant narration; keep code/data intact | | full | ~55% | Aggressive prose compression; ellipsis-heavy | | ultra | ~75% | Caveman grammar; skeletal output only |
Knowledge-graph operations. Actions: init | status | query <cypher> | rebuild | debug.
Read-only enforcement (AC-S014): the query action MUST reject any Cypher containing CREATE | MERGE | DELETE | SET | REMOVE | DROP (case-insensitive) before sending to Neo4j. Use a regex pre-check; if matched, abort with exit 2 and message "Read-only mode: only MATCH queries permitted. Use /forge-admin graph rebuild for writes."
This sub-area uses positional sub-actions, NOT flags.
Dispatch rules:
$ARGUMENTS.SUB="$1"; shift; REST="$*".$SUB is empty OR matches -* (bare invocation or flags-only): print the usage block and exit 2 (No sub-action provided. Valid: init | status | query | rebuild | debug.).$SUB == --help OR $SUB == help: print usage and exit 0.$SUB is in {init, status, query, rebuild, debug}: dispatch to the matching #### Action: <SUB> section with $REST as its arguments.Unknown sub-action '<SUB>'. Valid: init | status | query | rebuild | debug. Try /forge-admin graph --help. and exit 2.No default sub-action. This is intentional — rebuild is destructive, so a bare /forge-admin graph must not silently rebuild.
init, rebuild)status, debug)Sub-action-specific flags are documented under each sub-action section.
Before any sub-action:
.claude/forge.local.md exists. If not: "Pipeline not initialized. Run /forge first (auto-bootstraps if missing .claude/forge.local.md)." STOP.graph.enabled: true in forge.local.md. If false/absent: "Graph integration is disabled. Set graph.enabled: true to use this feature." STOP.docker info. If fails: "Docker is not available. Cannot run graph operations." STOP.Read graph.neo4j_container_name from .claude/forge.local.md. If not set, default: forge-neo4j. Use the resolved name in ALL docker commands below.
You are the graph initializer. Your job is to start the Neo4j container, import the plugin seed data, and build the project codebase graph. Be idempotent — detect what is already done and skip those steps.
Check that .claude/forge.local.md exists in the project root.
/forge first (auto-bootstraps if missing .claude/forge.local.md)." Abort.Read .claude/forge.local.md and check graph.enabled.
graph.enabled: false or the graph: section is absent: inform the user — "Graph integration is disabled in forge.local.md. Set graph.enabled: true to use this feature." Exit.Check Docker availability: docker info
.forge/state.json integrations: "neo4j": {"available": false}Copy the Docker Compose template to the pipeline working directory:
cp "${CLAUDE_PLUGIN_ROOT}/shared/graph/docker-compose.neo4j.yml" .forge/docker-compose.neo4j.yml
Substitute port variables from config (read graph.neo4j_port and graph.neo4j_bolt_port from forge.local.md, defaulting to 7474 and 7687 respectively). Edit the copied file to replace placeholder values with the resolved ports.
Check if the container is already running:
docker ps --filter "name=forge-neo4j" --format "{{.Names}}"
forge-neo4j appears in output: skip this step — container is already running.docker image inspect neo4j:5-community >/dev/null 2>&1
docker pull neo4j:5-community
docker compose -f .forge/docker-compose.neo4j.yml up -d
Important: The image tag neo4j:5-community uses a major-version floating tag, which always resolves to the latest 5.x release. This is intentional — Neo4j 5.x is backward-compatible within the major version. Do NOT pin to a specific patch version as it would require manual updates.
Poll the health check script until Neo4j is ready, up to 60 seconds:
"${CLAUDE_PLUGIN_ROOT}/shared/graph/neo4j-health.sh"
Run this in a loop (every 3 seconds) until it exits 0 or 60 seconds have elapsed.
docker logs forge-neo4j" Abort.Check for the seed marker node to determine if the seed has already been imported:
echo "MATCH (n:_SeedMarker {id: 'forge-seed-v2'}) RETURN count(n)" | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local --format plain
cat "${CLAUDE_PLUGIN_ROOT}/shared/graph/seed.cypher" | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local
Check .forge/graph/.last-build-sha — if it exists and matches the current git rev-parse HEAD, the graph is already up to date for this commit; skip rebuild and note this to the user.
After container startup, derive project_id:
PROJECT_ID=$(derive_project_id "$PROJECT_ROOT")
Pass to build-project-graph.sh:
./shared/graph/build-project-graph.sh --project-root "$PROJECT_ROOT" --project-id "$PROJECT_ID"
For monorepo with components, iterate each component:
for component in $(read_components "$PROJECT_ROOT"); do
./shared/graph/build-project-graph.sh --project-root "$PROJECT_ROOT" --project-id "$PROJECT_ID" --component "$component"
done
Otherwise, build the project graph:
"${CLAUDE_PLUGIN_ROOT}/shared/graph/build-project-graph.sh" --project-root . | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local
After success, write the current commit SHA to .forge/graph/.last-build-sha.
Create .forge/graph/ directory if it does not exist.
Update .forge/state.json integrations block:
"neo4j": {
"available": true
}
If .forge/state.json does not exist or has no integrations key, create/add the key. Do not overwrite unrelated fields.
Query and display node counts:
echo "MATCH (n) RETURN labels(n)[0] AS label, count(*) AS count ORDER BY count DESC" | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local --format plain
Present a summary:
Graph initialized successfully.
Container: forge-neo4j (running)
Seed: imported
Build SHA: <sha>
Node counts:
ProjectFile 142
ProjectClass 38
ProjectFunction 215
...
Run /forge-admin graph query to explore the graph.
Run /forge-admin graph status for health and coverage details.
Note any steps that were skipped (idempotency).
Key behavior preserved:
.forge/graph/.last-build-sha on success..forge/state.json.integrations.neo4j.available = true.neo4j:5-community if image not present locally.You are the graph status reporter. Your job is to display the current state of the Neo4j knowledge graph: container health, node and relationship counts, last build SHA, and enrichment coverage.
Read-only. Honors --json flag per skill-contract §2.
git rev-parse --show-toplevel 2>/dev/null. If fails: report "Not a git repository. Navigate to a project directory." and STOP./forge-admin graph init first." and STOP.Run the health check script:
"${CLAUDE_PLUGIN_ROOT}/shared/graph/neo4j-health.sh"
Also check the container's running state:
docker ps --filter "name=forge-neo4j" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
If Docker itself is unavailable, report: "Docker is not available. Cannot check graph status."
Show node counts grouped by project:
MATCH (n) WHERE n.project_id IS NOT NULL
RETURN n.project_id, labels(n)[0] AS label, count(n) AS count
ORDER BY n.project_id, label
If Neo4j is healthy, query node counts by label:
echo "MATCH (n) RETURN labels(n)[0] AS label, count(*) AS count ORDER BY count DESC" | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local --format plain
Display all results in a table.
Read .forge/graph/.last-build-sha and display its contents.
git rev-parse HEAD and indicate whether the graph is up to date or stale (HEAD has moved since last build).Read .forge/graph/.enriched-files if it exists.
git ls-files | wc -l).If Neo4j is healthy, query relationship counts:
echo "MATCH ()-[r]->() RETURN type(r) AS type, count(*) AS count ORDER BY count DESC" | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local --format plain
Display all results in a table.
Present a consolidated status summary:
Knowledge Graph Status
Container: HEALTHY (forge-neo4j)
Ports: 7474 (HTTP), 7687 (Bolt)
Last build: abc1234 (up to date)
Node counts:
ProjectFile 142
ProjectClass 38
ProjectFunction 215
ProjectPackage 12
ProjectDependency 27
_SeedMarker 1
Relationship counts:
CONTAINS 180
CALLS 94
IMPORTS 61
DEPENDS_ON 27
Enrichment coverage: 89/142 files (63%)
Run /forge-admin graph init to rebuild if stale.
Run /forge-admin graph query <cypher> to explore.
If Neo4j is unavailable, show what can be determined from local files (last build SHA, enriched files) and suggest running /forge-admin graph init.
You are the graph query executor. Your job is to accept a Cypher query (everything after query on the command line), validate that the graph is available, execute the query, and display formatted results.
Takes the Cypher query as a positional argument. If no argument: prompts the user. Read-only.
Before sending any query to Neo4j, run a regex pre-check on the query string. If the query (case-insensitive) matches any of CREATE | MERGE | DELETE | SET | REMOVE | DROP, abort with exit 2 and the message:
Read-only mode: only MATCH queries permitted. Use `/forge-admin graph rebuild` for writes.
Reference regex (case-insensitive): \b(CREATE|MERGE|DELETE|SET|REMOVE|DROP)\b
git rev-parse --show-toplevel 2>/dev/null. If fails: report "Not a git repository. Navigate to a project directory." and STOP./forge-admin graph init first." and STOP.Run the health check script:
"${CLAUDE_PLUGIN_ROOT}/shared/graph/neo4j-health.sh"
/forge-admin graph init to start the graph." Abort.Inject project_id automatically into all queries:
PROJECT_ID=$(derive_project_id "$PROJECT_ROOT")
User can override by specifying their own :param project_id in the query, or omit project_id for cross-project queries.
Accept the Cypher query from the skill argument (the text following query on the command line).
Store the query in $QUERY.
Run the query against Neo4j (after the read-only pre-check has passed):
echo "$QUERY" | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local --format plain
Capture both stdout and stderr.
Present the raw output from cypher-shell. If the output is empty (no rows returned), show: "Query returned no results."
Also show the query that was executed, so the user can reference or modify it:
Query:
MATCH (n:ProjectClass) RETURN n.name LIMIT 10
Results:
n.name
------
UserService
OrderRepository
PaymentGateway
...
(3 rows)
If the output is large (more than 50 rows), truncate display to 50 rows and note: "Showing first 50 of N rows. Add a LIMIT clause to restrict results."
After displaying results, offer useful next steps based on the query type:
MATCH ... RETURN with no LIMIT: suggest adding LIMIT for large graphs.MATCH (n) RETURN DISTINCT labels(n)./forge-admin graph status to see all available labels and relationship types.You are the graph rebuilder. Your job is to wipe all project-derived nodes from the knowledge graph and rebuild them from the current codebase. The plugin seed graph (framework conventions, patterns, rules) is preserved.
Honors --component <name>, --clear-enrichment, and --dry-run flags. Uses AskUserQuestion for the confirmation step. Destructive — deletes project-scoped nodes (preserves plugin seed).
git rev-parse --is-inside-work-tree. If not: report "Not a git repository." and STOP./forge-admin graph init to start the graph first." and STOP.Run git rev-parse --is-inside-work-tree. If not a git repository: ERROR — "Not a git repository." Abort.
Run the health check script:
"${CLAUDE_PLUGIN_ROOT}/shared/graph/neo4j-health.sh"
/forge-admin graph init to start the graph first." Abort.Inform the user what will happen:
"This will delete all project nodes (ProjectFile, ProjectClass, ProjectFunction, ProjectPackage, ProjectDependency) and rebuild them from the current codebase. The plugin seed graph will not be affected. Bugfix enrichment data (bug_fix_count, last_bug_fix_date) is preserved by default."
Use AskUserQuestion to confirm:
Accept optional --component <name> argument:
--component: rebuild all components for current project--component api: rebuild only the api componentDeletion is always scoped to current project — never touches other projects' nodes.
By default, ProjectFile enrichment properties (bug_fix_count, last_bug_fix_date) are preserved across rebuilds. The deletion step saves enrichment data before deleting, and the rebuild step restores it.
Accept optional --clear-enrichment flag to wipe all enrichment data. Useful when enrichment is stale or after significant codebase restructuring.
Derive the project_id for scoping all queries:
PROJECT_ID=$(git remote get-url origin 2>/dev/null | sed 's|.*github.com[:/]||; s|\.git$||')
# Fallback for repos without a remote:
[ -z "$PROJECT_ID" ] && PROJECT_ID=$(basename "$(git rev-parse --show-toplevel)")
All Cypher queries in this step MUST include n.project_id = '$PROJECT_ID' to avoid affecting other projects sharing the same Neo4j instance.
--clear-enrichment)echo "MATCH (n:ProjectFile {project_id: '$PROJECT_ID'}) WHERE n.bug_fix_count > 0 RETURN n.path AS path, n.bug_fix_count AS count, n.last_bug_fix_date AS date" | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local --format csv > /tmp/forge-enrichment-backup.csv
Delete project-derived nodes scoped to current project only:
echo "MATCH (n) WHERE (n:ProjectFile OR n:ProjectClass OR n:ProjectFunction OR n:ProjectPackage OR n:ProjectDependency) AND n.project_id = '$PROJECT_ID' DETACH DELETE n" | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local
/forge-admin graph init to fully reinitialize.Deleted N nodes, deleted M relationships).Also clear the stale build marker so the next step always runs:
rm -f .forge/graph/.last-build-sha
Re-run the build script and pipe output to Neo4j:
"${CLAUDE_PLUGIN_ROOT}/shared/graph/build-project-graph.sh" --project-root . | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local
build-project-graph.sh is executable and that the project root is correct..forge/graph/.last-build-sha:git rev-parse HEAD > .forge/graph/.last-build-sha
--clear-enrichment)If enrichment data was saved in Step 3a and the backup file is non-empty, restore it:
# Parse the CSV and apply enrichment via MERGE
while IFS=',' read -r path count date; do
[ -z "$path" ] && continue
# Escape single quotes in path to prevent Cypher injection
safe_path=$(printf '%s' "$path" | sed "s/'/''/g")
echo "MATCH (n:ProjectFile {project_id: '$PROJECT_ID', path: '$safe_path'}) SET n.bug_fix_count = $count, n.last_bug_fix_date = '$date';"
done < /tmp/forge-enrichment-backup.csv | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local
rm -f /tmp/forge-enrichment-backup.csvQuery and display the updated node counts:
echo "MATCH (n) RETURN labels(n)[0] AS label, count(*) AS count ORDER BY count DESC" | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local --format plain
Present a summary:
Graph rebuilt successfully.
Deleted: 142 project nodes, 350 relationships
Build SHA: <sha>
Node counts after rebuild:
ProjectFile 138
ProjectClass 41
ProjectFunction 228
ProjectPackage 13
ProjectDependency 27
_SeedMarker 1 (seed preserved)
Run /forge-admin graph status for enrichment coverage details.
Run /forge-admin graph query to explore the graph.
If any step failed partway through, clearly indicate the graph may be in an inconsistent state and suggest running /forge-admin graph init to fully reinitialize.
Targeted diagnostic skill for the Neo4j knowledge graph. Provides structured diagnostic recipes without requiring raw Cypher knowledge.
Read-only. Enforces LIMIT 50 on every query. All queries scoped to project_id.
git rev-parse --show-toplevel 2>/dev/null. If fails: report "Not a git repository. Navigate to a project directory." and STOP.shared/graph/neo4j-health.sh. If unhealthy: report "Neo4j is not available. Run /forge-admin graph init first." and STOP./forge-admin graph init to build the project graph." and STOP.Nodes with no relationships (potential data quality issue):
MATCH (n {project_id: $project_id})
WHERE NOT (n)--()
RETURN labels(n) AS type, count(n) AS count
LIMIT 50
Nodes not updated since the current HEAD:
MATCH (n {project_id: $project_id})
WHERE n.last_updated_sha <> $current_sha
RETURN labels(n)[0] AS type, n.name AS name, n.last_updated_sha AS stale_sha
LIMIT 50
Expected enrichment properties absent on node types:
MATCH (n:Function {project_id: $project_id})
WHERE n.complexity IS NULL OR n.test_coverage IS NULL
RETURN n.name AS function, n.file_path AS file
LIMIT 50
Check for expected relationship types:
MATCH (n {project_id: $project_id})
WHERE NOT (n)-[:DEFINED_IN]->()
RETURN labels(n)[0] AS type, n.name AS name
LIMIT 50
Quick health overview by label:
MATCH (n {project_id: $project_id})
RETURN labels(n)[0] AS label, count(n) AS count
ORDER BY count DESC
LIMIT 50
shared/graph/neo4j-health.sh/forge-admin graph init or Docker troubleshootingproject_id from git remote origin URL/forge-admin graph rebuild for widespread staleness, manual fixes for isolated issuesInherits the error-handling tables from each of the five sub-actions. Consolidated matrix:
| Condition | Action |
|---|---|
| Shared prerequisites fail | Report specific error and STOP |
| Docker image pull fails (init) | "Failed to pull Neo4j image. Check internet + Docker Hub access." STOP |
| Neo4j health timeout (60s) | "Neo4j did not become healthy within 60 seconds. Check docker logs forge-neo4j." STOP |
| Container not running (status/query/rebuild/debug) | "Neo4j not running. Run /forge-admin graph init first." STOP (or show local file data for status) |
| Seed import fails (init) | "Container is running but seed is missing. Retry /forge-admin graph init." |
| Query returns no results (query) | "Query returned no results. Check labels with MATCH (n) RETURN DISTINCT labels(n)." |
| Non-read-only Cypher passed to query | "Read-only mode: only MATCH queries permitted. Use /forge-admin graph rebuild for writes." Exit 2 |
| User cancels rebuild | "Rebuild cancelled. Graph unchanged." STOP |
| Deletion fails mid-rebuild | "Graph may be in partial state. Run /forge-admin graph init to fully reinitialize." STOP |
| Enrichment restore fails | WARNING "Bugfix telemetry will restart from zero." Continue |
| Condition | Action |
|---|---|
| Shared prerequisites fail | Report and STOP |
| Empty area / -* first token | Print usage and exit 2 |
| --help | Print usage and exit 0 |
| Unknown area | Print "Unknown area" and exit 2 |
| Unknown action within area | Print area-specific usage and exit 2 |
| graph query with non-read-only Cypher | Reject with exit 2 (AC-S014) |
| State corruption | recover diagnose reports; recover repair mutates |
/forge — Write-surface entry (run, fix, sprint, review, verify, deploy, commit, migrate, bootstrap, docs, audit)/forge-ask — Read-only queries (status, history, insights, profile, tour, codebase Q&A)development
[writes] Build, fix, deploy, review, or modify code in this project. Universal entry for the forge pipeline. Auto-bootstraps on first run; brainstorms before planning when given a feature description. Use when you want to take any productive action: implementing features, fixing bugs, reviewing branches, deploying, committing, running migrations.
development
[writes] Create, list, show, resume, or search forge session handoffs. Use when context is getting heavy and you want to transfer a forge run or conversation into a fresh Claude Code session, or to resume from a prior handoff artefact. Subcommands - no args (write), list, show, resume, search.
development
[writes] Manage the Neo4j knowledge graph. Subcommands: init, rebuild (writes); status, query <cypher>, debug (read-only). Requires Docker. No default — an explicit subcommand is required. Use when setting up the graph for the first time, rebuilding after major refactors, checking graph health, or running ad-hoc Cypher diagnostics.
testing
[writes] Diagnose or fix pipeline state — read-only diagnose (default), repair counters/locks, reset clearing state while preserving caches, resume from checkpoint, rollback worktree commits, rewind to any prior checkpoint (time-travel), or list the checkpoint DAG. Use when pipeline stuck, failed with state errors, or you need to explore alternate execution paths. Trigger: /forge-recover, diagnose state, repair pipeline, reset state, resume from checkpoint, rollback commits, rewind checkpoint, time travel, list checkpoints