Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

Wiggum is a CLI tool and MCP server that generates structured task files for autonomous AI coding workflows. It codifies a dependency-aware orchestration pattern — an orchestrator agent that drives subagents through a dependency-ordered task list until an entire project is implemented, hands-off.

What it does

Given a structured plan definition (TOML), Wiggum produces:

  • Task files (tasks/T{NN}-{slug}.md) — Structured markdown specs with goals, dependencies, implementation guidance, test requirements, preflight commands, and exit criteria
  • Progress tracker (PROGRESS.md) — A phase/task table with status tracking and learnings
  • Orchestrator prompt (orchestrator.prompt.md) — The agent-mode prompt that drives the loop
  • Implementation plan (IMPLEMENTATION_PLAN.md) — Architecture overview for subagent context
  • Agents manifest (AGENTS.md) — Agent role definitions

You can also opt in to:

  • Strict mode ([style] strict = true) — Injects language-specific rule sets beyond the baseline security rules, mirroring the verified-modern toolchain baseline (full pedantic clippy for Rust, golangci-lint v2 for Go, PHPStan level max for PHP, etc.). See Strict Standards.
  • Four output targets — VSCode + Copilot (default), opencode, Claude Code (full support via CLAUDE.md + hooks), and agent-rules for Cursor / Windsurf / GitHub Copilot users. See Targets.

Why it exists

Setting up an AI orchestration loop currently requires hand-authoring all of these artifacts. The structural and mechanical parts — numbering, dependency wiring, progress tables, preflight commands, orchestrator boilerplate — should be generated. The creative parts — what to build, architecture decisions, implementation details — come from the user.

Design principles

  • Agent-agnostic — Wiggum generates artifacts, not agent invocations. Works with any AI coding tool that can read markdown.
  • Scaffold, don’t execute — Wiggum produces plans and task files. Execution is someone else’s job.
  • Language-aware — Ships with profiles for Rust, Go, TypeScript, Python, Java, C#, Kotlin, Swift, Ruby, Elixir, and PHP — providing sensible defaults for build, test, lint, and security audit commands.
  • Security by default — Six OWASP-derived rules are injected into every task and orchestrator prompt automatically. Supply-chain audits run on every task completion. Plans with web-facing surface get an auto-appended security hardening task with verifiable exit criteria. None of this requires configuration.
  • Multi-target — A single plan can emit artifacts for VSCode + Copilot, opencode, Claude Code, and Cursor / Windsurf / GitHub Copilot simultaneously. Each target gets the file format and agent conventions that IDE expects.

Getting Started

This section covers installing Wiggum and creating your first plan.

Prerequisites

  • Rust toolchain (1.85+) if building from source
  • A project idea with a rough architecture in mind

Overview

The typical workflow:

  1. Create a plan — Either interactively with wiggum init or by writing a plan.toml by hand
  2. Generate artifacts — Run wiggum generate plan.toml to produce task files, progress tracker, and orchestrator prompt
  3. Run the loop — Use your preferred AI coding tool with the generated orchestrator prompt to execute tasks sequentially

Continue to Installation to get Wiggum on your system, or jump to Quick Start if you already have it installed.

Installation

From crates.io

cargo install wiggum

From source

git clone https://github.com/greysquirr3l/wiggum.git
cd wiggum
cargo install --path .

Verify installation

wiggum version

Expected output format:

wiggum <version> (<sha|unknown>)

Quick Start

1. Create a plan interactively

wiggum init

This walks you through project setup — name, description, language, architecture style, phases, and tasks — and writes a plan.toml.

2. Or bootstrap from an existing project

If you already have a project directory, Wiggum can detect the language, build system, and structure to create a starter plan:

wiggum bootstrap /path/to/project

3. Validate the plan

wiggum validate plan.toml --lint

This checks that the dependency graph is a valid DAG and runs lint rules to catch common plan quality issues.

4. Preview what will be generated

wiggum generate plan.toml --dry-run --estimate-tokens

5. Generate artifacts

wiggum generate plan.toml

This produces the task files, PROGRESS.md, IMPLEMENTATION_PLAN.md, and the target-specific agent prompts in your project directory.

