Stateful AI agent pipeline that turns task descriptions into review-ready branches
Project description
Sikula
Describe the task. Run Sikula. Come back to a working, tested branch — checked by independent agents and ready for human review.
Sikula is a stateful software engineering pipeline for real codebases, powered by specialized AI agents.
sikula run reads your codebase, writes the code, fixes build, test, and check failures, and iterates until done — committing the result to a dedicated branch. From the outside, it behaves as a single agent. Inside, it's a pipeline of specialized ones: analyst → planner → implementer → reviewer → security reviewer → test writer → build/fix loop. For multi-step tasks, implement/review/security/test phases run per step, then a final full-task gate checks the finished branch before final validation. The reviewer and security reviewer do not receive the implementer's reasoning — they review the task description, analyst prompt, changed files, and diff independently.
sikula review reviews an existing branch — report-only, or with --fix to apply corrections through the same build and fix loop.
Platforms: Android/Gradle, JVM backends (Spring Boot, Quarkus, Micronaut)/Gradle, JVM backends/Maven, Node.js/TypeScript/JavaScript, iOS/Xcode, Python, and Rust/Cargo are supported out of the box. The core orchestration and all agents are platform-neutral — supporting a new platform requires a BuildTool subclass and a project config YAML.
LLM providers: Codex, Claude, Gemini, and OpenCode are built in. Authenticate via CLI login or an API key in .env — no vendor lock-in.
Task description
│
▼
Analyst ───────────── reads codebase, produces implementation prompt
│
▼
Planner ───────────── SINGLE_PASS or ordered steps
│
├─ SINGLE_PASS
│ │
│ ▼
│ Implementer ◄────────── reviewer/security issues
│ │
│ ▼
│ Reviewer → Security Reviewer → Test Writer
│
└─ MULTI-STEP
│
▼
for each planned step:
Implementer → Reviewer → Security Reviewer → Test Writer
│
▼
Final full-task gate:
Reviewer → Security Reviewer → Test Writer
│
▼
Build / Fix loop ── compile → test → checks → fixer
│ │
│ └─ if fixer changes files:
│ validate again, then rerun review/security/test
▼
✓ Branch ready for human review
Watch Sikula turn a task into a branch
The task: add-search-by-name.md
adds an optional name query parameter to GET /countries, keeps the existing
region filter working, and returns [] when no countries match.
This is a full run on the JVM/Gradle Countries backend example: Sikula takes a written task, analyzes the codebase, implements name search, writes tests, catches a failing test case, fixes it, reruns independent review, security review, tests, and build validation, then commits the finished branch for human review.
# Describe the work in a task file, then run Sikula
sikula run .sikula/tasks/my-task.md
Quickstart
pipx install sikula
# Authenticate Codex — see §1 for other providers
codex login
Set up your project config — pick one path:
Auto-generate — sikula init scans your project and writes .sikula/config.yaml:
cd my-project/
# Detect build tool, language, platform, and source paths
sikula init
# Also generate a guidelines file (reads your codebase; biggest lever for output quality):
sikula init --provider codex --model gpt-5.5 --guidelines # adjust provider/model
# Or skip --guidelines and point guidelines.context_files at your existing architecture docs
Copy an example config and adapt it — faster when you already know what to fill in:
# Pick the example closest to your stack:
# Android: example/android/countries/.sikula/config.yaml
# iOS: example/ios/countries/.sikula/config.yaml
# JVM/Gradle: example/jvm/countries-gradle/.sikula/config.yaml
# JVM/Maven: example/jvm/countries-maven/.sikula/config.yaml
# Node/React: example/node/countries-react/.sikula/config.yaml
# Rust: example/rust/countries/.sikula/config.yaml
# Python: .sikula/config.yaml (this repo)
cp <example-config> my-project/.sikula/config.yaml
# update: project name, allowed_write_paths, build tasks, guidelines.context_files
Write and run a task (see §4 for what to include):
sikula run .sikula/tasks/my-task.md
Check the result:
sikula status
git diff main...sikula/<branch-name>
Tried Sikula? Open a feedback issue and tell us what worked, what failed, and whether the result was useful. Your stack and provider/model add helpful context.
Full setup: §1 One-time setup · Writing tasks: §4 Write a task · Working examples: §3 Try an example
Key features
- Stateful by design — every task records persistent state: prompts, outputs, review rounds, security findings, build errors, config snapshot, timestamps, and final result metadata
- Resumable long-running work — interrupted isolated tasks keep their worktree and can resume from completed phases instead of starting over
- Independent review loops — reviewer and security reviewer run separately from the implementer; blocking issues go back through the implementation loop before the task can finish
- Build-aware execution — Sikula compiles, tests, runs configured checks, and feeds failures to a fixer until the task passes or fails explicitly
- Precise scope control — the analyst reads your codebase before writing the implementation prompt; the reviewer verifies call sites, structured input contracts, completeness, and scope drift before code reaches you
- Fits existing git and CI workflows — output is a normal git branch and commit, ready for human review and whatever CI you already run
- Stack-flexible core — Android, iOS, JVM, Node.js/TypeScript/JavaScript, Python, and Rust are supported through platform-specific build tools; the orchestration stays the same
- Configurable and transparent — each phase can be enabled or disabled, each agent can use a different model/provider, and every run is inspectable with
sikula show <task-id>
Why a pipeline instead of a single agent?
Sikula is task-first rather than chat-first. It is designed for work you can hand off as a written task and later review as a branch, not for an open-ended conversation where the path from request to reviewed branch is hard to audit.
Many AI coding workflows focus on producing a code diff. Sikula optimizes for getting from a task description to an auditable branch that has passed the configured build, tests, checks, review, and security gates before it reaches you.
A single AI agent writing code and reviewing its own output is like asking a developer to approve their own PR — the same reasoning that produced the bug will miss it in review.
Sikula runs the reviewer and security reviewer as independent agents — neither had any part in the implementation. Each receives the task description, analyst prompt, changed files, and code diff as a fresh starting point. The reviewer verifies call sites and completeness, treating the analyst's scope claims as hypotheses rather than facts. The security reviewer checks independently for vulnerabilities introduced by the change.
The build loop closes the other gap: code that doesn't compile or pass enabled tests and checks is not accepted as a successful result. The fixer iterates until it does, or the task fails explicitly so you know what to fix.
Task descriptions may still name validation commands such as formatters, linters, tests, or report generators. Sikula treats those as acceptance criteria for the configured validation pipeline, not as arbitrary shell commands for agents to run manually.
When Sikula fits
- Well-defined tasks — adding a screen, an endpoint, a refactor with clear scope; the analyst reads your codebase and the reviewer verifies the implementer stayed on task
- Bug fixes — describe the expected behaviour and where it breaks; the analyst locates the root cause and the full pipeline ensures the fix compiles, passes tests, and doesn't introduce regressions
- Incremental development — stack tasks one on top of the other's branch; controlled scope and quality at every step
- Branch review — use
sikula reviewas an independent quality and security gate on any branch before merge - Multi-platform — the same task description (or a lightly adapted version) can drive implementations across platforms; the analyst reads each codebase independently and the implementer handles platform-specific details; run Android, iOS, web, and backend in parallel from one spec
1. One-time setup
Prerequisites: Python 3.10+, git installed, and your target project must be a git repository (Sikula uses git diff to detect file changes and generate diffs for the reviewer).
# Install pipx first if needed: https://pipx.pypa.io/stable/installation/
pipx install sikula
Sikula ships with built-in clients for:
CodexClient(provider: "codex") — calls thecodex execCLIClaudeClient(provider: "claude") — calls theclaude -pCLIGeminiClient(provider: "gemini") — calls thegemini -pCLIOpenCodeClient(provider: "opencode") — calls theopencode runCLI; model must be inprovider/modelformat (e.g.openai/gpt-5.3-codex)
To use a different model or provider, see Adding a new LLM provider and Per-agent LLM config.
codex login # Codex
claude login # Claude
gemini # Gemini
opencode auth login # OpenCode
Set up your project
Run from the project root — Sikula scans the codebase, detects the build tool, and writes .sikula/config.yaml:
cd my-project/
# Basic setup — detects build tool, language, platform, package manager, source paths
sikula init
# With LLM-generated guidelines — reads the codebase and writes .sikula/guidelines.md
sikula init --provider codex --model gpt-5.5 --guidelines
# You can also add generated guidelines later; existing config is preserved
sikula init --guidelines
sikula init auto-detects:
- Build tool, language, and platform family (Gradle Android/JVM, Maven/JVM, Cargo/Rust, Xcode/Swift, Python, Node.js/TypeScript/JavaScript)
- Node package manager (npm, pnpm, yarn, Bun) plus default sync/build/test/check commands from package scripts
- Xcode scheme when a shared iOS scheme is present
- Source and test directories for
allowed_write_pathsandallowed_test_write_paths - Existing guidance/docs to include as guidelines context (
AGENTS.md,guidelines.md,.github/copilot-instructions.md,ARCHITECTURE.md,README.md,CONTRIBUTING.md, etc.)
Anything that cannot be auto-detected is left as a TODO comment in the config. The output lists exactly which fields need manual attention before the first run.
--guidelines reads the entire codebase and writes .sikula/guidelines.md — a structured guide covering your architecture, naming conventions, and key patterns. It is automatically added to guidelines.context_files and received by every agent: the analyst, reviewer, and security reviewer get it pre-loaded; the implementer, fixer, and test writer read it via their file tools. It is the single biggest lever for output quality; review it, extend it based on what the reviewer catches, and keep it up to date. If .sikula/config.yaml already exists, sikula init --guidelines preserves the config and only writes .sikula/guidelines.md plus the missing guidelines.context_files entry. --provider and --model can be omitted when llm.provider and llm.model already exist in the config.
After sikula init, the .sikula/ directory contains:
.sikula/
config.yaml # project config — review TODOs before first run
guidelines.md # (--guidelines only) generated architecture guide — review and extend
tasks/ # store your task files here
.gitignore # excludes state/ and worktrees/ from source control
After filling in any TODOs, run your first task:
git add .sikula/config.yaml .sikula/.gitignore
# If you generated guidelines:
git add .sikula/guidelines.md
git commit -m "Add Sikula config"
Default isolated runs create a git worktree from HEAD, so .sikula/config.yaml and every file listed in guidelines.context_files must be committed first. If any of those files are missing, untracked, staged-only, or have unstaged changes, sikula run stops before creating the task. For a local experiment without committing, use --no-isolate.
sikula run .sikula/tasks/my_task.md
To adapt an existing project's config for a new project instead of running init, copy one of the example configs from example/*/*/.sikula/config.yaml.
Environment variables — .env
The orchestrator loads .env from the project root automatically at startup (via python-dotenv). Existing shell environment variables take precedence. sikula init adds .env to the project root .gitignore; when generating guidelines with Claude or Gemini, it also adds the provider settings directory that provider uses. Fill in any credentials your LLM provider or CLI requires.
2. Try Sikula on its own codebase
Sikula can run on itself — the repo is already configured in .sikula/config.yaml with agents/, core/, and tools/ as the writable sandbox.
Install the dev dependencies first (ruff and pytest are required for the build loop):
pipx inject sikula pytest pytest-cov ruff
A ready-to-run task file is included:
| Task file | What it does |
|---|---|
.sikula/tasks/status-emoji-icons.md |
Adds emoji icons to the human-readable sikula status table without changing JSON output |
The generated config uses
provider: codex— adjust thellm:section if you use a different provider.
# Run from the Sikula repo root — .sikula/config.yaml is auto-discovered
sikula run .sikula/tasks/status-emoji-icons.md
3. Try an example
The repo ships seven runnable example projects — each a countries browser or API built around the same domain data:
| Example | Stack | Data source | Config |
|---|---|---|---|
example/android/countries/ |
Kotlin, Jetpack Compose, Koin | REST Countries API | example/android/countries/.sikula/config.yaml |
example/ios/countries/ |
Swift, SwiftUI (iOS 17+), @Observable |
REST Countries API | example/ios/countries/.sikula/config.yaml |
example/jvm/countries-gradle/ |
Kotlin, Spring Boot, Gradle | local JSON dataset sourced from REST Countries | example/jvm/countries-gradle/.sikula/config.yaml |
example/jvm/countries-maven/ |
Kotlin, Spring Boot, Maven | local JSON dataset sourced from REST Countries | example/jvm/countries-maven/.sikula/config.yaml |
example/node/countries-bun-fullstack/ |
TypeScript, Bun full-stack | local TypeScript dataset sourced from REST Countries | example/node/countries-bun-fullstack/.sikula/config.yaml |
example/node/countries-react/ |
TypeScript, React, Vite | local TypeScript dataset sourced from REST Countries | example/node/countries-react/.sikula/config.yaml |
example/rust/countries/ |
Rust, Ratatui | local JSON file | example/rust/countries/.sikula/config.yaml |
Each example ships with ready-to-run task files. The Android and iOS tasks share the same specifications — the same task description drives both platforms; Sikula's agents handle the platform-specific implementation:
Task files live in .sikula/tasks/ inside each example project. Run from the example directory — the config is auto-discovered.
| Task | What it does | Android | iOS |
|---|---|---|---|
| Format population | Adds a formatted population string to the model and shows it in the list — minimal, single-pass, no new dependencies | .sikula/tasks/format-population.md |
.sikula/tasks/format-population.md |
| Pull-to-refresh | Adds pull-to-refresh to the countries list — a focused, single-pass change | .sikula/tasks/add-pull-to-refresh.md |
.sikula/tasks/add-pull-to-refresh.md |
| Country detail screen | Adds a full detail screen — multi-step: data & domain layer first, then presentation and navigation (Android also includes DI wiring with Koin) | .sikula/tasks/add-country-detail-screen.md |
.sikula/tasks/add-country-detail-screen.md |
The Rust CLI ships its own set of tasks suited to a local-data command-line tool:
| Task file | What it does |
|---|---|
.sikula/tasks/format-population.md |
Formats the population number with B/M/K suffixes |
.sikula/tasks/sort-list.md |
Adds sorting options to the country list |
.sikula/tasks/add-neighbours.md |
Shows neighbouring countries for a given country |
The Rust example also ships with ready-to-use extra_rules files in example/rust/countries/.sikula/. They are commented out in the config by default — uncomment the reviewer, security_reviewer, test_writer, and planner blocks in example/rust/countries/.sikula/config.yaml to activate them and see project-specific rules in action.
The Node/React example ships a TypeScript web UI with Vitest and React Testing Library already configured:
| Task file | What it does |
|---|---|
.sikula/tasks/add-search-by-name.md |
Adds a country name search control |
.sikula/tasks/add-country-detail-view.md |
Adds a country detail view with browser and in-app back navigation |
.sikula/tasks/format-population.md |
Formats the population number with B/M/K suffixes |
The Bun full-stack example ships a compact TypeScript app using Bun.serve,
strict TypeScript type checking, Bun browser bundling, and bun:test:
| Task file | What it does |
|---|---|
.sikula/tasks/add-search-by-name.md |
Adds a name search query to the API and browser UI |
.sikula/tasks/add-country-detail-view.md |
Adds a detail view backed by GET /api/countries/:code |
.sikula/tasks/format-population.md |
Formats list population values with B/M/K suffixes |
The JVM examples ship the same Spring Boot REST API and the same task set in both build systems. Use the Gradle or Maven variant depending on the backend stack you want to test:
| Task | What it does | Gradle | Maven |
|---|---|---|---|
| Search by name | Adds a name query parameter to the list endpoint |
.sikula/tasks/add-search-by-name.md |
.sikula/tasks/add-search-by-name.md |
| Sorting | Adds sort and order query parameters to the list endpoint |
.sikula/tasks/add-sorting.md |
.sikula/tasks/add-sorting.md |
| Population stats | Adds an endpoint with aggregate population statistics | .sikula/tasks/add-population-stats.md |
.sikula/tasks/add-population-stats.md |
The example configs use
provider: codex— adjust thellm:section if you use a different provider.
cd example/android/countries
sikula run .sikula/tasks/add-pull-to-refresh.md
4. Write a task
Create a Markdown task file. Plain text (.txt) is supported too, but .md is the recommended convention. Store it anywhere; a common location is .sikula/tasks/ alongside the project config.
# From the same directory as the task file
cd my-project/.sikula/tasks/
sikula run my_task.md
# Or use any path relative to CWD, or an absolute path
sikula run .sikula/tasks/my_task.md
sikula run /abs/path/to/my_task.md
Task file path resolution: absolute path → relative to CWD. Sikula auto-discovers .sikula/config.yaml by walking up from CWD, so you can run from any subdirectory of the project.
Focus on requirements and intent. The analyst explores the codebase and works out the implementation details — include anything it cannot infer on its own: API endpoints, third-party service constraints, business rules, out-of-scope items. See example/android/countries/.sikula/tasks/add-country-detail-screen.md for a real runnable example, or Writing Sikula Tasks for optional task-writing guidance.
The task file must be self-contained — the analyst's tools are limited to reading files in your project. URLs, Jira tickets, Figma links, and other external references cannot be fetched and will be ignored.
When the task introduces new API calls, include the complete response contract: whether the endpoint returns a single object or a collection, and for any new data types the response introduces that don't already exist in the project, the field names and their types. The analyst cannot verify external contracts; without this information the implementer will have to guess.
For any new user-visible text (labels, titles, messages, button text), include string values in the task description — either as prose ("the screen title should be 'Country Detail'") or as an explicit Strings: section:
Strings:
- country_detail_title: "Country Detail"
- country_detail_population: "Population: {count}" (count: Long)
If your project uses a translation management tool (Phrase, Lokalise, etc.), always specify the exact string keys — the analyst cannot invent keys that match your translation workflow. For single-platform projects you can use platform-native string notation directly and the analyst will use it as-is.
5. Run
Run a task against your project (from the project directory; .sikula/config.yaml is auto-discovered):
sikula run my_task.md
# Or pass the config explicitly (e.g. from outside the project directory):
sikula --config /path/to/.sikula/config.yaml run my_task.md
| Agent | What it does |
|---|---|
analyst |
Reads the codebase and task; produces an implementation prompt with exact file paths |
planner |
Decides whether to run single-pass or split the task into ordered steps |
implementer |
Writes the code changes |
reviewer |
Read-only review for correctness, completeness, structured input contracts, dead code, and contract-bearing test weakening; issues are fed back to the implementer |
security_reviewer |
Read-only security review; blocking issues are fed back to the implementer; warnings are logged only |
test_writer |
Writes or updates unit tests after review/security phases complete, including positive/negative contract matrices for structured input |
fixer |
Fixes build, test, and check failures; test failures and test-origin validation failures start test-only and require production-vs-test triage before any production fix |
A few common one-off overrides (without editing the config YAML — see the full flag reference below):
# skip compile/test/check validation for this run
sikula run my_task.md --no-build
# force single-pass for this run (skip planner even if config has run_planner: true)
sikula run my_task.md --no-planner
# use a stronger model for the analyst and a different provider for the implementer
sikula run my_task.md \
--agent-model analyst=gpt-5.5 \
--agent-provider implementer=claude --agent-model implementer=claude-sonnet-4-6
Full loop — enable phases in config as needed:
# .sikula/config.yaml
run_presync: true # run BuildTool.generate_sources() before the analyst — ensures
# build-generated sources (OpenAPI DTOs, etc.) exist in build/
run_planner: true # triage + split: SINGLE_PASS for focused tasks; 2-N ordered steps for larger ones
run_review: true # logical/completeness review after implement; issues fed back to implementer
run_security_review: true # security review after the review phase; still runs when run_review is false
# unless run_security_review is also false
run_test_writing: true # write/update unit tests after review/security phases complete
run_build: true # compile check; enables compile/test/check validation and fix loop
run_tests: true # run unit tests after each passing build
run_checks: true # run quality checks (lint, detekt, …) after tests; failures feed the fixer
run_build_per_step: false # build/fix once after all steps (true = after each step plus final build)
Every run_* key (and build.presync_clean) can be overridden per-run without editing the YAML. Flag omitted = use config value.
The full loop: presync → analyze → plan → implement → review → security review → test write → sync → build → test → checks → fix → ... until done or the active build/fix loop reaches sandbox.max_iterations. In multi-step runs, implement → review → security review → test write runs for each planned step, then a final full-task gate runs before final build/fix validation. After a build/test/check failure, the fixer can iterate directly against deterministic validation; reviewer, security reviewer, and test writer rerun only after build/test/check are green again, and any changes they make are validated by another build/test/check pass. Test failures, and build/check diagnostics that reference only test files or recognized test targets, start with a test-only fixer pass. If that pass reports production_defect + production_code without changing files, Sikula runs a second production-enabled fixer pass; production writes during the first pass fail the task. Every phase except analyze and implement is optional — controlled by run_* flags.
Long-running phases publish an active-operation heartbeat while they are in progress.
By default Sikula logs a "Still running" line every 60 seconds and updates task state
so sikula status --verbose and sikula status --json can show the current agent
or validation phase during long CI runs. Configure this under progress::
progress:
heartbeat_interval_seconds: 60 # set to 0 to disable
run_build_per_step: true runs the build/fix loop after each individual step; multi-step runs still get the final full-task gate and final build/fix loop after all planned steps complete. Each per-step loop and the final full-task loop gets its own max_iterations budget, while build_iterations remains a total audit counter. Leave it false unless you explicitly want every step physically built; planner steps should still keep immediate compile dependencies together, such as resource or localization keys, route/API/command constants, service registrations, and interface implementations. Build-fix reviews during per-step builds stay scoped to that step; build-fix reviews in the final phase are scoped to the complete task, not the last planned step. See ARCHITECTURE.md for a detailed description of the planner and the step loop.
The sync step calls BuildTool.sync() once before the first build and again whenever the fixer changes a build-config file. It resolves dependencies and generates any required sources. A sync failure is treated like a build failure — the error is passed to the fixer and the loop continues.
Build, test, and check commands are expected to validate the repository, not leave new source files or mutate task output. Sikula snapshots non-ignored dirty files before each command, restores any unexpected command-produced repository artifacts afterward, and records that cleanup in task state. Ignored build outputs and caches are left alone. If cleanup itself fails, the validation step fails and the normal fixer loop gets the diagnostic.
Validation command coverage: task descriptions often mention validation commands such as formatters, linters, tests, or report generators. Sikula treats those as acceptance criteria for the configured pipeline, not as shell commands for agents to run manually. The reviewer sees the effective build/test/check commands from the Sikula config file (auto-discovered .sikula/config.yaml by default, or the file passed with --config). That configured validation pipeline is what Sikula can execute. For sikula run, validation command coverage is the preflight/reviewer check that task-described commands are represented by that pipeline.
To make a command count as task-described validation, write it explicitly: in backticks, in a shell code fence, with a $ prompt, or as a command list under a heading/prefix such as Verification: or Run:; Markdown blank separator lines after the heading are allowed. Bare tool names such as cargo or npm are not treated as validation commands. If a sikula run task command is not covered there, it is reported as a validation coverage gap so you can add the same command to the effective build/test/check config or adjust the task before rerunning. In sikula review modes, commands found in PR/review text are informational branch-verification context and do not preflight-abort review/fix.
A generic command from the same tool family is not enough when the task specifies materially different flags, targets, scripts, packages, schemes, or paths. Gradle/Maven wrapper spelling for the same invocation (./gradlew vs gradle, ./mvnw vs mvn) is treated as equivalent, as are Python module forms (python -m pytest vs pytest, python -m ruff vs ruff), the npm test shortcut (npm test vs npm run test), and pnpm/Yarn package-script shorthands for common validation scripts (pnpm typecheck vs pnpm run typecheck, yarn lint vs yarn run lint). Different tasks, scripts, goals, or flags are not. For run-task validation coverage gaps, Sikula does not ask the implementer to edit the pipeline config inside the current task, because the effective pipeline is loaded before the agent loop starts.
Isolation (default on): each run creates a git worktree in .sikula/worktrees/<task-id>/ (under the git root) and a branch sikula/<task-stem>-<task-id>. On success the changes are committed to that branch and the worktree is removed. On failure the worktree is preserved for inspection and resume. .sikula/worktrees/ is added to .git/info/exclude automatically on the first run (local, not committed). Use --no-isolate to run directly in the project directory without creating a branch.
Parallel runs: because each task uses its own worktree, multiple Sikula processes can run simultaneously against the same project without conflicting. Start each in a separate terminal with its own task file.
Stacking tasks: to build task 2 on top of task 1's changes, check out the sikula/<task1> branch before running task 2. The new worktree will branch from that commit and inherit all of task 1's changes.
What Sikula sees: the worktree starts from HEAD — only committed changes are visible. Staged or unstaged changes in your working directory are not carried over. Commit any changes you want the agent to see before running. .sikula/config.yaml and files listed in guidelines.context_files are enforced: for isolated runs they must exist, be tracked, and be clean relative to HEAD before Sikula creates the worktree. Other files referenced only by the task description, such as design mockups, screenshots, or specs, are not enforced; commit them if you want the analyst to read them. (With --no-isolate the agent runs directly in the working directory and sees all files regardless of git status.)
Phase flags (--flag enables, --no-flag disables):
| Flag | Overrides | Effect |
|---|---|---|
--no-isolate |
— | Skip worktree creation; run directly in the project directory. A git repository is still required. |
--build / --no-build |
run_build |
Enable/disable compile/test/check validation and the fix loop |
--presync / --no-presync |
run_presync |
Enable/disable pre-analyze source generation |
--presync-clean / --no-presync-clean |
build.presync_clean |
Run clean before the presync task |
--planner / --no-planner |
run_planner |
Enable/disable planner (task splitting) |
--review / --no-review |
run_review |
Enable/disable reviewer |
--security-review / --no-security-review |
run_security_review |
Enable/disable security reviewer |
--test-writing / --no-test-writing |
run_test_writing |
Enable/disable test writer |
--tests / --no-tests |
run_tests |
Enable/disable running tests after build |
--build-per-step / --no-build-per-step |
run_build_per_step |
Also build/fix after each step; final full-task build still runs |
--checks / --no-checks |
run_checks |
Enable/disable quality checks after tests |
Per-agent LLM flags (repeatable; agent accepts _ or -; valid agents: analyst, planner, implementer, reviewer, security_reviewer, test_writer, fixer):
| Flag | Overrides | Example |
|---|---|---|
--agent-model AGENT=MODEL |
agents.<agent>.llm.model |
--agent-model analyst=gpt-5.5 |
--agent-provider AGENT=PROVIDER |
agents.<agent>.llm.provider |
--agent-provider implementer=claude |
--agent-timeout AGENT=SECONDS |
agents.<agent>.llm.agent_timeout |
--agent-timeout implementer=2400 |
CLI values layer on top of agents: overrides in the project YAML.
Additional config keys (all optional unless noted):
| Section | Key | Default | Description |
|---|---|---|---|
project |
root_path |
. |
Project root; "." (the default) resolves to the directory containing .sikula/config.yaml; use an absolute path only when the config lives outside the project tree |
project |
build_tool |
"gradle-android" |
BuildTool selection: "gradle-android" (Android/Gradle), "gradle-jvm" (JVM backend/Gradle), "maven" (Maven), "node" (NodeTool for TypeScript/JavaScript), "python" (PythonTool), "cargo" (CargoTool), or "xcodebuild" (XcodeTool) |
project |
platform |
— | Target platform (e.g. Android, iOS); injected into agent prompts |
project |
language |
— | Tech stack language (e.g. Kotlin, Python); injected into agent prompts |
project |
ui |
— | UI framework (e.g. Jetpack Compose); injected into agent prompts |
planner |
max_steps |
8 |
Maximum number of steps the planner may produce |
planner |
extra_rules |
— | Path (relative to project root) to a Markdown file appended to the planner's system prompt as ## Project-specific rules. Use for task-splitting conventions: which concerns to split, which compile dependencies must stay atomic, and which concerns to keep together. Has no effect on what individual agents do — only influences how the planner divides the implementation prompt into steps. |
reviewer |
extra_rules |
— | Path to a Markdown file appended to the reviewer's system prompt. Use for project-specific correctness checks: thread safety requirements, mandatory invariants, architecture constraints. Does not affect implementation — the reviewer is read-only. |
security |
context |
— | Short description of what the application does, what data it handles, and who the users are. Injected into the security reviewer's prompt so it can focus on threats relevant to your project — e.g. "Mobile app. Fetches user data from our backend — auth tokens in Keychain. Main concerns: token handling and API response validation." See Configuring the security reviewer. |
security_reviewer |
extra_rules |
— | Path to a Markdown file appended to the security reviewer's system prompt. Use for project-specific security requirements: compliance rules (GDPR, PCI), threat model specifics, data classification rules. Appended before the BLOCKING/WARNING categories — project rules take priority. |
test_writer |
coverage_target |
90 |
Minimum branch+line coverage % for new/changed code within the configured test surface |
test_writer |
test_surface_policy |
existing_infrastructure |
Test surface the test writer should use: existing_infrastructure stays within existing project test infra and does not treat missing heavy UI/browser/device/runtime harnesses as gaps by themselves; complete opts in to TESTABILITY GAP reports when important behaviour needs missing test infra outside the existing surface |
test_writer |
testability_gap_policy |
warn |
What to do when the test writer reports behaviour that cannot be safely tested with available seams/infra: warn records a visible audit warning; fail fails the task |
test_writer |
extra_rules |
— | Path to a Markdown file appended to the test writer's prompt. Use for project-specific testing conventions: required test doubles, naming patterns, parametric table rules. |
progress |
heartbeat_interval_seconds |
60 |
Seconds between heartbeat updates; 0 disables the heartbeat |
guidelines |
context_files |
[] |
Files loaded as guidelines context into agent prompts (relative to project root); used by analyst, implementer, fixer, test writer, reviewer, and security reviewer |
guidelines |
max_file_chars |
— | Max characters read from each guidelines file |
All paths in the config are relative to the project root. project.root_path itself defaults to "." and is resolved relative to the config file's parent directory — use an absolute path only when the config lives outside the project tree.
Guidelines are the single biggest lever for output quality. The analyst reads them before writing a word of code — they define architecture, naming conventions, patterns to follow, and anti-patterns to avoid. A well-maintained
guidelines.md,AGENTS.md, or architecture doc produces implementations that fit your codebase; missing or vague guidance produces generic code that the reviewer will flag. Start with your existing agent/project docs and expand based on what the reviewer catches.
See ARCHITECTURE.md for the full config reference including build.* keys (compile_task, test_task, presync_task, timeouts).
Using extra_rules
extra_rules files are plain Markdown. Store them anywhere in your project — a .sikula/ subdirectory is a natural convention. Wire them up in your project config:
reviewer:
extra_rules: .sikula/reviewer_rules.md
security_reviewer:
extra_rules: .sikula/security_rules.md
test_writer:
extra_rules: .sikula/test_writer_rules.md
planner:
extra_rules: .sikula/planner_rules.md
The file content is appended to the agent's system prompt under a ## Project-specific rules heading with an explicit statement that project rules take priority over any conflicting defaults.
These settings apply across all commands that invoke the respective agents: reviewer and security_reviewer rules are active in sikula run, sikula review, and sikula review --fix; test_writer rules apply in sikula run and sikula review --fix; planner rules apply in sikula run only (the planner is always disabled in sikula review).
Example — .sikula/reviewer_rules.md:
- All public API methods must be documented in `docs/api.md`.
- Any change to a shared repository must verify that all ViewModels that inject it
are still passing the correct arguments after the change.
- Do not approve changes that introduce a new dependency without a corresponding
entry in `docs/dependencies.md`.
Example — .sikula/security_rules.md:
- This project processes payment data. Any field that may contain card numbers,
CVVs, or bank account details must never appear in logs, even as a partial value.
- All API endpoints under `/admin` require role `ADMIN` — flag any new endpoint
in that path that does not explicitly check for this role.
What belongs here vs guidelines.md: guidelines.md reaches every agent — the analyst and reviewers get the full content injected into their prompt; the implementer, fixer, and test writer receive it as filenames they read via file tools. extra_rules never reaches the implementer or fixer — use it for constraints that should apply at the planning, review, or test-writing stage without influencing what the implementer writes directly.
Configuring the security reviewer
The security reviewer runs after the review phase and checks for vulnerabilities introduced by the change. With the default run_review: true, that means after reviewer approval; if review is disabled for a run, security review still runs unless run_security_review is also disabled. It has a built-in list of blocking categories — hardcoded credentials, injection vulnerabilities, missing auth, weak crypto, PII in logs, path traversal, disabled TLS — but without project context it has to guess what matters most for your specific application.
Two config fields let you tune it:
security.context — tell the reviewer what your application is. One to three sentences covering what data it handles, who the users are, and where the trust boundaries lie. The reviewer uses this to focus on relevant threat categories and skip the ones that don't apply.
security:
context: Mobile app. Fetches user data from our backend API — auth tokens stored in EncryptedSharedPreferences. No PII beyond email; no financial data. Main concerns: token handling, API response validation, and navigation argument sanitisation.
Without this field the reviewer still catches the universal categories above, but has no way to judge what is high-risk for your specific application.
security_reviewer.extra_rules — add mandatory checks on top of the built-in categories. Use this for compliance requirements, data classification rules, or security invariants specific to your codebase.
security_reviewer:
extra_rules: .sikula/security_rules.md
The two fields are complementary: security.context tells the reviewer what the application is, extra_rules tells it what specific rules apply. A well-configured security reviewer is a meaningful gate — not just a pass-through.
6. Review an existing branch
sikula review runs the same reviewer and security reviewer on an existing branch without interrupting your working directory. It requires --description or --description-file; that text is the review scope, similar to a PR description. Project-specific rules (reviewer.extra_rules, security_reviewer.extra_rules) from the project config are honored in all modes; test_writer.extra_rules is honored in --fix mode when the test writer runs. See Using extra_rules in §5. In report-only mode the branch is isolated as a detached-HEAD worktree, so you can run it even while sitting on the branch being reviewed. In --fix mode a real branch checkout is used so that fixes can be committed back. The diff shown to reviewers is computed as git diff base...branch (three-dot — all commits introduced by the branch); in --fix mode it is refreshed before each reviewer pass so Sikula's own uncommitted fixes are included. Unlike the normal sikula run pipeline, review mode treats changed test files as branch output and reviews them for correctness and relevance. Test-writer changes made during --fix receive one final reviewer/security validation pass; if that pass rejects them, the task fails instead of entering another test-writing loop.
Report-only — branch is never modified:
sikula review \
--branch feature/login \
--base-branch main \
--description "Add login screen with JWT authentication"
# Pass the PR description from a file:
sikula review \
--branch feature/login \
--description-file login_pr.md
Runs ReviewerAgent, then SecurityReviewerAgent (only if review passes; controlled by --security-review / project config, default on). Prints the full review output and exits 0 (approved) or 1 (issues found). The worktree is removed on completion. Task state is saved — inspect with show <task-id>.
Fix mode (--fix) — applies suggested fixes to the branch:
sikula review \
--branch feature/login \
--base-branch main \
--description-file login_pr.md \
--fix
Uses the full orchestrator loop: reviewer finds issues → implementer fixes them → build and checks run per the project config. The planner is always disabled and the reviewer is always enabled for review --fix. Gitignored build files (e.g. local.properties on Android) are copied into the worktree automatically — same as sikula run. If the reviewer approves without any fixes, build, tests, and checks are skipped — the branch is assumed to be already CI-validated. On success with fixes, changes are committed to the PR branch with the message sikula: review fixes for <branch>; if no fixes were needed, the worktree is simply removed. On failure, the worktree is preserved at .sikula/worktrees/<task-id>/ (under the git root) for inspection. Resume is supported via sikula run --task-id <id> — Sikula reuses the task state and worktree, keeps planner disabled and reviewer enabled, and reuses the original security-review setting unless you override it with --security-review / --no-security-review.
# Review with a stronger model for both reviewers:
sikula review \
--branch feature/login \
--description-file login_pr.md \
--agent-model reviewer=gpt-5.5 \
--agent-model security_reviewer=gpt-5.5
Options:
| Flag | Default | Description |
|---|---|---|
--branch |
required | Branch to review (must already exist) |
--base-branch |
main |
Base branch to diff against |
--description TEXT |
required* | PR description — scope and context for the reviewer |
--description-file FILE |
— | Path to a file containing the PR description |
--fix |
off | Apply fixes: run implementer on review issues and commit to the branch |
--security-review / --no-security-review |
from config (true) |
Enable/disable SecurityReviewerAgent |
--agent-model AGENT=MODEL |
— | Override model for one agent (repeatable) |
--agent-provider AGENT=PROVIDER |
— | Override provider for one agent (repeatable) |
--agent-timeout AGENT=SECONDS |
— | Override timeout for one agent (repeatable) |
*Use either --description or --description-file.
7. Check results
# List all tasks (sorted oldest → newest)
# STATUS: DONE, FAILED, CLEANED, INTERRUPTED, or the current phase/active heartbeat
# STEP shows current planner step; BUILD shows build/fix iterations
sikula status
# Include the next useful command for each task
sikula status --verbose
# Filter the list
sikula status --active
sikula status --failed
sikula status --cleaned
sikula status --done
# Machine-readable task overview
sikula status --json
# Show full task state as JSON (includes config_snapshot with all effective run settings)
sikula show <task-id>
# See what the agent changed (branch name is printed in the run summary)
git diff main...sikula/<task-stem>-<task-id>
# Merge into your working branch or open a PR
git merge sikula/<task-stem>-<task-id>
# or: git push origin sikula/<task-stem>-<task-id> && gh pr create ...
Sikula state commands such as status and show can also be run from inside a
preserved task worktree; Sikula resolves task state from the original project, not
from the worktree copy.
Start fresh tasks from the original project, not from inside another task worktree.
Use sikula run --task-id <task-id> to resume the current task instead.
cleanup --force and delete --force must be run from outside the worktree they
would remove.
The config_snapshot field in the state JSON records every effective setting used for the run: project name, all run_* flags (including run_checks), max_iterations, max_review_iterations, max_security_review_iterations, progress.*, sandbox.* paths (allowed_write_paths, allowed_test_write_paths, allowed_read_paths), build.* settings (presync task, compile task, timeouts, checks list), and the resolved provider/model/agent_timeout for each agent. If extra_rules is configured for an agent, its path is also captured in the snapshot. It is written once at the start of the first run and never overwritten on resume, so it always reflects the original run's configuration.
Terminal task state also records finished_at, result_commit when Sikula creates a commit, and final test_status / check_status values (success, failed, or skipped) for audit and debugging.
Privacy note: the state JSON contains full LLM prompts and outputs, which may include your task description, source code excerpts, inlined guidelines content, and build error output. Before sharing a state file in a bug report or publicly, review it and redact any proprietary or sensitive content.
8. Resume an interrupted task
sikula run --task-id <task-id>
Resume applies to sikula run tasks and sikula review --fix tasks. Report-only sikula review does not support resume — re-run it to start fresh.
Resume works for tasks that were interrupted mid-run (process killed, timeout, crash). Each phase has a guard flag in the state so already-completed phases are skipped automatically.
Tasks that explicitly failed (e.g. implementer produced no file changes in single-pass mode, max iterations reached) are in a terminal failed state and cannot be resumed directly. Use --reset-failed to clear the failed flag and retry:
sikula run --task-id <task-id> --reset-failed
--reset-failed also clears any pending error blobs (errors, test_errors, check_errors) so the fixer doesn't see stale failures from before the reset on the first resumed iteration. It also inspects the current git diff in the worktree (or project root for --no-isolate runs): if files_changed is empty but files within allowed_write_paths are already dirty (e.g. the implementer ran but change detection produced a false negative), those files are written into files_changed so the orchestrator skips the implement phase and proceeds directly to review → test_writer → build.
For isolated tasks the worktree is preserved on failure so resume works out of the box — the worktree path is stored in the task state and reused automatically when you pass --task-id. The currently checked-out branch in your working directory is irrelevant for resume (including --reset-failed): the orchestrator always works inside the task's own worktree on its dedicated sikula/<task> branch. Only a fresh run with a new task file is affected by which branch you have checked out, because the new worktree branches from the current HEAD.
Cleaning Up Task Worktrees
Failed or interrupted isolated runs keep their git worktree so you can inspect changes,
resume the task, or recover a patch manually. When you no longer need that workspace,
use cleanup:
sikula cleanup <task-id> # dry run; prints what would be removed
sikula cleanup <task-id> --force # remove the preserved worktree, keep state JSON
cleanup preserves the state JSON and records a cleanup entry in history, so sikula show
still works for audit/debugging. Resume is no longer possible after the worktree is removed.
To remove both the worktree and the state JSON:
sikula delete <task-id> # dry run
sikula delete <task-id> --force # remove worktree and state JSON
Dirty worktrees are protected by default. If the worktree has uncommitted changes, cleanup and delete fail unless you explicitly acknowledge data loss:
sikula cleanup <task-id> --force --discard
sikula delete <task-id> --force --discard
9. Sandbox — what agents are allowed to do
Agents operate within several layers of protection:
Git isolation — by default each run works in a dedicated git worktree and branch
(sikula/<task-stem>-<task-id>). All changes are visible via git diff before you merge.
Nothing reaches your main branch without a deliberate merge. With --no-isolate the changes
land as uncommitted working-tree modifications — equally visible and equally under your control
before any commit.
Filesystem scope — agents run with cwd=project_root, anchoring all relative paths
to the project. Workspace boundary enforcement depends on the provider — see the sandbox notes
under each provider in § Adding a new LLM provider. allowed_read_paths and
allowed_write_paths subdirectory restrictions within the workspace are passed as constraints
in the agent prompt; they are not enforced at the OS level.
After each write-capable agent call, Sikula compares the files reported by that call with
the active write path list and records a non-blocking write_path_warning in task history
when a file falls outside it. Inspect with sikula show <task-id>.
This is an audit signal, not pipeline control flow: it does not fail the task and it only
covers files reported by the provider's run_agent() result.
Sandbox config — defined in .sikula/config.yaml under sandbox:
| Key | Used by | Purpose |
|---|---|---|
allowed_write_paths |
ImplementerAgent, FixerAgent (build errors, production-confirmed test fixes, check errors) | Production source directories agents may write to |
allowed_test_write_paths |
TestWriterAgent, FixerAgent (test-only test-failure pass, production-confirmed test fixes, check errors) | Test source directories; agents may write here when fixing malformed/stale tests or check violations (e.g. detekt) |
allowed_read_paths |
ImplementerAgent, FixerAgent, TestWriterAgent | Directories agents may read from (prompt constraint); "." means the entire project root |
max_iterations |
Orchestrator build/fix loop | Max attempts per active build/fix loop before the task is aborted |
max_review_iterations |
Orchestrator review loop | Max review+implement-fix cycles before task is aborted |
max_security_review_iterations |
Orchestrator security review loop | Max security-review+fix cycles (independent of max_review_iterations); default equals max_review_iterations if not set |
Bash is constrained by Sikula's agent prompts to read-only commands (grep, find,
ls) plus git rm for tracked file deletion — no rm, mv, or other destructive shell
commands. Provider-level enforcement varies below; where git rm is used, deletions are
tracked, visible in git diff, and reversible.
Enforcement varies by provider:
| Provider | Write agents (bash restriction) | Read-only calls |
|---|---|---|
CodexClient |
prompt-level — CLI does not support per-command filtering | file writes blocked by --sandbox read-only; shell command filtering is prompt-level |
ClaudeClient |
technically enforced via --allowedTools |
technically enforced via --allowedTools |
GeminiClient |
prompt-level — run_shell_command is in tools.core but agent is instructed to limit its use |
technically enforced — run_shell_command excluded from tools.core |
OpenCodeClient |
prompt-level — CLI does not support per-command filtering | technically enforced via bash: deny in OpenCode config |
Network access is forbidden by Sikula's agent prompts via AGENT_SECURITY_PREFIX —
agents are instructed not to make network requests or access external services. Provider-specific
tool restrictions may further reduce network-capable shell/tool access, but Sikula does not rely on
an explicit provider-level network-deny setting.
10. Per-agent LLM config
Each agent can use a different model, provider, or timeout. All five fields (provider, model,
max_tokens, temperature, agent_timeout) can be overridden per agent. Any field omitted
falls back to the top-level llm: section:
llm:
provider: codex
model: gpt-5.3-codex
agent_timeout: 1800 # seconds; default for all agents
agents:
analyst:
llm:
model: gpt-5.5 # stronger model: analyst output determines the entire task outcome
reviewer:
llm:
model: gpt-5.5 # stronger model: thoroughness matters more than speed here
security_reviewer:
llm:
model: gpt-5.5 # stronger model: must reliably detect subtle security issues
implementer:
llm:
agent_timeout: 2400 # implementer may need more time on large codebases
# planner, test_writer, fixer inherit the default llm: above
The analyst, reviewer, and security reviewer benefit most from a stronger model. The analyst's output determines the outcome of the entire task; the reviewer and security reviewer need strong reasoning to catch subtle issues reliably. The implementer, planner, test writer, and fixer work from precise, structured prompts — a faster model is usually sufficient. The implementer and test writer are most likely to hit the timeout on large codebases — increase agent_timeout for them if needed.
Existing configs without an agents: block are unaffected.
Capabilities
This section summarizes what Sikula currently does. Detailed orchestration, state, and sandbox contracts are documented in ARCHITECTURE.md and Sandbox - what agents are allowed to do.
Project Setup
sikula initscans a project, detects the build tool, language, platform family, source/test paths, Node package manager, package-script validation defaults, and shared Xcode scheme when present.- Generated project files include
.sikula/config.yaml,.sikula/tasks/, and optional LLM-generated.sikula/guidelines.mdviasikula init --guidelines. .sikula/config.yamlis auto-discovered by walking up from the current directory; use--configto point Sikula at a specific config file.- Every run snapshots the effective config, including phase flags, sandbox paths,
build settings, test-writer settings, per-agent model/provider/timeout, and
configured
extra_rulespaths.
Run Pipeline
sikula runreads a task file and executes the configured pipeline: pre-sync, analysis, optional planning, implementation, review, security review, test writing, sync/build/test/check validation, and fixer loops.- The analyst and reviewer are read-only. The implementer, fixer, and test writer write
through sandboxed file tools and their changes are detected with
git diff. - Multi-step tasks run implement/review/security/test phases per step, then a final full-task gate reviews the finished branch against the original task before final validation.
run_build_per_step: truealso runs build/fix after each planned step. The final full-task build/fix loop still runs afterward.- Build/test/check failures feed the fixer until validation passes or the active
build/fix loop reaches
sandbox.max_iterations. - After fixer changes, stale reviewer, security reviewer, and test-writer gates rerun only once deterministic validation is green again. Any files changed by those gates trigger another validation pass.
Review Pipeline
sikula reviewruns reviewer and security reviewer against an existing branch usinggit diff base...branchand an explicit PR/task description as scope.- Report-only review is read-only and exits success/failure without modifying the branch.
sikula review --fixapplies corrections through the normal orchestrator loop and commits successful fixes back to the reviewed branch.- In review mode, changed test files are treated as branch output and reviewed for
correctness and relevance. Test-writer changes made during
review --fixget one final reviewer/security validation pass. - Security reviewer blocking issues feed back to the implementer; security warnings are recorded for audit and do not block by themselves.
Validation
- Built-in BuildTools cover Android/Gradle, JVM/Gradle, Maven, Node.js/TypeScript/JavaScript, Python, Rust/Cargo, and Xcode/Swift.
- Validation can include sync, compile/typecheck, tests, and configured quality checks.
Check entries support
name,command,timeout, and optional deterministicfix_command. - Build, test, and check commands are guarded against unexpected non-ignored repository artifacts; Sikula restores those files and records the cleanup for audit.
- Task-described validation commands are matched against the effective configured
build/test/check pipeline. In
sikula run, uncovered commands fail as validation coverage gaps; insikula review, PR/review commands are informational context. - Testability gaps reported by the test writer are visible in task state and interpreted
against
test_writer.test_surface_policy. The default surface isexisting_infrastructure, which keeps generated tests within existing project test infra instead of warning solely because a heavy UI/browser/device/runtime harness is absent. Usecompleteto opt in to gaps for missing test infrastructure outside that surface. The default gap policy iswarn;test_writer.testability_gap_policy: failmakes reported gaps blocking.
Safety And Scope
- Reviewer and security reviewer agents stay read-only.
- The sandbox has separate production and test write allowlists:
sandbox.allowed_write_pathsandsandbox.allowed_test_write_paths. - Test-origin fixer triage protects production writes. Test failures and test-origin
validation failures start test-only; production writes are enabled only by a separate
pass after explicit
production_defectplusproduction_codetriage. That second pass must actually change production code; test-only changes belong in the first pass. - Build/check diagnostics that reference only test files or recognized test targets may allow test repair. Unknown, production, or mixed diagnostics fall back to normal build/check scope.
- Mixed source/test files require an opt-in
BuildTool.is_test_only_change()proof. The default is fail-closed; Cargo currently proves edits limited to an existing Rust#[cfg(test)] mod testsblock. - Write-scope audit warnings are recorded when a write-capable agent changes files outside its active prompt scope.
State And Auditability
- Task state persists prompts, LLM outputs, files written, validation records, retry records, testability gaps, security-review records, final summaries, and relevant transition metadata.
sikula show <task-id>exposes the saved audit trail. Review/redact state JSON before sharing because it may contain source excerpts or sensitive project context.sikula run --task-id <id>resumes interruptedrunandreview --fixtasks. Report-onlysikula reviewis intentionally not resumed.- Isolated runs use a dedicated git branch and worktree. Successful tasks are committed and cleaned up; failed tasks preserve the worktree for inspection and resume.
status,show,cleanup, anddeleteprovide task-state and worktree management.
Configuration
- Each phase can be enabled or disabled through config or per-run
--flag/--no-flagoverrides. - Each agent can use its own LLM provider/model/timeout via
agents.<name>.llmor--agent-model,--agent-provider, and--agent-timeout. - Supported local CLI providers are Codex, Claude, Gemini, and OpenCode.
- Project-specific
extra_rulesfiles are supported for reviewer, security reviewer, test writer, and planner agents. - LLM failures are retried up to four times with backoff. Retry attempts are recorded in task history, and retries stop when partial file changes are detected.
Adding a new LLM provider
LLMClient defines three methods that all providers must implement. Adding a new provider
requires changes in one file only (core/llm_client.py).
| Method | Used by | What it must do |
|---|---|---|
generate(system, user) -> str |
PlannerAgent | Single-shot text generation |
run_readonly_agent(prompt, cwd) -> str |
AnalystAgent, ReviewerAgent, SecurityReviewerAgent | Run the model as an autonomous agent with read-only tools in cwd; return the model's text output |
run_agent(prompt, cwd) -> tuple[list[str], str] |
ImplementerAgent, TestWriterAgent, FixerAgent | Run the model as an autonomous agent with file read/write tools in cwd; return (changed_file_paths, agent_text_output) — file paths detected via git diff, text output best-effort |
Four providers are built in: CodexClient (provider: "codex"), ClaudeClient (provider: "claude"), GeminiClient (provider: "gemini"), and OpenCodeClient (provider: "opencode", model in provider/model format). For providers that call an HTTP API directly, you'll need the provider's SDK and credentials in .env.
Step 1 — implement the client (core/llm_client.py):
class CustomClient(LLMClient):
def __init__(self, config: LLMConfig) -> None:
from my_provider_sdk import Client
self._config = config
self._client = Client() # reads provider credentials from env
def generate(self, system: str, user: str) -> str:
resp = self._client.chat.complete(
model=self._config.model,
messages=[
{"role": "system", "content": system},
{"role": "user", "content": user},
],
)
return resp.choices[0].message.content
def run_readonly_agent(self, prompt: str, cwd: Path) -> str:
# Implement using the provider's agent/tool-use API with read-only tools.
# Must return the model's text output.
raise NotImplementedError
def run_agent(self, prompt: str, cwd: Path) -> tuple[list[str], str]:
# Implement using the provider's agent/tool-use API.
# Must return (changed_file_paths, agent_text_output).
# Text output is best-effort — return "" if not available.
raise NotImplementedError
The system argument passed to generate and the prompt argument passed to run_readonly_agent and run_agent already contain AGENT_SECURITY_PREFIX (defined in agents/base_agent.py) — the network and filesystem constraint is injected by each agent before calling the provider. You do not need to add it in your implementation.
Step 2 — register in the factory (core/llm_client.py):
def create_llm_client(config: LLMConfig) -> LLMClient:
if config.provider == "codex":
return CodexClient(config)
if config.provider == "custom": # add this
return CustomClient(config)
raise ValueError(...)
Step 3 — switch in the project config (no code changes):
llm:
provider: custom
model: your-model-name
max_tokens: 8096
Nothing else needs to change — orchestrator, agents, and tools are unaffected. See ARCHITECTURE.md § Add an LLM provider for the full interface contract.
Using the built-in CodexClient
No custom code is needed — CodexClient is already registered as provider: "codex".
llm:
provider: codex
model: gpt-5.3-codex
CodexClient calls the codex exec CLI. Authenticate with codex login or via an API key — see the Codex CLI documentation.
Sikula passes --skip-git-repo-check to codex exec; repository and worktree checks are handled by Sikula before task execution.
Sandbox notes:
- Single-shot and read-only calls (planner, analyst, reviewer, security reviewer,
and
sikula init --guidelines):--sandbox read-onlyis passed tocodex exec— file writes are blocked at the OS level. Shell execution is not per-command filtered; any bash constraints are prompt-level only. - Write-capable agents (implementer, fixer, test writer):
--sandbox workspace-writeallows file editing in the workspace and normal shell execution under the Codex sandbox. The bash restriction (grep,find,ls,git rmonly) is enforced at the prompt level — Codex CLI does not support per-command shell filtering. Sikula does not pass--add-dir; any writable paths outside the working root are determined by the Codex CLI sandbox policy, not by Sikula.
Using the built-in ClaudeClient
No custom code is needed — ClaudeClient is already registered as provider: "claude".
llm:
provider: claude
model: claude-sonnet-4-6
ClaudeClient calls the claude -p CLI, which is Claude Code Agent SDK usage.
Authentication and usage limits follow your Claude Code plan and environment. Local
developer machines may use the normal Claude Code authentication flow; scripted or CI
environments should provide ANTHROPIC_API_KEY or an apiKeyHelper via Claude settings.
Sandbox notes:
- Workspace boundary: enforced via
sandbox.filesystem.allowWrite(Seatbelt on macOS, bubblewrap on Linux). Sikula writes a generated Claude settings file with absolute sandbox paths and passes it explicitly via--settings; it does not rely on project-level Claude settings for the workspace boundary. - Read-only agents (analyst, reviewer, security reviewer): technically enforced via
--allowedTools— write tools and bash are blocked at the CLI level. - Write-capable agents (implementer, fixer, test writer): bash restricted to
grep,find,ls,git rmvia--allowedTools; technically enforced.
Using the built-in GeminiClient
No custom code is needed — GeminiClient is already registered as provider: "gemini".
llm:
provider: gemini
model: gemini-2.5-pro
GeminiClient calls the gemini CLI. Install and authenticate Gemini CLI according
to the distribution you use; if your setup supports API-key auth, Sikula can load
GEMINI_API_KEY from .env.
Sandbox notes:
- Workspace boundary: enforced by the
write_filetool's own path check (Path not in workspace). Sikula passes--skip-trusttogemini; repository and worktree checks are handled by Sikula before task execution. Note: Gemini CLI permits writes to its own internal temp directory (~/.gemini/tmp/); Sikula agents do not use this path. - Read-only agents (analyst, reviewer, security reviewer): write tools and shell are
excluded from
tools.corein.gemini/settings.json— technically enforced. - Write-capable agents (implementer, fixer, test writer):
run_shell_commandis included intools.corebut restricted at the prompt level (grep,find,ls,git rmonly). Gemini CLI does not support per-command shell filtering.
Using the built-in OpenCodeClient
No custom code is needed — OpenCodeClient is already registered as provider: "opencode".
Model must be in provider/model format:
llm:
provider: opencode
model: openai/gpt-5.3-codex
Configure authentication in OpenCode according to your provider — see the OpenCode documentation.
Sikula writes generated OpenCode agent definitions to a temporary OpenCode config
directory for each agent run and passes --dir with the task project root. It does
not write generated OpenCode files into the project or the original checkout.
Sandbox notes:
- Workspace boundary: Sikula invokes OpenCode with
cwdand--dirset to the task project root. Sikula does not add an OS-level workspace sandbox for OpenCode; any additional workspace boundary behavior comes from OpenCode itself.allowed_read_pathsandallowed_write_pathsare prompt constraints, not OS-level restrictions. - Read-only agents (analyst, reviewer, security reviewer): bash denied entirely via
bash: denyin OpenCode config — technically enforced. - Write-capable agents (implementer, fixer, test writer): bash restricted to
grep,find,ls,git rmonly at the prompt level — OpenCode CLI does not support per-command filtering.
Adding a new platform
The orchestrator loop is platform-agnostic — all platform-specific logic is isolated in
BuildTool subclasses (tools/base_tool.py) and .sikula/config.yaml project configs.
The build-loop methods (generate_sources, sync, compile_check, run_tests, run_check,
is_build_config_file) and the conservative mixed-file audit hook (is_test_only_change) are
defined on BuildTool; AndroidGradleTool, JvmGradleTool, MavenTool, NodeTool,
PythonTool, CargoTool, and XcodeTool are the current implementations.
| Platform | New file |
|---|---|
| Java backend / Maven | tools/maven_tool.py — subclass BuildTool |
| Any other | tools/<platform>_tool.py — subclass BuildTool |
Each new platform also needs:
.sikula/config.yamlin the project directory withsandbox.allowed_write_paths,guidelines.context_files, andguidelines.max_file_chars- Platform-specific guidelines docs (listed under
guidelines.context_files)
The agents and the orchestration loop need no changes. See ARCHITECTURE.md § Add a platform for the step-by-step.
Development
The test suite lives in tests/ and is split into unit tests and end-to-end tests:
tests/
├── test_*.py # Unit tests — all LLM calls mocked via unittest.mock
└── e2e/
├── conftest.py # FakeLLMClient, SequencedFakeLLMClient, shared fixtures
├── test_run.py # E2E tests for `sikula run`
└── test_review.py # E2E tests for `sikula review`
Dev dependencies (pytest, pytest-cov, ruff) are declared in pyproject.toml under [project.optional-dependencies]. Clone the repo and install in editable mode:
git clone https://github.com/sikula-ai/sikula
cd sikula/
pip install -e ".[dev]"
Editable installs run directly from the checkout. sikula --version shows the packaged
version plus a development suffix when the checkout is inside git, for example
sikula 0.2.0-dev+feature.example.abc1234.
If you get an "externally-managed-environment" error (common on Linux and some Homebrew setups), create a venv first:
python3 -m venv .venv && source .venv/bin/activate
Run all tests:
python3 -m pytest tests/ -v
Run only unit tests:
python3 -m pytest tests/ --ignore=tests/e2e -v
Run only e2e tests:
python3 -m pytest tests/e2e/ -v
Run with coverage:
python3 -m pytest tests/ --cov=agents --cov=core --cov=tools --cov=sikula --cov-report=term-missing
Use coverage to check new or changed code where meaningful. Coverage is useful for unit-testable behaviour such as state transitions, tool commands, orchestration logic, prompt construction, and output parsing. LLM calls are always mocked in tests — actual model behaviour is not unit-testable and is validated through end-to-end tests.
Unit test coverage:
| Module | What is tested |
|---|---|
core/orchestrator.py |
Phase gating, idempotency guards, build/fix loop, review loop, security loop, max-iteration limits, build tool factory |
core/state.py |
TaskState field defaults, serialisation, history append, review_diff round-trip and backward compat |
core/llm_client.py |
Factory (provider selection), call-with-retry, provider-specific response parsing (Codex, Gemini, OpenCode) |
agents/base_agent.py |
AGENT_SECURITY_PREFIX constant (network/filesystem constraint prepended to all agent prompts), extra-rules loading shared across agents |
agents/analyst_agent.py |
Guard conditions, output parsing, implementation prompt population |
agents/planner_agent.py |
SINGLE_PASS fallback, step parsing, max_steps enforcement |
agents/implementer_agent.py |
Guard conditions, file tracking, prompt construction (step context, review issues, write paths), error handling |
agents/reviewer_agent.py |
Guard conditions, approval/rejection parsing, diff truncation, review_diff state field takes priority over git_tool.diff_head(), plan step context |
agents/security_reviewer_agent.py |
Guard conditions, approval/blocking/warning/unexpected-output parsing, diff truncation, review_diff state field priority |
agents/fixer_agent.py |
Guard conditions, build/test/check error routing, write-path switching (production vs test dirs), error section construction |
agents/test_writer_agent.py |
Guard conditions, skip when test paths unconfigured, diff truncation, coverage target, error handling |
agents/init_agent.py |
Prompt construction, output parsing |
tools/base_tool.py (Sandbox) |
Path enforcement: allowed read/write roots, resolve logic |
tools/python_tool.py |
subprocess dispatch, exit-code 5 handling, timeout, sync, is_build_config_file |
tools/gradle_tool.py |
GradleBaseTool: subprocess dispatch, timeout, run_check, is_build_config_file |
tools/gradle_android_tool.py |
AndroidGradleTool: task configuration, generate_sources with presync clean, sync, compile, test |
tools/gradle_jvm_tool.py |
JvmGradleTool: configurable tasks (classes/test), presync clean, inheritance from GradleBaseTool |
tools/maven_tool.py |
MavenTool: ./mvnw auto-detection, command construction, presync clean, is_build_config_file |
tools/node_tool.py |
NodeTool: package-manager detection, package-script defaults, sync/compile/test/check command dispatch, is_build_config_file |
tools/cargo_tool.py |
subprocess dispatch, timeout, task configuration, is_build_config_file, run_check |
tools/xcode_tool.py |
subprocess dispatch, project args, compile/test task names, run_check, error extraction, is_build_config_file |
tools/git_tool.py |
subprocess dispatch, diff/status/checkout/add/commit/worktree operations |
tools/file_tool.py |
Sandbox-enforced read/write, path validation |
tools/scanner.py |
Build tool detection, guideline file scanning, write path detection, Xcode scheme detection |
sikula.py (helpers) |
_find_project_root, _resolve_config, _resolve_state_dir, _load_config, _branch_stem, _generate_config, _resolve_root_path, _resolve_task_path |
sikula.py (review) |
cmd_review worktree setup, report-only mode, gitignore management |
sikula.py (status) |
cmd_status task ordering, version flag |
E2E test coverage:
E2E tests exercise the full sikula run and sikula review command paths with a FakeLLMClient that writes deterministic files to disk — no API keys or network required.
| Scenario | What is tested |
|---|---|
| Single-pass happy path | Exit 0, state.done=True, files written to disk, history records key phases |
| Single-pass no changes | Exit 1 when implementer writes no files |
| Multi-step plan | Planner splits task into 2 steps, both complete in order |
| Multi-step step skipped | Step with no file changes is skipped, not aborted |
| Review rejection cycle | Reviewer rejects → implementer fixes → re-review approves |
| Max review iterations | Task fails after max_review_iterations consecutive rejections |
| Security blocking cycle | Security reviewer blocks → implementer fixes → re-review + re-security approves |
| Max security review iterations | Task fails after max_security_review_iterations consecutive security rejections |
| Agent exception | Unhandled agent exception sets state.failed=True, exits 1 |
| All steps skipped | Multi-step where every step writes nothing fails the task |
| Worktree isolation | Successful run commits to sikula/<task> branch, removes worktree |
| Worktree preserved on failure | Failed run keeps worktree at .sikula/worktrees/ for inspection |
| Resume interrupted task | --task-id resumes from partial state (analyst + planner done, implementer not yet run) |
| Reset failed task | --reset-failed clears failed flag and resumes to completion |
sikula review approved |
Exit 0, review_approved=True, security_approved=True, done=True |
sikula review rejected |
Exit 1, review_approved=False, failed=True |
sikula review security warnings |
Warnings-only security output is non-blocking; review approved |
See ARCHITECTURE.md for the full system design, execution flow, and agent descriptions.
Contributing
Sikula is a maintainer-led project. Feedback, bug reports, task-result reports, documentation fixes, and small corrections are welcome. For larger code changes, please open an issue or Discussion before starting so we can align on scope and keep the project focused. Pull request contributions require the CLA.
Security
To report a security vulnerability, use GitHub private vulnerability reporting from the repository Security tab. If that flow is unavailable, email contact@sikula.ai. Do not open a public issue for vulnerabilities. See SECURITY.md for the full disclosure policy.
License
Sikula is licensed under the GNU Affero General Public License v3.0 only (AGPL-3.0-only).
See NOTICE for copyright information.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sikula-0.2.0.tar.gz.
File metadata
- Download URL: sikula-0.2.0.tar.gz
- Upload date:
- Size: 281.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cd58a93888e36c744ba2408e1a8931d7fc2877365f5bebe3758742f51da97e8e
|
|
| MD5 |
6b0db9137afec5cff6c87ce86bc0a5ce
|
|
| BLAKE2b-256 |
a04d62b67d0a630935dac3c2d15a0e50372216d7bb03ccf14f9be343a642926c
|
File details
Details for the file sikula-0.2.0-py3-none-any.whl.
File metadata
- Download URL: sikula-0.2.0-py3-none-any.whl
- Upload date:
- Size: 161.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
85b7adadf12b8f6932107910dd23b3dc10113a0b1dd5779bd3d5a0e96987c75d
|
|
| MD5 |
4d2c803c7126dae12d7ada1144e3716a
|
|
| BLAKE2b-256 |
267cd0484ce4e3b3d3b01c640e3ebd91e120e7977d29c99dcfdb0db6b48c39c9
|