By default, Wiggum emits VSCode + Copilot prompt files (.vscode/*.prompt.md). Other targets are opt-in via --target or the plan’s [targets] section:

TargetCLI / plan fieldWhat gets emitted
VSCode + Copilot (default)--target vscode.vscode/orchestrator.prompt.md + three siblings
opencode--target opencode.opencode/agents/orchestrator.md (single-file, embedded subagent) + planner + background-auditor + (optional) evaluator + ORCHESTRATOR.md
Claude Code--target claudeCLAUDE.md (project memory) + .claude/settings.json (hooks)
Cursor / Windsurf / GitHub Copilot--target agent-rules.cursorrules + .windsurfrules + .github/copilot-instructions.md

Run wiggum generate plan.toml --target all to emit everything at once. See Targets for the full reference.

6. Run the loop

Open your AI coding tool, load the generated orchestrator prompt as the agent prompt, and let it work through the tasks.

  • VSCode + Copilot: open the project, switch to agent mode, paste .vscode/orchestrator.prompt.md as the user message.
  • opencode: open the project — the orchestrator agent is auto-discovered from .opencode/agents/. Select it from the agent picker (or invoke /orchestrator). Read ORCHESTRATOR.md at the project root for the full workflow reference.
  • Claude Code: open the project — CLAUDE.md is auto-loaded and the PreCompact hook is auto-registered. Run claude in the terminal.
  • Cursor / Windsurf / Copilot: open the project — the IDE reads its corresponding rules file automatically.

Plan Definition

Wiggum plans are defined in TOML files. A plan describes your project metadata, phases, tasks, preflight commands, and orchestrator configuration.

Minimal example

[project]
name = "my-app"
description = "A web API for widget management"
language = "rust"
path = "/path/to/project"

[[phases]]
name = "Foundation"
order = 1

[[phases.tasks]]
slug = "project-setup"
title = "Initialize project structure"
goal = "Set up the repo with build tooling and CI"
depends_on = []

[[phases.tasks]]
slug = "domain-model"
title = "Define domain entities"
goal = "Create the core domain types and traits"
depends_on = ["project-setup"]

Per-task evaluation criteria

You can attach verifiable exit criteria to any task. These are embedded in the generated task file and registered in features.json for the evaluator agent to score:

[[phases.tasks]]
slug = "domain-model"
title = "Define domain entities"
goal = "Create the core domain types and traits"
depends_on = ["project-setup"]
evaluation_criteria = [
    "All domain types implement Serialize/Deserialize",
    "No business logic leaks into the infrastructure layer",
]

Four default criteria are added to every task automatically: build succeeds, all tests pass, linter clean, and implementation matches goal.

Target selection

Wiggum can emit artifacts for one or more AI coding tools at once. Add a [targets] section to your plan to control which tools’ agent files are generated. See Targets for the full reference.

[targets]
vscode      = true   # default when [targets] is absent — GitHub Copilot prompt files
opencode    = false  # .opencode/agents/*.md + ORCHESTRATOR.md
claude      = false  # CLAUDE.md + .claude/settings.json (full Claude Code support)
agent-rules = false  # .cursorrules + .windsurfrules + .github/copilot-instructions.md

When the [targets] section is absent, Wiggum defaults to vscode = true and the other targets false — this matches the pre-[targets] behavior exactly. The CLI flag --target <vscode|opencode|claude|agent-rules|all> overrides the plan.

Sections

Checking plan quality

Before generating, score your plan with wiggum check:

wiggum check plan.toml

This evaluates the plan on five dimensions (granularity, dependency health, coverage, richness, and token budget) and prints actionable suggestions. Plans scoring below 7/10 exit with a non-zero status so they can be caught in CI.

Project Configuration

The [project] section defines your project metadata.

Fields

FieldRequiredDescription
nameYesProject name
descriptionYesBrief description of what you’re building
languageYesProgramming language (see Language Profiles)
pathYesPath to the project directory (output target)
architectureNoArchitecture style hint: hexagonal, layered, modular, flat

Example

[project]
name = "my-api"
description = "A REST API for managing inventory"
language = "rust"
path = "/home/user/projects/my-api"
architecture = "hexagonal"

Language

The language field determines which language profile is used for default build, test, and lint commands, as well as template hints like file patterns and documentation style.

Supported values: rust, go, typescript, python, java, csharp, kotlin, swift, ruby, elixir, php.

Note for .NET projects: use language = "csharp" — this covers all .NET SDK project types (ASP.NET Core, console, class library, etc.) regardless of whether you write C#, F#, or VB.NET.

Architecture

The optional architecture field provides hints to the generated task files about how code should be organized. This influences implementation guidance in the generated artifacts.

Phases and Tasks

Work in a Wiggum plan is organized into phases, each containing one or more tasks. Tasks are dependency-ordered across the entire plan, forming a directed acyclic graph (DAG).

Phases

Phases are logical groupings. They appear in PROGRESS.md as section headers but don’t affect task ordering — dependencies do.

[[phases]]
name = "Foundation"
order = 1

[[phases]]
name = "Core Features"
order = 2

Tasks

Tasks are defined within their parent phase. Each task becomes a T{NN}-{slug}.md file.

[[phases.tasks]]
slug = "api-routes"
title = "Define API route handlers"
goal = "Implement REST endpoints for CRUD operations"
depends_on = ["domain-model", "database-adapter"]

Task fields

FieldRequiredDescription
slugYesURL-safe identifier, used in filenames and dependency references
titleYesHuman-readable task title
goalYesWhat this task should accomplish
kindNoTask archetype (default: feature) — see Task kinds below
depends_onYesArray of task slugs this task depends on (empty array [] for no dependencies)
hintsNoImplementation hints or code snippets
test_hintsNoSuggested tests the subagent should write
must_havesNoHard exit criteria the task must satisfy
gateNoPrecondition the orchestrator must verify before starting this task
evaluation_criteriaNoVerifiable criteria scored by the evaluator agent

Task kinds

The kind field selects a task archetype that influences the generated task file template — the section headings, suggested exit criteria, and approach guidance are all tailored to the kind of work involved.

KindDescription
featureNew functionality: emphasises implementation and tests. Default.
refactorCode quality change: emphasises behavioural equivalence before and after
infrastructureCI, config, tooling, or IaC work
researchExploratory spike: produces a document or recommendation, not code
auditSecurity or quality audit: produces findings, not new behaviour
[[phases.tasks]]
slug  = "api-routes"
title = "Define API route handlers"
kind  = "feature"          # default, can be omitted
goal  = "Implement REST endpoints for CRUD operations"
depends_on = ["domain-model"]

[[phases.tasks]]
slug  = "dependency-audit"
title = "Audit third-party dependencies"
kind  = "audit"
goal  = "Identify outdated or vulnerable packages and produce a remediation report"
depends_on = []

Audit-kind tasks are automatically excluded from the orphan-detection lint rule, since they intentionally produce findings rather than code artefacts.

Task numbering

Tasks are numbered sequentially across all phases: T01, T02, T03, and so on. The ordering follows phase order first, then task order within each phase, but dependencies can cross phase boundaries.

Dependency graph

Wiggum validates that task dependencies form a valid DAG (no cycles). Use wiggum validate plan.toml to check before generating.

Preflight and Orchestrator

Preflight commands

The [preflight] section defines the commands that subagents run to verify their work. Language-specific defaults are provided automatically based on the project language, but you can override them.

[preflight]
build = "cargo build --workspace"
test  = "cargo test --workspace"
lint  = "cargo clippy --workspace -- -D warnings"

If omitted, Wiggum uses the defaults from the selected language profile.

Security audit command

Each language profile includes a default vulnerability audit command that is appended to the preflight chain and added as an exit criterion on every task. For Rust this is cargo audit; for TypeScript, npm audit --audit-level=high; for Python, pip-audit; etc.

Override it per-plan:

[preflight]
audit = "cargo audit --deny warnings"

Disable it by setting an empty string:

[preflight]
audit = ""

See the full list of per-language defaults in Language Profiles.

Orchestrator configuration

The [orchestrator] section configures the generated orchestrator prompt.

[orchestrator]
persona   = "You are a senior Rust software engineer"
strategy  = "standard"
rules = [
    "Never log tokens at any log level",
    "Keep domain crate free of I/O dependencies",
    "Rust edition 2024, stable toolchain",
]

Fields

FieldRequiredDefaultDescription
personaNo"You are a senior software engineer"The subagent persona baked into every task prompt
strategyNostandardExecution strategy: standard (goal → implement → test → preflight), tdd (red → green → refactor → preflight), gsd (must-haves checklist → implement → verify), complete (root-fix end-to-end → tests including failure paths → docs update → preflight)
max_retriesNo2Maximum number of preflight-fail/retry cycles before on_failure is applied
on_failureNopauseAction taken when a task exhausts max_retries: pause, skip, or escalate
modelNo—Recommended model identifier (e.g. "claude-opus-4.7", "gpt-5", "gemini-2.5-pro") for the orchestrator agent itself. Rendered as a header note in orchestrator.prompt.md; Wiggum cannot enforce the picker choice from a prompt file.
subagent_modelNo—Model identifier passed as model: to every runSubagent call the orchestrator dispatches for implementation work. Lets you run the orchestrator on a stronger model (e.g. Opus) while implementation subagents use a cheaper one (e.g. Sonnet or Haiku).
rulesNoProject-specific rules included in each subagent prompt. Appended after the automatic security rules from the language profile.

Failure actions

When a task exhausts its max_retries budget, the orchestrator applies on_failure:

ValueBehaviour
pauseEmit a GATE banner and stop — a human must restart to proceed. Default.
skipMark the task [!] (blocked) and continue to the next available task
escalateEmit a structured failure block with a diagnosis summary into PROGRESS.md, then continue
[orchestrator]
max_retries = 3
on_failure  = "escalate"

Model selection

By default the orchestrator, every implementation subagent, and the evaluator all inherit whatever model is selected in the VS Code Copilot Chat picker at the time you run the prompt. Three optional fields let you pin different models for each role so you can drive orchestration with a stronger reasoning model while implementation runs on something cheaper:

[orchestrator]
model          = "claude-opus-4.7"     # orchestrator agent (recommendation header)
subagent_model = "claude-sonnet-4.5"   # every runSubagent call for implementation

[evaluator]
model          = "claude-sonnet-4.5"   # evaluator agent

How each field is applied depends on the active target:

  • VSCode target (default):
    • [orchestrator] model — rendered as a Recommended model header at the top of orchestrator.prompt.md. Wiggum cannot enforce a model from a prompt file, so this is a reminder to set the picker before you start the loop.
    • [orchestrator] subagent_model — injected into the orchestrator’s instructions as model: "<name>" on every #tool:agent/runSubagent call dispatched for implementation work.
    • [evaluator] model — header note on evaluator.prompt.md plus the model: argument when the orchestrator dispatches the evaluator as a subagent.
  • opencode target:
    • [orchestrator] model — written into the orchestrator agent’s frontmatter (model: field). opencode uses the frontmatter model when the agent runs.
    • [orchestrator] subagent_model — written into the implementer agent’s frontmatter. opencode does not support per-dispatch model: arguments — the subagent’s model is pinned by the agent definition itself.
    • [evaluator] model — written into the evaluator agent’s frontmatter.
  • Claude target:
    • [orchestrator] model, [orchestrator] subagent_model, and [evaluator] model are not currently rendered into CLAUDE.md — Claude Code uses the model selected in its own picker at session start. The fields are preserved on the plan for future use and for consistency with other targets.

Equivalent ChatGPT or Gemini model identifiers work the same way — the string is passed through to runSubagent verbatim, so whatever the picker accepts is valid (e.g. "gpt-5", "gpt-5-mini", "gemini-2.5-pro", "gemini-2.5-flash").

Local / BYOK models

Any model registered in the VS Code Copilot Chat picker is valid — including local runtimes and OpenAI-compatible endpoints added through Manage Models… (Ollama, LM Studio, llama.cpp server, vLLM, Azure OpenAI, etc.). Use the exact label the picker shows, in the form "Model Name (Vendor)":

[orchestrator]
model          = "Claude Opus 4.7 (Anthropic)"
subagent_model = "Qwen 2.5 Coder 32B (Ollama)"

[evaluator]
model = "GLM 4.6 (LM Studio)"

Practical caveats when pinning a local model as the subagent runner:

  • The local server must be running before the orchestrator dispatches a subagent — wiggum does not start it for you.
  • Local models typically have much smaller effective context windows than hosted ones. Keep subagent_model for narrow implementation tasks and leave the orchestrator on a hosted model with a large context, or shrink task scope via wiggum check and per-task hints before running.
  • runSubagent calls the local model through the same VS Code language-model API as hosted providers; tool calling, parallel groups, and the preflight loop work the same way, but availability of advanced features depends on what the local backend implements.

Common pairings:

Use casemodelsubagent_modelEvaluator model
Highest qualityclaude-opus-4.7claude-sonnet-4.5claude-sonnet-4.5
Budget-consciousclaude-sonnet-4.5claude-haiku-4.5claude-haiku-4.5
ChatGPT stackgpt-5gpt-5-minigpt-5-mini
Gemini stackgemini-2.5-progemini-2.5-flashgemini-2.5-flash

All three fields are optional and independent — omit any of them to fall back to the picker-selected model.

complete strategy

Inspired by Gary Tam’s (Y Combinator) execution standard for AI-assisted development: every task must be a finished deliverable, not a partial checkpoint.

Use strategy = "complete" when you want each task treated as a finished deliverable instead of a partial checkpoint. Generated prompts will require:

  • Root-cause fix (not workaround) when in scope
  • Tests for behavior changes, including edge/failure path coverage
  • Documentation updates in the same task
  • Full preflight pass before task completion

The completion contract is baked into the orchestrator prompt, each task file, and AGENTS.md so every participant in the loop sees the same standard.

Use --dry-run to preview the generated output before running:

# Preview what each strategy generates without writing any files
wiggum generate plan.toml --dry-run

Change strategy in [orchestrator], run --dry-run, and compare. The orchestrator prompt is the primary artifact that changes between strategies.

Evaluator configuration

The optional [evaluator] section enables an independent QA agent that scores each task after the subagent marks it complete. When present, .vscode/evaluator.prompt.md is generated alongside the orchestrator prompt.

[evaluator]
persona        = "You are a skeptical QA engineer"
pass_threshold = 7
hard_fail      = true
test_tool      = "cargo test --workspace"

Fields

FieldRequiredDefaultDescription
personaNo"You are a rigorous QA evaluator"Evaluator agent persona
pass_thresholdNo7Minimum score (0–10) for a criterion to pass
hard_failNofalseIf true, abort the loop on any failed criterion
test_toolNoInherits preflight.testCommand the evaluator uses to run the test suite
modelNo—Model identifier for the evaluator agent. Rendered as a header note in evaluator.prompt.md and passed as model: when the orchestrator dispatches the evaluator via runSubagent.

Security configuration

The optional [security] section controls Wiggum’s automatic security features.

[security]
skip_hardening_task = false

Fields

FieldRequiredDefaultDescription
skip_hardening_taskNofalseWhen true, suppresses auto-injection of the security-hardening task even if web-surface keywords are detected in task slugs

See Security for a complete description of all three levels of automatic security hardening.

Integration configuration

The optional [integration] section controls Wiggum’s automatic integration audit tasks that catch common AI failure modes.

[integration]
skip_wiring_audit = false
skip_stub_audit = false

Fields

FieldRequiredDefaultDescription
skip_wiring_auditNofalseWhen true, suppresses auto-injection of the integration-wiring task
skip_stub_auditNofalseWhen true, suppresses auto-injection of the stub-cleanup task

Both audit tasks are auto-injected when your plan has 3+ tasks. The wiring audit verifies that all components are properly connected (routes registered, services instantiated, middleware mounted). The stub cleanup audit searches for placeholder implementations like todo!(), unimplemented!(), or raise NotImplementedError.

See Security — Integration Audits for full details.

Style configuration

The optional [style] section controls writing style guidance to reduce detectability of AI-generated code.

[style]
avoid_ai_patterns = true
avoid_god_files = true
strict = false
completion_standard = """
Every task ships when all of the following hold:
- Preflight (build + test + lint) passes with zero errors and zero warnings.
- No placeholder implementations remain in production code.
- No lint suppressions have been added to silence errors — the issue was fixed in code.
"""

Fields

FieldRequiredDefaultDescription
avoid_ai_patternsNotrueWhen enabled, prompts receive hints to avoid common AI writing patterns
avoid_god_filesNotrueWhen enabled, prompts include file-structure guidance that discourages creating “God” files
strictNofalseWhen true, injects the language-specific strict rule set (full pedantic clippy for Rust, golangci-lint v2 for Go, PHPStan level max for PHP, etc.) into every prompt. See Strict Standards.
completion_standardNogeneric defaultThe standing directive on the bar for shipped work. Rendered as a <COMPLETION_STANDARD> reference block at the top of the opencode orchestrator prompt and in the root ORCHESTRATOR.md, and injected verbatim into every subagent dispatch alongside Accumulated Learnings and Codebase State. Override per project to narrow the bar with project-specific requirements (e.g. “no unwrap() outside tests”, “balance math must use checked arithmetic”).

When avoid_ai_patterns is enabled, generated prompts include guidance to:

  • Avoid “slop” vocabulary — Words like “robust”, “comprehensive”, “leverage”, “utilize”, “delve”, “embark”, “streamlined”, “cutting-edge”, “pivotal”, “seamless”, “synergistic”, “transformative”, “harness”, “facilitate”, “innovative”
  • Skip filler phrases — Phrases like “it’s worth noting that”, “at its core”, “let’s break this down”, “in order to”, “from a broader perspective”, “a key takeaway is”
  • Prevent prompt leakage — Avoid echoing instructions or stating “As an AI…” in comments
  • Write naturally — Prefer direct, human-like language over formal or corporate phrasing
  • Self-documenting code — Favor meaningful names over excessive comments

Each language profile includes ai_avoidance_rules and comment_guidelines that are injected when this setting is enabled.

When avoid_god_files is enabled, generated prompts also include guidance to:

  • Keep files focused on one primary responsibility
  • Create a focused module/file for new concerns instead of extending unrelated files
  • Avoid catch-all files (utils, helpers, common) containing unrelated logic
  • Split overloaded files before adding more behavior

When avoid_god_files is enabled and architecture = "hexagonal", prompts additionally include:

  • Introduce the port trait first when splitting an overloaded file, then move the implementation — never invent the interface and migrate code in the same step

Disabling AI pattern avoidance

[style]
avoid_ai_patterns = false
avoid_god_files = false

Workspaces

A Wiggum workspace lets you orchestrate multiple plan.toml files as a unit. Use it when a project spans several independent components — a shared library, an API service, a background worker — that must be developed in a specific order but each need their own plan.

workspace.toml

A workspace.toml file sits at the root of a multi-plan project and lists each component plan:

[workspace]
name = "my-platform"
description = "Multi-service platform workspace"

[[plans]]
name = "shared-lib"
path = "libs/shared/plan.toml"

[[plans]]
name = "api-service"
path = "services/api/plan.toml"
depends_on = ["shared-lib"]

[[plans]]
name = "worker"
path = "services/worker/plan.toml"
depends_on = ["shared-lib"]

Fields

[workspace]

FieldRequiredDescription
nameYesHuman-readable workspace name
descriptionNoBrief description of what the workspace contains

[[plans]]

FieldRequiredDescription
nameYesShort identifier used in depends_on references (e.g. "api-service")
pathYesRelative path to the plan TOML (relative to workspace.toml)
depends_onNoNames of other plans this plan depends on. Plans in the dependency list must be completed before this plan begins.

Inter-plan dependencies

depends_on on a [[plans]] entry defines ordering at the workspace level, independent of task-level dependencies inside each plan. Wiggum validates that the workspace dependency graph is a valid DAG — circular workspace dependencies are rejected.

Scaffold generation

Use the wiggum_draft_plan MCP tool or wiggum init to create individual plan.toml files for each component, then wire them together in workspace.toml.

Each component plan is generated and run independently. The workspace file provides the dependency ordering so the orchestrator knows which plans can proceed in parallel and which must wait.

Capabilities

A capability is a persistent behavioural contract the system must satisfy, independent of any particular phase or task. Capabilities cut across the implementation plan: they describe what the system does, while phases describe when that work happens.

Capabilities are useful when:

  • The same contract is satisfied by tasks that live in different phases (e.g. validation lives in Phase 1 and Phase 3 — one capability, two implementers).
  • You want a stable artefact that survives regenerations: the contract for “what POST /webhook must do” should not change just because T03 was renumbered to T07.
  • The evaluator needs a structured acceptance contract more rigorous than a flat bullet — WHEN ... THEN ... scenarios are machine-readable in a way prose is not.

Defining a capability

Add a [[capabilities]] block to plan.toml:

[[capabilities]]
name        = "webhook-reception"
title       = "Inbound Webhook Reception"
description = "Accepts and validates inbound webhook deliveries before fanning out to subscribers."

[[capabilities.requirements]]
value = "MUST validate the HMAC-SHA256 signature on every inbound POST before persisting the event."

[[capabilities.requirements]]
value = "MUST return 202 Accepted within 200ms p95 for a valid request."

[[capabilities.scenarios]]
name = "valid-signature"
when = "POST /webhook receives a request with a valid HMAC-SHA256 signature"
then = "the server returns 202 Accepted and persists the event via the EventStore port"

[[capabilities.scenarios]]
name = "invalid-signature"
when = "POST /webhook receives a request with a tampered payload or wrong secret"
then = "the server returns 400 Bad Request and never persists the event"

Fields

FieldRequiredNotes
nameyesFilename-safe slug. Used for capabilities/<name>.md and as the cross-reference target from TaskDef::implements. Must be unique within the plan.
titleyesDisplay title rendered as the file’s # <title> heading.
descriptionnoFree-form prose; rendered under ## Description.
requirementsnoList of MUST / SHALL bullets. Rendered as a flat bullet list under ## Requirements. Lint warns when empty.
scenariosnoList of WHEN/THEN clauses. Rendered as ### Scenario: <name> blocks. Lint warns when empty.

Scenario fields

FieldRequiredNotes
nameyesSlug identifying the scenario within its capability.
whenyesPrecondition / trigger. Free-form prose.
thenyesObservable outcome. Free-form prose. Must be verifiable — a THEN clause that cannot be checked is a lint anti-pattern.

Referencing a capability from a task

Add implements = ["<name>"] to the task. Multiple references are allowed:

[[phases.tasks]]
slug      = "webhook-router"
title     = "HMAC-validated webhook router"
goal      = "Stand up the router that receives, validates, and persists inbound webhooks."
implements = ["webhook-reception"]

When a task declares implements, the rendered task file gains a new section between Goal and Project Context:

## Implements

### Capability: [webhook-reception](../capabilities/webhook-reception.md) — Inbound Webhook Reception

- MUST validate the HMAC-SHA256 signature on every inbound POST before persisting the event.
- MUST return 202 Accepted within 200ms p95 for a valid request.

**Scenarios this task must satisfy:**

- **valid-signature** — WHEN `POST /webhook receives a request with a valid HMAC-SHA256 signature` THEN `the server returns 202 Accepted and persists the event via the EventStore port`
- **invalid-signature** — WHEN `POST /webhook receives a request with a tampered payload or wrong secret` THEN `the server returns 400 Bad Request and never persists the event`

This means the subagent sees the acceptance contract inline — no extra file reads required.

Generated artifacts

When the plan has one or more [[capabilities]], wiggum generate emits:

FileContent
capabilities/<name>.mdOne per capability. Self-contained — title, description, requirements, scenarios.
IMPLEMENTATION_PLAN.mdNew ## Capabilities section that links to every capability file.
tasks/TNN-<slug>.md## Implements section (only when the task references ≥1 capability) inlining requirements and scenarios.
ORCHESTRATOR.md / orchestrator promptsNew Setup step telling the orchestrator to read every file in capabilities/ before dispatching subagents.

If the plan has zero capabilities, no capabilities/ directory is created — this is the default and keeps generated trees minimal for plans that don’t need contracts.

Validation

wiggum validate enforces structural correctness:

  • Unknown reference (error): T03 references undefined capability "foo" — every name in implements = [...] must resolve to a [[capabilities]] entry.
  • Duplicate name (error): duplicate capability name(s): foo — every capability needs a unique name.

wiggum validate --lint adds softer checks:

RuleSeverityTrigger
capability-unusedwarningCapability declared but no task references it.
capability-no-scenarioswarningCapability has no acceptance contract.
capability-no-requirementsinfoCapability has no prose requirements.

Cleaning up

wiggum clean removes every capabilities/<name>.md file that corresponds to a declared capability. The capabilities/ directory itself is removed when empty afterwards — same safety rule as .vscode/ and .opencode/: hand-written files alongside wiggum’s output are preserved.

Relationship to phases

Capabilities and phases are independent axes:

  • A capability is satisfied by one or more tasks across one or more phases.
  • A phase contains one or more tasks that may or may not implement any capability.
  • A task without implements = [...] is fine — most tasks aren’t contract-bearing.

If a capability is satisfied by tasks scattered across phases (e.g. “audit logging” might span Phase 4 logging, Phase 7 compliance review), that’s normal — the capability file is the persistent contract and the task files are the per-phase implementers.

Code Anchors

An anchor pins a task to a verified location in the target repository — a path, optionally a line range, and a note explaining why that location matters.

Anchors are a progressive enhancement. Every plan is valid without them. If a plan declares no anchors, nothing about generation, validation, or linting changes. Add them to the tasks where a subagent would otherwise have to guess where the work happens.

Why they exist

A task goal like “extend the retry logic in the delivery worker” sends the implementer subagent hunting. It greps, finds three plausible sites, picks one, and may produce a plausible-looking change to the wrong file.

An anchor replaces the guess with a citation:

[[phases.tasks.anchors]]
path = "src/adapters/queue.rs"
lines = "88-140"
role = "modify"
note = "the exponential-backoff loop — replace the fixed 30s sleep"

The subagent starts at a known location. It also has a cheap way to notice when its instructions have gone stale: if the cited region no longer looks like the note describes, trust the code.

Defining an anchor

Anchors live under a task:

[[phases.tasks]]
slug = "add-retry-backoff"
title = "Exponential backoff on delivery retry"
goal = "Replace the fixed 30-second retry sleep with exponential backoff."
hints = ["Keep the maximum delay capped at 5 minutes."]
test_hints = ["Test that delay roughly doubles across the first four attempts."]

[[phases.tasks.anchors]]
path  = "src/adapters/queue.rs"
lines = "88-140"
role  = "modify"
note  = "the fixed 30s sleep and the retry counter around it"

[[phases.tasks.anchors]]
path = "src/domain/backoff.rs"
role = "create"
note = "new module — compute the next delay from the attempt count"

Fields

FieldRequiredTypeDescription
pathyesstringPath relative to [project].path. Absolute paths and paths containing .. are rejected.
linesnostringInclusive 1-based range: "42" or "42-80". Reversed input ("80-42") is normalised. Omit to anchor the whole file.
rolenostringread (default), modify, or create.
notenostringWhy this location matters — the symbol to extend, the behaviour to change.

role drives the checks: create asserts the file does not exist yet; read and modify assert it does.

Rendered output

Anchors are inlined into the generated task file under a ## Code Anchors section, so the subagent sees them without opening anything else:

## Code Anchors

Start here before searching. These locations were verified against the codebase
when the plan was written; if one no longer matches, trust the code over the plan.

- `src/adapters/queue.rs:88-140` — **modify**: the fixed 30s sleep and the retry counter around it
- `src/domain/backoff.rs` — **create**: new module — compute the next delay from the attempt count

Tasks with no anchors get no such section.

Drift detection

wiggum validate --lint resolves every anchor against the project on disk. This is what turns “the plan says X” into something checkable.

ConditionRuleSeverity
path is emptyanchor-empty-patherror
File missing (read/modify)anchor-file-missingerror
Path is a directoryanchor-is-directoryerror
Path is absolute or escapes the project rootanchor-escapes-rootwarning
Line range ends past the last lineanchor-line-driftwarning
lines is not parseableanchor-bad-lineswarning
File not readable as UTF-8anchor-unreadableinfo
role = "create" but the file already existsanchor-already-createdinfo
No note suppliedanchor-no-noteinfo
[project].path does not existanchor-root-missingwarning (reported once)
$ wiggum validate plan.toml --lint
✅ Plan is valid
   Phases: 1
   Tasks:  1
   Order:  add-retry-backoff

Lint: 1 warning
  ⚠  T01-add-retry-backoff: `src/adapters/queue.rs:88-140` points past end of
     file (2 lines) — the code likely moved

Warnings do not fail the run. Errors do not block generation either — validate --lint reports them, and wiggum generate proceeds. Treat a anchor-file-missing error as “this plan needs a refresh before it is worth running”, not as a hard gate.

The same checks run through the MCP wiggum_lint_plan tool, so an agent driving wiggum through MCP gets drift warnings in its lint result too.

Keeping anchors honest

Anchors rot. A refactor upstream shifts lines, and a citation written last month can now point at the wrong function. Two habits keep them trustworthy:

  • Re-run wiggum validate --lint before wiggum generate. Drift is cheap to find and cheap to fix at that point.
  • Treat a anchor-line-drift warning as “re-read the file”, not “widen the range”. The line numbers moved; the note is still the thing that tells you which function you meant.

If an anchor no longer describes anywhere useful, delete it. An anchor that is present but wrong is worse than no anchor, because the subagent is told to trust it.

Relationship to capabilities

Capabilities and anchors answer different questions:

  • A capability says what must be true of the system — WHEN ... THEN ... scenarios, invariant across tasks and phases.
  • An anchor says where in the code a specific task does its work.

They compose: a task can implement a capability and anchor to the region it edits. Anchors are per-task and usually short-lived; capabilities are plan-scoped and long-lived.

Security

Wiggum bakes security into every generated plan at three levels: rules embedded in subagent prompts, a vulnerability audit command appended to every preflight chain, and an automatically injected security hardening task for plans with web-facing surface.

Why it’s automatic

Independent security research consistently finds that AI-generated code introduces OWASP Top 10 vulnerabilities at high rates — particularly hardcoded secrets, SQL injection, missing HTTP security headers, disconnected rate limiting, unsafe file uploads, and SSRF. Wiggum treats these as structural concerns that belong in every plan by default, not optional additions the user must remember to include.

For projects that need an even stricter baseline — every commit must satisfy the verified-modern toolchain baseline for the target language (full pedantic clippy for Rust, golangci-lint v2 + govulncheck for Go, PHPStan level max + Psalm --taint-analysis for PHP, etc.) — enable [style] strict = true. See Strict Standards for the full rule sets per language.

Level 1 — Security rules in every subagent prompt

Every generated task file and orchestrator prompt includes a ## Security (non-negotiable) section populated from the language profile. These six rules are always injected:

CategoryRule
SecretsCredentials and API keys must only be read from environment variables or a secrets manager — never hardcoded
SQL injectionAll database queries must use parameterised inputs — never interpolate user input into query strings
Security headersHTTP servers must set Content-Security-Policy, Strict-Transport-Security, X-Frame-Options, and X-Content-Type-Options
Rate limitingRate-limiting middleware must be wired to the router, not just defined — verified by a smoke test
File uploadsUpload handlers must validate MIME type server-side, reject executable extensions, and enforce a maximum file size
SSRFAny feature fetching URLs on behalf of a user must validate the target against an explicit allowlist

Rules are language-specific (e.g. the SQL rule references sqlx for Rust, PreparedStatement for Java, Ecto for Elixir) but cover the same six categories for every language.

You can add project-specific security rules on top via [orchestrator] rules:

[orchestrator]
rules = [
    "HMAC secrets must never appear in log output at any log level.",
    "All outbound HTTP requests must use a timeout of 10 seconds.",
]

Level 2 — Vulnerability audit in every preflight

Each language profile includes an audit_cmd that is appended to the preflight chain run after every task:

LanguageAudit command
Rustcargo audit
Gogovulncheck ./...
TypeScriptnpm audit --audit-level=high
Pythonpip-audit
Javamvn dependency-check:check
C#dotnet list package --vulnerable
Kotlingradle dependencyCheckAnalyze
Rubybundle exec bundler-audit check --update
Elixirmix deps.audit
Swift(no standard tool; field left empty)

So for a Rust plan, every task’s preflight block becomes:

cargo build --workspace && cargo test --workspace && cargo clippy --workspace -- -D warnings && cargo audit

And the task’s exit criteria automatically includes:

  • cargo audit reports no vulnerabilities

Overriding the audit command

Override per-plan in [preflight]:

[preflight]
audit = "cargo audit --deny warnings"

Disabling the audit

Set audit to an empty string:

[preflight]
audit = ""

Level 3 — Auto-injected security hardening task

When your plan contains web-facing surface, Wiggum automatically appends a security-hardening task as the final task. Web surface is detected from task slugs and titles containing any of: http, api, server, router, route, endpoint, handler, webhook, upload, auth, login, session, request, response, middleware, web, rest, grpc, graphql.

The injected task has:

  • Goal — Verify and enforce the six OWASP baseline security properties across the entire codebase
  • Hints — One concrete guidance item per category (grep for secrets, verify parameterised queries, check headers are wired, write a rate-limit smoke test, inspect upload handlers, check URL-fetching allowlists)
  • Test hints — Rate-limit smoke test (assert HTTP 429 at N+1 requests), upload rejection test, SSRF rejection test
  • Must-haves — Six items, one per OWASP category
  • Evaluation criteria — Five verifiable conditions scored by the evaluator

This task depends on the last explicit task in your plan, so it always runs last. The evaluator will hard-fail if any criterion is not met when [evaluator] hard_fail = true.

Opting out

If you’re handling security via a separate process or your plan doesn’t actually have web surface, suppress injection with:

[security]
skip_hardening_task = true

You can also manually include a task with the slug security-hardening in your plan — if that slug is already present, auto-injection is skipped automatically.

Integration Audits

Beyond security vulnerabilities, AI-generated code frequently has two structural failure modes that lead to runtime crashes:

  1. Disconnected wiring — modules, services, and handlers are created but never actually connected to the application
  2. Stub implementations — placeholder code like todo!(), unimplemented!(), or raise NotImplementedError that compiles but crashes at runtime

Wiggum auto-injects two late-stage audit tasks when your plan has 3+ tasks:

Integration wiring audit

The integration-wiring task verifies all components are properly connected:

CheckDescription
Public exportsAll public items from library modules are imported and used somewhere
Route registrationAll handlers/controllers are registered with the router/framework
Service instantiationAll interfaces have implementations that are actually instantiated
Background tasksAll workers/jobs are spawned in application startup
MiddlewareAll middleware/interceptors are mounted on the request pipeline
ConfigurationConfig values are read and passed to components that need them

Each language profile provides specific wiring hints tailored to its ecosystem (e.g., “Confirm every port trait has at least one adapter implementation wired in main.rs” for Rust hexagonal architecture).

Stub cleanup audit

The stub-cleanup task finds and replaces placeholder implementations:

LanguageSample stub patterns (not exhaustive)
Rusttodo!(), unimplemented!(), panic!("not implemented"), // TODO, // FIXME
Gopanic("not implemented"), // TODO, return nil // stub, return errors.New("not implemented")
TypeScriptthrow new Error('Not implemented'), // TODO, return undefined as any
Pythonraise NotImplementedError, pass # TODO, # FIXME
Javathrow new UnsupportedOperationException(), // TODO, return null; // stub

Each language profile contains the full list of patterns — see the stub_patterns field in src/domain/languages/*.rs for the complete set.

Opting out

Suppress either or both audits with:

[integration]
skip_wiring_audit = true   # Disable wiring audit
skip_stub_audit = true     # Disable stub cleanup audit

You can also manually include tasks with slugs integration-wiring or stub-cleanup — if either slug is already present, the corresponding auto-injection is skipped.

Repository security posture (Wiggum itself)

The sections above describe security controls injected into generated plans. Wiggum’s own repository and release pipeline are also hardened:

  • Least-privilege Actions tokens — workflows use minimum required permissions.
  • No privileged trigger patterns — no pull_request_target; workflow_run is used only for safe workflow chaining.
  • CI-gated tagging — auto-tag only runs after CI succeeds on main.
  • CI-gated publishing — release workflow verifies CI passed for the tagged commit SHA before publishing to crates.io.
  • Version/tag integrity checks — release workflow verifies Cargo package version matches the release tag.
  • Continuous security checks — CodeQL, cargo audit, and dependency updates (Dependabot) run continuously.

Why release uses workflow_run chaining

GitHub does not trigger downstream on: push: tags workflows when a tag is pushed by another workflow using the default GITHUB_TOKEN.

To keep releases automated without introducing elevated tokens, Wiggum uses this chain:

  1. CI succeeds on main.
  2. Auto-tag workflow creates/pushes v* tag.
  3. Release workflow is triggered via workflow_run on auto-tag completion.
  4. Release verifies CI status for the tagged SHA, then publishes.

This avoids token escalation while keeping publish automation deterministic.

Strict Standards

Beyond the language-specific security rules that every plan inherits, Wiggum offers an opt-in strict mode that injects a richer, harder-to-satisfy rule set into every prompt. Strict mode is for projects where “the build passes” is not enough — every commit must produce code that complies with a verified-modern toolchain baseline for the target language.

Enabling strict mode

Add strict = true to your [style] section:

[style]
strict = true   # inject the language's strict ruleset

When strict = true, the orchestrator injects the matching language block into every subagent’s task prompt and escalates the profile’s lint/audit commands — it does not replace the existing lint_cmd / audit_cmd, it adds to them. The intent is the same as the Rust profile: fail-secure by default, parse at boundaries, no panics on untrusted input, no weak crypto, supply-chain audited in CI, warnings treated as errors.

Disable per-task if a specific task is a spike or prototype:

[[phases.tasks]]
slug = "experiment-with-rust-macros"
title = "Investigate derive macro ergonomics"
goal = "Spike to evaluate macro options; produce a recommendation document."
strict = false

What strict mode adds

Each language profile defines a strict_rules array that is injected as a new section in the generated prompts only when strict = true. The rules are language-specific but share a common theme: they encode the modern, security-centric baseline that the language’s toolchain makes possible when fully engaged.

For Rust, strict mode mirrors the rules in your project’s nick-v2.md (the personal standards file; supersedes the original nick.md). New rules are sourced from the Rust 1.78→1.99 catchup guide at ~/Projects/rust/rust-docs/rust-catchup-1.78-1.99.md. For every other language, the ruleset is documented in the companion file docs/strict-lints.md at the root of the wiggum repo.

What strict mode adds for Rust (1.95+)

The Rust strict_rules array is sourced from two documents:

  • ~/Projects/nick-v2.md — DDD-lite hexagonal layout, narrow port traits, AuthContext + idempotency keys, with_tx transaction boundaries, object-safe async port traits, CLI input parsing at adapter boundaries, the strengthened #[expect] rule, OWASP web security defaults.
  • rust-catchup-1.78-1.99.md — Rust 1.95+ syntax (if let arm guards, let chains on 2024 edition), std::io::pipe() + Vec::extract_if (1.87+), the 1.97 pin! deref coercion soundness fix, deny(dead_code_pub_in_binary) for tight binaries, cross-platform #[cfg(unix)] hygiene.

Code snippets for surprising new rules

Object-safe async port trait (boxed future)

In a hexagonal codebase where port traits are used as Arc<dyn Trait> for dependency injection, native async fn in traits isn’t object-safe. Use the async-trait crate or write an explicit boxed future:

#![allow(unused)]
fn main() {
// Native async — NOT object-safe
trait MyPort {
    async fn process(&self) -> Result<()>;
}

// Object-safe — explicit boxed future
trait MyPort: Send + Sync {
    fn process(&self) -> std::pin::Pin<Box<dyn Future<Output = Result<()>> + Send + '_>>;
}

impl MyPort for ConcreteAdapter {
    fn process(&self) -> std::pin::Pin<Box<dyn Future<Output = Result<()>> + Send + '_>> {
        Box::pin(async move {
            // implementation
            Ok(())
        })
    }
}
}

One heap allocation per call is acceptable for HTTP handlers and DB operations; reserve concrete impl Trait for hot paths.

LazyCell vs LazyLock choice (Rust 1.80+)

#![allow(unused)]
fn main() {
// Single-threaded context — use LazyCell
use std::cell::LazyCell;
static CONFIG: LazyCell<Config> = LazyCell::new(|| load_config());

// Multi-threaded initialisation — use LazyLock
use std::sync::LazyLock;
static CONFIG: LazyLock<Config> = LazyLock::new(|| load_config());
}

Reaching for LazyLock thread-locally signals a confused ownership model — single-threaded code should use LazyCell (no atomic reference count, no thread safety overhead).

#[expect(dead_code)] over #[allow(dead_code)] (Rust 1.81+)

#![allow(unused)]
fn main() {
// Old way — silently becomes dead code when the lint no longer fires
#[allow(dead_code)]
fn temporary_scaffolding() { }

// New way — compiler warns when the suppression becomes unnecessary
#[expect(dead_code)]
fn temporary_scaffolding() { }
}

Use #[expect(lint, reason = "...")] for temporary suppressions; reserve #[allow] for permanent, intentional deviations with a documented reason.

Let chains (2024 edition)

#![allow(unused)]
fn main() {
if let Some(x) = opt
    && let Ok(y) = x.parse::<i32>()
    && y > 0
{
    println!("{y}");
}
}

Replaces nested if let Some(...) { if let Ok(...) { if ... {} } } chains. Available on the 2024 edition only — port your crate to 2024 before adopting this pattern.

Match arm if let guards (Rust 1.95+)

#![allow(unused)]
fn main() {
match event {
    Event::Login { user_id } if user_id.is_admin() => handle_admin(),
    Event::Login { user_id } => handle_user(user_id),
    _ => {}
}
}

Cleaner pattern-matching control flow than nesting a match inside an if let.

Example strict-mode rule excerpts by language:

LanguageSample strict rules (see docs/strict-lints.md for the full set)
RustNo .unwrap() / .expect() / panic! in production code; no index slicing that can panic; no #[allow(clippy::...)] suppressions; full pedantic + nursery + perf clippy profile with hard denials
Gogolangci-lint v2 + gofumpt + govulncheck; never discard errors; context.Context everywhere; depguard bans on crypto/md5, crypto/sha1, math/rand
TypeScripttypescript-eslint v8 strictTypeChecked; noUncheckedIndexedAccess; Zod at every input boundary; no any / !; node:crypto for randomness
PythonRuff with the S (bandit) group on; mypy --strict; pip-audit; no pickle.loads / yaml.load; secrets for tokens
JavaError Prone + NullAway + SpotBugs findsecbugs; PreparedStatement only; no ObjectInputStream on untrusted data
C# / .NETRoslyn AnalysisMode=All + Nullable=enable + Security Code Scan; no ! null-forgiving; no BinaryFormatter
Kotlindetekt allRules + explicitApi(); no !!; no GlobalScope; structured concurrency only
SwiftSwift 6 language mode + complete strict concurrency; no @unchecked Sendable; no force-unwrap/try/cast outside tests
RubyRuboCop Security/* + Lint/* as errors; Brakeman with -z; Sorbet # typed: strict
Elixir--warnings-as-errors + mix credo --strict + Dialyzer + Sobelow --exit; never String.to_atom/1 on user input
PHPPHPStan level max + phpstan-strict-rules + Psalm --taint-analysis; declare(strict_types=1); password_hash (Argon2id); random_bytes / random_int

Cross-language baseline

Every language profile’s strict_rules includes the same closing pair drawn from the cross-language baseline in docs/strict-lints.md:

  • Treat warnings as errors — the language’s “warnings as errors” switch stays on; a warning fails the build.
  • No suppression without justification — never blanket-disable a rule; suppress narrowly, inline, with a rule ID and a one-line reason. Prefer fixing.

These hold regardless of language and are injected alongside the language-specific rules.

Where strict rules appear

When strict = true, every generated artifact that contains prompt content gets the strict block:

  • VSCode target — orchestrator.prompt.md, each tasks/T{NN}-{slug}.md, evaluator.prompt.md (when [evaluator] is configured)
  • opencode target — orchestrator.md (the embedded subagent body inherits the rules), evaluator.md (when [evaluator] is configured)
  • Claude target — CLAUDE.md (so Claude Code sees the rules on every session)
  • agent-rules target — .cursorrules, .windsurfrules, .github/copilot-instructions.md (so Cursor / Windsurf / Copilot users see them too)

Tooling version pins

The strict profiles track specific toolchain versions because the rules are written against those tools’ surface area. When you adopt strict mode, pin your project to the version that matches the rules — moving to a newer toolchain without re-pinning the rules can leave gaps.

Current pins (see docs/strict-lints.md for the canonical list):

  • Rust — 1.95+ minimum (match arm if let guards, deny(dead_code_pub_in_binary), LazyCell/LazyLock); 1.97+ recommended for the pin! deref coercion soundness fix and [build] warnings = "deny"; 2024 edition required for let chains.
  • Go — golangci-lint v2 + Go 1.24+ + gofumpt
  • TypeScript — typescript-eslint v8 flat config + projectService: true
  • Python — Ruff (linter + formatter) + mypy --strict + Python 3.12+
  • Java — Error Prone + NullAway + SpotBugs + findsecbugs on JDK 21+
  • C# / .NET — Roslyn AnalysisMode=All + Security Code Scan on .NET 8+
  • Kotlin — detekt allRules = true + ktlint on JDK 21+
  • Swift — Swift 6 language mode + SwiftLint --strict
  • Ruby — RuboCop (with rubocop-performance, rubocop-rspec) + Brakeman + Sorbet on Ruby 3.2+
  • Elixir — Credo --strict + Dialyzer via Dialyxir + Sobelow on Elixir 1.16+ / OTP 26+
  • PHP — PHPStan 2.x at level max + Psalm --taint-analysis on PHP 8.3+

When to enable strict mode

Enable strict = true when:

  • The project is security-sensitive (auth, payments, PII, infra)
  • The team is multi-engineer and you want a single canonical standard instead of per-developer conventions
  • You’re starting a new codebase and you want to prevent AI-generated slop from accumulating
  • The project will outlive any one AI model’s current capabilities — rules survive the model

Leave strict = false (the default) when:

  • You’re prototyping or evaluating wiggum itself
  • The codebase predates the strict toolchain baseline (e.g. a Python 2 codebase)
  • You need fast iteration and are willing to clean up later

Strict mode is additive, not destructive. You can flip strict = true on an existing plan at any time — the next wiggum generate injects the new rules into all subagent prompts without altering task content, hints, or preflight commands.

CLI Reference

wiggum init

Interactively create a new plan file.

wiggum init [--plan <path>]
OptionDescription
--plan, -pPath to write the generated plan TOML (default: plan.toml)

wiggum generate

Generate all artifacts from a plan file.

wiggum generate <plan> [OPTIONS]
OptionDescription
<plan>Path to the plan TOML file
--output, -oOverride the output directory (defaults to project.path from the plan)
--forceOverwrite existing files without prompting
--dry-runPreview what would be generated without writing files
--estimate-tokensShow estimated token counts for generated artifacts
--skip-agents-mdSkip AGENTS.md generation
--target <vscode|opencode|claude|agent-rules|all>Override the target tool selection. Overrides the plan’s [targets] section. See Targets.
--no-summarySuppress the plan quality scorecard printed after generation
--thinUniversal artifacts only — skip every per-tool prompt directory (.vscode/, .opencode/, .claude/, agent_rules)

wiggum check

Score the quality of a plan file before generating. Unlike validate --lint, which checks structural correctness, check scores the substance of a plan on five dimensions and produces concrete improvement suggestions. The same score is now printed at the end of wiggum generate so the quality signal lands at the moment it matters most — right after scaffolding. Pass --no-summary to suppress it.

wiggum check <plan> [--json]
OptionDescription
<plan>Path to the plan TOML file
--jsonOutput results as JSON instead of human-readable text

The six scoring dimensions are:

DimensionWhat it measures
GranularityWhether tasks are sized for a single agent session (not too broad or too narrow)
Dependency healthDAG fan-out, orphan detection, and over-coupling
CoverageBalance of task kinds across the plan
RichnessPresence of hints, must-haves, evaluation criteria
Token budgetEstimated prompt size across all generated artifacts
Harness complexityWhether the evaluator harness matches plan scale — penalises over-engineered harnesses on tiny plans and rewards high criteria coverage

Each dimension scores 0–10. The overall score is a weighted composite. Plans scoring ≥ 7 are considered healthy; wiggum check exits with a non-zero status if the plan is below this threshold.

Run check before generate to catch low-quality plans early:

wiggum check plan.toml
wiggum check plan.toml --json

wiggum validate

Validate a plan file without generating artifacts.

wiggum validate <plan> [--lint]
OptionDescription
<plan>Path to the plan TOML file
--lintRun lint rules to check plan quality

wiggum add-task

Add a task to an existing plan file interactively.

wiggum add-task <plan>

wiggum bootstrap

Bootstrap a plan from an existing project directory. Detects language, build system, and project structure.

wiggum bootstrap [path] [OPTIONS]
OptionDescription
[path]Path to the project directory (default: .)
--output, -oPath to write the generated plan TOML (default: <path>/plan.toml)
--forceOverwrite existing plan file without prompting

wiggum reverse

Clone a remote git repo into a tempdir and generate a plan.toml skeleton from its contents. Optionally merge a hints file (TOML for structured overrides, Markdown for freeform rules) and/or run an LLM pass to produce intelligent phases + tasks.

wiggum reverse <url> [OPTIONS]
OptionDescription
<url>Any URL git clone accepts (GitHub, GitLab, self-hosted, SSH, file://)
--hintsOptional hints file — .toml for structured, .md for freeform rules
--output, -oPath to write the generated plan TOML (default: ./plan.toml)
--forceOverwrite existing plan file without prompting
--keep-tmpKeep the cloned tempdir after generation (for debugging)
--subdirScope the clone to a specific subfolder of the repo. For GitHub URLs, /tree/<branch>/<path> works too
--github-apiPrefer the GitHub REST API for fetching repo metadata/tree/files (falls back to git clone on failure)
--llmUse an LLM to generate intelligent phases + tasks. Provider = anthropic or minimax. Requires <PROVIDER>_API_KEY env var
--llm-modelOverride the LLM model (default: claude-sonnet-4-5 for anthropic, MiniMax-M3 for minimax)
--api-keyOverride the LLM API key (otherwise read from ANTHROPIC_API_KEY / MINIMAX_API_KEY)

Examples:

# Deterministic, no LLM
wiggum reverse https://github.com/foo/bar --hints hints.toml

# Scope to a monorepo subpackage
wiggum reverse https://github.com/foo/bar/tree/main/services/api --hints hints.md

# LLM-driven phase decomposition (Anthropic Claude)
ANTHROPIC_API_KEY=sk-... wiggum reverse https://github.com/foo/bar --llm anthropic

# LLM-driven (MiniMax)
MINIMAX_API_KEY=... wiggum reverse https://github.com/foo/bar --llm minimax

# Pick a specific MiniMax model
wiggum reverse https://github.com/foo/bar --llm minimax --llm-model MiniMax-M2.7-highspeed

The hints TOML supports [project], [orchestrator], and [[phase]] blocks. See reference/example-hints.toml for the full schema, and reference/example-hints.md for the Markdown variant.

When --llm is set, the deterministic skeleton is replaced by an LLM-generated phase decomposition. The LLM receives the detected language, manifest, top-level tree, README excerpt, and your hints; it returns a JSON plan that we validate and merge. The LLM pass honours any phases you declared in your hints TOML — your structure wins over the model’s.

wiggum serve

Start the MCP server for agent integration.

wiggum serve --mcp

wiggum version

Show the CLI version and embedded git SHA.

wiggum version

Output format:

wiggum <version> (<sha|unknown>)

wiggum retro

Analyse a completed PROGRESS.md and emit retrospective improvement suggestions.

wiggum retro [OPTIONS]
OptionDescription
--progressPath to PROGRESS.md (default: PROGRESS.md)
--planPath to the plan TOML (used to correlate task slugs)
--saveSave the retrospective as a reusable pattern in ~/.wiggum/patterns/

wiggum replan

Re-generate a single task file after a failure. Reads the current plan and any failure evidence recorded in PROGRESS.md, then re-renders the task’s .md file with augmented hints.

wiggum replan <plan> --task <slug> [--dry-run]
OptionDescription
<plan>Path to the plan TOML file
--task, -tSlug of the task to re-generate (required)
--dry-runPrint the new content to stdout instead of writing to disk

Failure evidence is extracted from lines in PROGRESS.md that are tagged [~] or contain “required fix”. Extracted lines are prepended as [Previous failure] hints in the regenerated task file so the subagent has full context for the retry.

wiggum patterns

Manage the local pattern store (~/.wiggum/patterns/). Patterns are reusable TOML files derived from retrospectives that can be applied as hint augmentations to future plans.

wiggum patterns <action> [OPTIONS]

list

List all saved patterns.

wiggum patterns list

save

Save a pattern from a PROGRESS.md file (or an existing retro output).

wiggum patterns save --from <progress-path> --plan <plan-path>
OptionDescription
--fromPath to the source PROGRESS.md
--planPath to the plan TOML (provides language metadata)

apply

Apply matching patterns as additional hints to a plan file. Patterns are matched by language.

wiggum patterns apply --plan <plan-path>
OptionDescription
--planPath to the plan TOML to augment

wiggum report

Generate a post-execution report from PROGRESS.md.

wiggum report [OPTIONS]
OptionDescription
--progressPath to PROGRESS.md (default: PROGRESS.md)
--project-dirProject directory for git timeline (optional)

wiggum watch

Watch PROGRESS.md for live progress updates.

wiggum watch [OPTIONS]
OptionDescription
--progressPath to PROGRESS.md (default: PROGRESS.md)
--poll-msPoll interval in milliseconds (default: 1000)
--stall-secsSeconds before an in-progress task triggers a stall warning (default: 1800; set to 0 to disable)

When --stall-secs is non-zero, the watch display emits a ⚠ HEALTH warning next to any task that has remained [~] in-progress for longer than the threshold. The warning continues to refresh on every poll cycle until the task transitions to a terminal state.

MCP Server

Wiggum can run as an MCP (Model Context Protocol) server, allowing AI agents to invoke Wiggum’s capabilities mid-session.

The server speaks the MCP protocol revisions 2024-11-05 through 2025-11-25 over stdio transport (newline-delimited JSON). The revision is negotiated per session: the server answers initialize with the revision it settled on, and gates the rest of the session on that revision — a peer that offers 2024-11-05 is not sent features that revision does not have.

The wire format, the JSON-RPC framing, and the version negotiation are protocol-droid’s, so wiggum does not carry a second description of them and cannot fall out of date with the specification.

Starting the server

wiggum serve --mcp

This starts the MCP server using stdio transport.

Integration

To use Wiggum as an MCP server with your AI coding tool, add it to your MCP configuration. For example, in VS Code’s MCP settings:

{
  "servers": {
    "wiggum": {
      "command": "wiggum",
      "args": ["serve", "--mcp"]
    }
  }
}

This enables agents to generate plans and task scaffolds directly within a coding session, without leaving the editor.

Available tools

ToolDescription
wiggum_versionReturn wiggum version metadata (package, git SHA, MCP protocol)
wiggum_generate_planGenerate full scaffold from a plan TOML file path
wiggum_validate_planValidate a plan TOML file (dependency DAG check, missing fields)
wiggum_lint_planRun quality lint rules against a plan TOML file
wiggum_check_planScore plan quality on five dimensions (granularity, dependency health, coverage, richness, token budget); returns overall score 0–10 and actionable suggestions
wiggum_draft_planGenerate a skeleton plan.toml from a natural-language description; takes project_name, description, language, and optional task_slugs
wiggum_read_progressParse PROGRESS.md and return structured status
wiggum_update_progressUpdate a task’s status in PROGRESS.md
wiggum_list_templatesList available language/architecture templates
wiggum_reportGenerate a post-execution report from PROGRESS.md
wiggum_generate_agents_mdGenerate an AGENTS.md file from a plan TOML
wiggum_bootstrapScan an existing project directory and generate a skeleton plan TOML

Protocol compliance

The server handles all required lifecycle messages:

  • initialize — responds with the negotiated protocolVersion and the capabilities that revision has
  • notifications/initialized and notifications/cancelled — silently acknowledged (no response, per spec)
  • ping — responds with an empty result at any lifecycle phase
  • tools/list — returns the full tool catalogue
  • tools/call — dispatches to the named tool and returns content + optional isError

Unknown methods return JSON-RPC error -32601. Tool execution errors are returned as tool results with isError: true rather than protocol errors, so agents can self-correct.

Each tool’s inputSchema is derived from its Rust signature rather than written by hand, so the schema a peer sees and the arguments the tool reads cannot drift apart.

wiggum_version reports the revision the session actually settled on, not the newest revision the build could speak — a session knows what it negotiated, and a peer that asks an older revision is answered about that revision. The range it can be negotiated onto is reported alongside it.

Runtime security guardrails

MCP tools/call execution includes a baseline guardrail pipeline:

  • Input guardrail: blocks mutating tools when arguments contain common prompt-injection patterns (for example, attempts to override instructions or request exfiltration). The arguments are searched as a set rather than in any fixed order, so a pattern split across two or three arguments — or split mid-word — is still caught whatever order the arguments arrive in.
  • Output redaction: redacts common sensitive values in tool output text before returning content to the caller (emails, SSN format, bearer tokens, and basic secret-assignment patterns).
  • Output hard block: blocks responses that still contain high-risk secret markers (for example private key headers).
  • Security events: emits structured security events via tracing with event type, tool name, and detail for incident investigation.
  • Session anomaly monitoring: tracks tool-call sequences in-process and emits alerts for high read volume and suspicious read-to-write pivots.

Set WIGGUM_MCP_GUARDRAIL_STRICT=true to hard-block on session anomalies instead of alert-only mode.

These controls are intentionally lightweight and deterministic. They provide a production baseline for MCP sessions and can be extended with stricter policy engines where required.

The Ralph Wiggum Loop

The Ralph Wiggum loop is an orchestration pattern for autonomous AI coding. An orchestrator agent drives subagents through a dependency-ordered task list until an entire project is implemented.

How it works

  1. The orchestrator reads PROGRESS.md and features.json to find the next incomplete task
  2. It agrees a sprint contract with the subagent before handoff (scope, files, exit criteria)
  3. The subagent implements the task, runs preflight checks (build, test, lint), and commits
  4. The orchestrator independently re-runs preflight to verify — it does not trust the subagent’s self-report
  5. If an [evaluator] agent is configured, it scores each exit criterion and updates features.json
  6. The orchestrator marks the task complete in PROGRESS.md, records the codebase state diff and any learnings
  7. Repeat until all tasks show [x]

Why it works

  • Bounded context — Each subagent only sees one task file, keeping the prompt focused and reducing hallucination
  • Dependency ordering — Tasks are topologically sorted, so each subagent builds on verified prior work
  • Preflight gates — Every task must pass build/test/lint before being marked complete
  • Independent verification — The orchestrator re-runs preflight itself rather than trusting the subagent’s checkbox
  • Feature registry — features.json tracks per-criterion pass/fail state as objective ground truth
  • Codebase state handoff — Each subagent records what it changed, giving the next one accurate context
  • Learnings accumulate — The progress tracker captures insights from each task, providing growing context

Origin

The pattern was proven on the Yakko project, a Rust Microsoft Teams TUI client where 13 tasks across 7 phases were executed sequentially by subagents in a single automated session.

Wiggum makes this pattern reproducible for any project by generating the required artifacts from a structured plan definition.

Language Profiles

Wiggum ships with built-in profiles for 11 programming languages. Each profile provides sensible defaults for build commands, test patterns, documentation style, error handling conventions, security rules, and a vulnerability audit command.

Supported languages

LanguageBuild commandTest commandLint commandAudit command
Rustcargo build --workspacecargo test --workspacecargo clippy --workspace -- -D warningscargo audit
Gogo build ./...go test -v ./...go vet ./... && golangci-lint run ./...govulncheck ./...
TypeScripttsc --noEmitvitest runeslint .npm audit --audit-level=high
Pythonpython -m py_compilepytest -vruff check .pip-audit
Javamvn compilemvn testmvn checkstyle:checkmvn dependency-check:check
C# / .NETdotnet build --nologo -v qdotnet test --nologo -v qdotnet format --verify-no-changesdotnet list package --vulnerable
Kotlingradle buildgradle testgradle detektgradle dependencyCheckAnalyze
Swiftswift buildswift testswiftlint(none — SwiftPM has no first-party SCA)
Rubyruby -cbundle exec rspecbundle exec rubocopbundle exec bundler-audit check --update
Elixirmix compile --warnings-as-errorsmix testmix credo --strictmix deps.audit
PHPcomposer install --no-interaction --no-progressvendor/bin/phpunitvendor/bin/php-cs-fixer fix --dry-run --diff && vendor/bin/phpstan analysecomposer audit

What profiles provide

Each language profile includes:

  • Build success phrase — The expected output indicating a successful build (e.g., “Compiling” for Rust, “Build complete” for Go)
  • Test file pattern — Where test files are typically found (e.g., tests/ for Rust, *_test.go for Go)
  • Doc style — Documentation conventions (e.g., /// doc comments for Rust, GoDoc for Go)
  • Error handling — Idiomatic error handling approach (e.g., Result<T, E> for Rust, error return values for Go)
  • Security rules — Language-specific rules covering OWASP categories plus crypto, deserialization, and path traversal (see Security); profiles currently define 14–16 rules depending on the language
  • Audit command — Supply-chain vulnerability scanner appended to every task’s preflight chain
  • AI avoidance rules — Guidelines to reduce detectability of AI-generated code (see below)
  • Comment guidelines — Best practices for meaningful, non-robotic comments
  • Strict rules — A deeper language-specific rule set that is injected only when [style] strict = true is set in the plan (see Strict Standards)

Profile defaults are applied in different places during generation: build/test/audit settings flow into generated task and preflight content, while security, AI-avoidance, and strict guidance are injected through the orchestrator and exit-criteria templates rather than directly into task.md.

AI avoidance rules

When [style] avoid_ai_patterns = true (the default), each language profile injects five AI avoidance rules:

RuleDescription
Slop vocabularyAvoid words like “robust”, “leverage”, “comprehensive”, “delve”, “embark”
Filler phrasesSkip “it’s worth noting that”, “at its core”, “let’s break this down”
Prompt leakageNever echo instructions or write “As an AI…” in code or comments
Natural writingUse direct, human language — not corporate jargon
Self-documentingPrefer meaningful names over excessive comments

Comment guidelines

Profiles also include language-specific comment guidance. Common themes include:

GuidelineDescription
WHY not WHATPrefer comments that explain reasoning, tradeoffs, or intent rather than restating the code
Preserve important annotationsKeep existing structured comments such as safety or security annotations when the target language and codebase use them
Delete tutorial commentsRemove temporary instructional or step-by-step comments after implementation
Use project conventions for TODOsFollow the repository’s existing TODO/FIXME ownership and formatting conventions
Doc contractsDocument preconditions, postconditions, and error cases when they are important to callers

Overriding defaults

You can override any default preflight command in your plan’s [preflight] section. Language profile defaults are used only when no explicit override is provided.

[preflight]
build = "cargo build --workspace --release"
audit = "cargo audit --deny warnings"

To disable the audit entirely, set it to an empty string:

[preflight]
audit = ""

Generated Artifacts

When you run wiggum generate, the following artifacts are produced in your project directory.

Universal artifacts (always emitted)

These files are produced regardless of the selected target set. They are tool-agnostic and form the shared state of the loop.

Task files — tasks/T{NN}-{slug}.md

Each task becomes a numbered markdown file with a consistent structure:

  • Goal — What the task accomplishes
  • Dependencies — Which tasks must be complete first
  • Project Context — Where this fits in the architecture
  • Implementation — Guidance, file paths, type signatures, code snippets
  • Tests — What to test and where
  • Security — Six non-negotiable OWASP rules from the language profile, always present
  • Preflight — Commands to run before marking complete (build, test, lint, and security audit)
  • Exit Criteria — Verifiable conditions for completion, including a cargo audit (or equivalent) check

Capability contracts — capabilities/<name>.md

Only emitted when the plan declares one or more [[capabilities]] entries. Each capability renders to its own file under capabilities/, containing the title, description, prose requirements, and structured scenarios (WHEN/THEN). Tasks reference capabilities via implements = ["<name>"], which inlines the relevant scenarios into the task file under ## Implements. The IMPLEMENTATION_PLAN.md links out to every capability file from a new ## Capabilities section. See Capabilities for the full reference.

Progress tracker — PROGRESS.md

A markdown table tracking all phases and tasks with status columns:

StatusMeaning
[ ]Not started
[~]In progress
[x]Complete
[!]Blocked

Includes a Codebase State section where subagents record which files were created or modified. This gives each subsequent subagent accurate context about what the previous one actually changed.

Includes a Completion Standard section that travels verbatim with every subagent dispatch. By default this is a generic bar (no placeholders, no lint suppressions, preflight clean, tests added, progress doc updated) — override per project with [style] completion_standard.

Includes a learnings column where the orchestrator records insights from each completed task.

Implementation plan — IMPLEMENTATION_PLAN.md

A high-level architecture document derived from your plan’s project description and phase structure. Subagents reference this for context about how their task fits into the overall project.

Agents manifest — AGENTS.md

Defines agent roles and capabilities. Auto-discovered by opencode (and any other tool that follows the AGENTS.md convention). Can be skipped with --skip-agents-md.

Feature registry — features.json

A structured JSON file listing every task with its pass/fail state and per-criterion results. Both the orchestrator and evaluator reference this as the source of truth for what is actually complete.

{
  "project": "my-app",
  "tasks": [
    {
      "id": "T01",
      "slug": "project-setup",
      "title": "Initialize project structure",
      "passes": false,
      "criteria": [
        { "label": "build succeeds", "passes": false },
        { "label": "all tests pass", "passes": false },
        { "label": "linter clean", "passes": false },
        { "label": "implementation matches goal", "passes": false }
      ]
    }
  ]
}

Custom criteria can be added per-task via the evaluation_criteria field in your plan TOML.

Auto-injected security hardening task

When your plan contains web-facing surface (task slugs or titles containing http, api, server, webhook, upload, auth, etc.), Wiggum automatically appends a security-hardening task as the final task in the resolved task list. This task has pre-populated hints, test_hints, must_haves, and evaluation_criteria covering all six OWASP baseline categories.

Suppress with [security] skip_hardening_task = true in your plan, or by including your own task with the slug security-hardening. See Security for details.

Per-target artifacts

Wiggum emits tool-specific agent prompts and configuration based on the active [targets] set. See Targets for how to select targets.

VSCode target — .vscode/*.prompt.md

FileRole
.vscode/orchestrator.prompt.mdThe agent-mode prompt that drives the implementation loop. It tells the orchestrator how to read progress, spawn subagents, verify their output independently, and update the tracker. Includes a sprint contract step, a codebase state handoff step, and a guard against premature completion.
.vscode/evaluator.prompt.mdOnly generated when [evaluator] is configured. Defines a skeptical QA agent that re-runs preflight independently, scores each exit criterion, and updates features.json with verified results.
.vscode/planner.prompt.mdAn agent-mode prompt for the planning phase. The planner subagent assists with breaking down new work items, estimating complexity, and suggesting task decompositions — without touching the implementation.
.vscode/background-auditor.prompt.mdA continuously running QA companion that watches for regressions while the orchestrator advances through tasks.

These prompts use GitHub Copilot’s runSubagent tool to dispatch subagents.

opencode target — .opencode/agents/*.md

FileRole
.opencode/agents/orchestrator.mdSingle-file primary agent (mode: primary). Contains both <ORCHESTRATOR_INSTRUCTIONS> and <SUBAGENT_PROMPT> blocks. Dispatches the built-in general subagent via the task tool with subagent_type: "general" and the inline subagent body as prompt.
.opencode/agents/evaluator.mdQA subagent (mode: subagent). Only generated when [evaluator] is configured.
.opencode/agents/planner.mdSubagent for the planning phase.
.opencode/agents/background-auditor.mdSubagent for continuous cross-task regression watching.
ORCHESTRATOR.md (project root)Long-form workflow reference document with the task state machine, agents table, evaluator rubric, completion standard, and failure-mode recovery. Anyone (human or fresh LLM) joining mid-stream reads this to orient.

The orchestrator prompt is single-file: it embeds the subagent body inline as a <SUBAGENT_PROMPT> block and dispatches subagent_type: "general". There is no separate wiggum-implementer.md agent to maintain.

The agent frontmatter declares permissions — the orchestrator runs with permissive defaults (edit: allow, bash: allow, task: allow, todowrite: allow, webfetch: ask) so it can independently run preflight and dispatch subagents. The planner and background-auditor carry their own narrow permissions. See Targets for the full permissions matrix.

Claude target — CLAUDE.md + .claude/settings.json

The Claude target gives Claude Code full project context at session start, plus a companion hook that protects active work. Two files:

  • CLAUDE.md (repo root) — Claude Code’s project memory file, loaded on every session. It contains the project persona, preflight commands, architecture rules, user-defined rules, security rules, AI-avoidance guidance (when [style] avoid_ai_patterns = true), and the workflow loop. Claude Code IS its own orchestrator — wiggum supplies context + rules, Claude Code drives dispatch.
  • .claude/settings.json — A PreCompact hook that blocks context compression while any in-progress task marker ([~]) exists in PROGRESS.md. This prevents Claude from compacting away active working state mid-task, preserving the full task context until the task is marked complete.

agent-rules target — .cursorrules, .windsurfrules, .github/copilot-instructions.md

The agent-rules target emits three fork-neutral rules files from a single shared template, so the rules stay in lockstep across forks. It is designed for VSCode-family IDEs that do not speak the GitHub Copilot runSubagent or opencode task protocols.

FileRead by
.cursorrulesCursor (project-level rules)
.windsurfrulesWindsurf (project-level rules; same format as .cursorrules)
.github/copilot-instructions.mdGitHub Copilot (repo-level instructions); also picked up by some VSCode forks

Each file contains the project metadata, preflight, architecture, user rules, security rules, AI-avoidance guidance (when enabled), strict rules (when enabled), strategy, and commit conventions — no orchestrator-loop directives. The receiving IDE drives its own agent loop; wiggum never dispatches subagents on its behalf.

Why a separate target? The vscode target emits prompts that call #tool:agent/runSubagent, which is GitHub Copilot Chat-specific. Cursor, Windsurf, and other VSCode forks do not implement that tool. Use the agent-rules target when targeting those forks.

Parallel groups

If tasks have no mutual dependencies, Wiggum identifies them as parallelizable and notes this in the generated progress tracker.

Targets

Wiggum can generate scaffold artifacts for one or more AI coding tools at the same time. Each “target” is a different way of running the orchestrator loop — the underlying plan, tasks, progress tracker, and features registry are shared, but the agent prompts and configuration differ.

Supported targets

TargetStable identifierAgent file(s)Dispatch mechanism
VSCode (default)vscode.vscode/orchestrator.prompt.md (and three siblings)GitHub Copilot runSubagent tool
opencodeopencode.opencode/agents/orchestrator.md (and three siblings) + ORCHESTRATOR.mdopencode task tool with subagent_type: "general" and inline subagent body
ClaudeclaudeCLAUDE.md (project memory) + .claude/settings.json (hooks)Claude Code reads both files on every session; PreCompact hook blocks compaction mid-task
agent-rulesagent-rules.cursorrules + .windsurfrules + .github/copilot-instructions.mdThe receiving IDE drives its own agent loop; wiggum supplies only rules + project context

You can enable any combination of targets for a single generate run.

Note on the vscode target: it targets GitHub Copilot Chat specifically (the #tool:agent/runSubagent tool). Other VSCode forks — Cursor, Windsurf, Antigravity, Trae, Cody, Cline, Roo Code, Continue — do not implement that tool. If you use one of those forks, enable the agent-rules target instead so its corresponding rules file (.cursorrules, .windsurfrules, etc.) gets written.

Selection

Targets are selected via the plan TOML or a CLI flag. The CLI flag always wins.

Plan-level: [targets]

[targets]
vscode   = true   # default if [targets] is absent
opencode = true
claude   = false

Each field is optional. When [targets] is absent entirely, the default is vscode = true and the others are false — this preserves the pre-[targets] behavior exactly.

When the [targets] section is present, only the fields you set take effect; absent fields are treated as false.

CLI: --target

wiggum generate plan.toml --target opencode          # just opencode
wiggum generate plan.toml --target all               # all four
wiggum generate plan.toml --target agent-rules       # Cursor / Windsurf / Copilot rules
wiggum generate plan.toml --target vscode,opencode   # not supported — single value

--target accepts a single value: vscode, opencode, claude, agent-rules, or all.

Precedence

  1. The --target CLI flag (if provided) overrides everything.
  2. Otherwise, the plan’s [targets] section.
  3. Otherwise, the default (vscode only) for back-compat.

If the resolved TargetSet is empty (every field explicitly false), wiggum generate errors out — at least one target must be enabled.

How the targets differ

VSCode target

  • Files: .vscode/orchestrator.prompt.md, .vscode/evaluator.prompt.md, .vscode/planner.prompt.md, .vscode/background-auditor.prompt.md.
  • Format: Each file is a Copilot prompt file with a YAML frontmatter (agent: agent, description:) and a body that includes a <SUBAGENT_PROMPT> block the orchestrator dispatches via #tool:agent/runSubagent.
  • Model selection: the [orchestrator].model field is rendered as a recommendation note in the prompt. The actual model is selected by the user in the Copilot chat picker.
  • Per-dispatch model: [orchestrator].subagent_model is passed as the model: argument on each runSubagent call.
  • Evaluator prompt is generated only when [evaluator] is configured.

opencode target

  • Files:
    • .opencode/agents/orchestrator.md — single-file orchestrator. Contains both <ORCHESTRATOR_INSTRUCTIONS> and <SUBAGENT_PROMPT> blocks; the orchestrator dispatches the built-in general subagent via the task tool with subagent_type: "general".
    • .opencode/agents/evaluator.md — QA evaluator. Only generated when [evaluator] is configured.
    • .opencode/agents/planner.md — task-decomposition planner.
    • .opencode/agents/background-auditor.md — continuous cross-task auditor.
    • ORCHESTRATOR.md (project root) — long-form workflow reference document with the task state machine, agents table, per-agent tooling matrix, evaluator rubric, completion standard, gates, session handoff protocol, and failure-mode recovery. Useful for any human or fresh LLM joining mid-stream.
  • Format: Each agent file is an opencode agent with full YAML frontmatter (description:, mode: primary|subagent, model: provider/model-id, permission:) followed by <PLAN>, <TASKS>, <PROGRESS>, <FEATURES>, <COMPLETION_STANDARD> reference blocks.
  • Single-file orchestrator: unlike the VSCode target (which has separate orchestrator + subagent files), the opencode orchestrator embeds the subagent body inline as a <SUBAGENT_PROMPT> block. The orchestrator passes that block as the prompt argument when invoking task(subagent_type="general", prompt=<SUBAGENT_PROMPT>). There is no separate wiggum-implementer agent file to maintain.
  • Permissions: the orchestrator runs with permissive defaults (edit: allow, bash: allow, task: allow, todowrite: allow, webfetch: ask) so it can independently run preflight and dispatch subagents. Subagents run as the built-in general subagent, which inherits the dispatcher’s permissions.
  • Evaluator agent is generated only when [evaluator] is configured.
  • Plan TOML path: the orchestrator prompt and ORCHESTRATOR.md reference the plan file as <your-plan>.toml — a placeholder the reader substitutes with their actual filename. wiggum doesn’t guess the filename from the project name.

Claude target

  • Files: CLAUDE.md (project memory at repo root) and .claude/settings.json (hooks).
  • CLAUDE.md — Claude Code reads this file on every session. It contains the project persona, preflight commands, architecture rules, user-defined rules, security rules, AI-avoidance guidance (if enabled), and a workflow loop. Claude Code IS its own orchestrator; wiggum just supplies the context + rules it needs.
  • Hook: PreCompact blocks context compression while any [~] task exists in PROGRESS.md.
  • Combined, the two files constitute “full Claude Code support” — wiggum drives neither the loop nor the dispatch; Claude Code does.

agent-rules target

  • Files: .cursorrules, .windsurfrules, and .github/copilot-instructions.md. All three are emitted from a single shared template, so the rules stay in lockstep across forks.
  • Use case: VSCode forks that don’t speak the GitHub Copilot runSubagent or opencode task protocols — Cursor, Windsurf, Antigravity, Trae, Cody, Cline, Roo Code, Continue. Each of those IDEs reads its own format’s file as project-level instructions.
  • No orchestrator loop. Unlike vscode and opencode, these files contain rules + project context only. The receiving IDE drives its own agent loop; wiggum never dispatches subagents on its behalf.
  • GitHub Copilot reads .github/copilot-instructions.md as repository-level instructions — this works in VSCode + Copilot even when the vscode target is also enabled (the two are complementary).

Universal artifacts

The following files are always emitted, regardless of the active target set:

  • PROGRESS.md — the task tracker
  • IMPLEMENTATION_PLAN.md — the high-level plan
  • AGENTS.md — tool-agnostic agent instructions
  • features.json — structured task/criteria registry
  • tasks/T{NN}-{slug}.md — per-task files

Examples

Default (back-compat)

A plan with no [targets] section generates only the VSCode artifacts — exactly the pre-[targets] behavior.

wiggum generate plan.toml
# → .vscode/orchestrator.prompt.md
# → .vscode/evaluator.prompt.md   (if [evaluator] configured)
# → .vscode/planner.prompt.md
# → .vscode/background-auditor.prompt.md
# → .claude/settings.json          (when claude = true)

opencode-only

[targets]
vscode   = false
opencode = true
wiggum generate plan.toml
# → .opencode/agents/orchestrator.md          (single-file, embedded SUBAGENT_PROMPT)
# → .opencode/agents/evaluator.md             (if [evaluator] configured)
# → .opencode/agents/planner.md
# → .opencode/agents/background-auditor.md
# → ORCHESTRATOR.md

agent-rules-only (Cursor / Windsurf / Copilot)

[targets]
vscode      = false
opencode    = false
claude      = false
agent-rules = true
wiggum generate plan.toml
# → .cursorrules                    (Cursor)
# → .windsurfrules                  (Windsurf)
# → .github/copilot-instructions.md (GitHub Copilot)

Multi-target

wiggum generate plan.toml --target all
# → VSCode files AND opencode files AND CLAUDE.md + .claude/settings.json
#   AND .cursorrules + .windsurfrules + .github/copilot-instructions.md

Cleaning up

wiggum clean removes generated files for all targets, including the deprecated wiggum-*.md filenames from older wiggum versions. To clean only one target’s files, delete the relevant directory by hand (e.g. rm -rf .opencode).

Custom templates

.wiggum/templates/ overrides still work, with two layouts:

  • Flat (legacy): .wiggum/templates/orchestrator.opencode.md overrides the opencode variant.
  • Subdir (new): .wiggum/templates/opencode/orchestrator.md is also discovered and takes priority over the flat layout. Subdirs map to target names: vscode, opencode.

Custom template names that match the opencode variants:

Subdir layoutFlat layout
.wiggum/templates/vscode/orchestrator.md.wiggum/templates/orchestrator.md
.wiggum/templates/vscode/evaluator.md.wiggum/templates/evaluator.md
.wiggum/templates/vscode/planner.md.wiggum/templates/planner.md
.wiggum/templates/vscode/background_auditor.md.wiggum/templates/background_auditor.md
.wiggum/templates/opencode/orchestrator.md.wiggum/templates/orchestrator_opencode.md
.wiggum/templates/opencode/evaluator.md.wiggum/templates/evaluator_opencode.md
.wiggum/templates/opencode/planner.md.wiggum/templates/planner_opencode.md
.wiggum/templates/opencode/background_auditor.md.wiggum/templates/background_auditor_opencode.md

Note: there is no opencode/implementer.md template override — the opencode target uses a single-file orchestrator with the subagent body embedded inline. The legacy flat name implementer.md is no longer recognized.

Contributing

Contributions to Wiggum are welcome.

Building from source

git clone https://github.com/greysquirr3l/wiggum.git
cd wiggum
cargo build

Running tests

cargo test --workspace

Linting

cargo clippy --workspace -- -D warnings
cargo fmt -- --check

Code style

  • Rust edition 2024, MSRV 1.85
  • Strict clippy: pedantic, nursery, cargo, and perf lints as warnings
  • unwrap(), expect(), panic!(), and indexing with [] are denied — use Result and .get() instead
  • Dual licensed under MIT and Apache-2.0

Project structure

src/
├── adapters/    # CLI, filesystem, VCS, MCP server
├── domain/      # Plan model, DAG validation, language profiles, linting
├── generation/  # Template rendering, task/progress generation
├── error.rs     # Error types
├── ports.rs     # Port traits (hexagonal architecture)
├── lib.rs       # Library root
└── main.rs      # CLI entry point

Architecture

Wiggum follows a hexagonal (ports and adapters) architecture.

Layers

Domain (src/domain/)

Pure business logic with no I/O dependencies:

  • Plan model — The Plan struct parsed from TOML, with phases, tasks, and project metadata
  • DAG validation — Topological sort and cycle detection on the task dependency graph
  • Language profiles — Built-in profiles for 11 programming languages (Rust, Go, TypeScript, Python, Java, C#, Kotlin, Swift, Ruby, Elixir, PHP)
  • Lint rules — Plan quality checks (e.g., missing descriptions, unreachable tasks)

Ports (src/ports.rs)

Trait definitions for I/O boundaries:

  • PlanReader — Reading plan files from the filesystem

Adapters (src/adapters/)

Concrete implementations of ports and external integrations:

  • CLI — Clap-based command definitions
  • Filesystem — File reading/writing
  • VCS — Git integration for reports
  • MCP — Model Context Protocol server (stdio transport)
  • Init — Interactive plan creation
  • Bootstrap — Project detection and plan generation

Generation (src/generation/)

Template-based artifact rendering:

  • Task files — Tera templates producing T{NN}-{slug}.md files
  • Progress tracker — PROGRESS.md generation with parallel group annotations
  • Orchestrator prompt — Agent-mode prompt rendering
  • Implementation plan — Architecture overview generation
  • Token estimation — Approximate token counts for generated content

Data flow

Plan TOML → Parse → Validate DAG → Generate artifacts
                                      ├── PROGRESS.md
                                      ├── orchestrator.prompt.md
                                      ├── IMPLEMENTATION_PLAN.md
                                      ├── AGENTS.md
                                      └── tasks/T{NN}-{slug}.md