Workflows
A workflow is a named, optionally scheduled sequence of prompts that run unattended on a dedicated wf:{name} worker sub-agent — the same mechanism as ws: workspace agents. Use workflows for recurring or batch jobs: “check the CI pipeline every 30 minutes”, “triage open issues each morning”, “run this 3-step refactor once, now”. Workflows with a schedule: key are executed by the scheduler.
Where to define them
Preferred: one YAML file per workflow in ~/.cecli/workflows/ (with ~/.agents/workflows/ also discovered):
~/.cecli/workflows/
check-pipeline.yml
morning-triage.yml
Also supported: a repo-local .cecli/workflows/ or .agents/workflows/ directory (one YAML file per workflow), inline under a workflows: key in .cecli.conf.yml, or a repo-local .cecli.workflows.yml. Each source may hold a single workflow mapping, a workflows: [...] list, or a plain list of mappings. Resolution precedence (highest first):
- CLI argument — an inline JSON/YAML string or a file path passed to
--workflows. - Local file —
.cecli.workflows.yml/.cecli.workflows.yamlin the git root (or working directory). - Local directory —
.cecli/workflows/*.yml/*.yamlin the git root (or working directory), loaded in sorted order. - Standard local directory —
.agents/workflows/*.yml/*.yamlin the git root (or working directory), loaded in sorted order. - Merged config — the
workflows:key in.cecli.conf.yml. - Global files —
~/.cecli/workflows/*.yml/*.yaml, loaded in sorted order. - Standard global directory —
~/.agents/workflows/*.yml/*.yaml, loaded in sorted order.
On a duplicate name, the higher-precedence definition wins and the loser is skipped with a warning. A source that fails to parse is skipped with a warning as well — a malformed workflows file never crashes startup.
Reference format
name: check-pipeline # required, unique, no ':'
description: Poll CI, summarize failures
schedule: "*/30 * * * *" # optional cron / @preset / '@every 30m'; omit for manual-only
timezone: UTC # optional IANA name (default: local, interpreted as UTC)
enabled: true # optional (default: true)
lease_timeout: 30 # optional minutes (default: 30)
model: <model-name> # optional model override for the worker
root: ~/repos/my-project # optional working directory
repo: /repos/app # optional worktree source (local path or remote URL)
worktree: true # optional isolated git worktree; or {branch: main}
hooks: # optional, same schema as Hooks
start: [{ name: audit, command: "echo start >> ~/.cecli/workflows/audit.log" }]
skills: [gitlab-ci] # optional -> preloaded as skills_init
servers: [gitlab] # optional -> active MCP servers (lowercased)
on_failure: stop # stop | continue
persist: true # keep the worker in the TUI and reuse it next run
metadata: # optional sub-agent overrides (see below)
agent-config:
allow_nested_delegation: false
tasks:
- name: attach-gitlab
prompt: /mcp load gitlab
- name: triage
prompt: >
Monitor the gitlab test pipeline and recent builds; summarize any errors
into .cecli/reports/pipeline.md
save_to: pipeline_summary
Keys are snake_case or hyphen-case; both accepted. ${ENV_VAR} / $VAR inside any string is interpolated from the environment (after .env files load).
Validation notes: repo must be a string. worktree must be a boolean or a mapping; a mapping currently supports only branch, which must be a string (unknown keys warn and are ignored). When worktree is enabled, the source repository is inferred from repo, then root, then the git top-level containing the workflow file; if none can be found a warning is logged and the dispatching agent’s root is used at run time.
Field meanings:
| field | default | meaning |
|---|---|---|
name |
— | Identity; the worker agent is wf:{name}. Must match [A-Za-z0-9_.-]+ (no :) |
tasks |
— | Ordered prompt list; at least one task is required |
description |
"" |
Shown in /workflows list |
schedule |
none | 5-field cron expression, a preset (@hourly, @daily, @weekly, @monthly, @yearly) or an interval (@every 30m). Without one the workflow runs only via /workflows run |
timezone |
local |
IANA zone the cron expression is interpreted in; local — like any unknown zone, which falls back with a warning — is treated as UTC |
enabled |
true | Disabled workflows never tick and register no worker |
lease_timeout |
30 | Minutes a scheduler instance holds the job lock before peers may steal an abandoned run |
model |
inherited | Model (or sentinel) for the worker sub-agent |
root |
session root | Working directory the worker operates in |
repo |
— | Source repository for the optional worktree: a local path or remote URL. When the worktree is enabled, the run’s root becomes the generated worktree checkout |
worktree |
disabled | Set true to run the worker in an isolated git worktree, or {branch: <ref>} to choose the base ref to start from. See Worktrees |
hooks |
{} | Per-workflow hooks (start/end/pre_tool/…); run on the worker |
skills |
[] | Skills loaded into the worker’s context at start |
servers |
[] | MCP servers made available to the worker |
metadata |
{} | Sub-agent overrides, matching workspace project metadata: model, hooks and auto_reap map to the sub-agent config fields, an agent-config mapping is merged on top of the declarative skills/servers, and any other key is merged into the sub-agent metadata. name, description and root always come from the workflow definition |
persist |
false | Keep the worker sub-agent (and its TUI container) after the run finishes. The next run reuses the same worker after clearing its conversation, file context and prompt queue. Persistent workers are never auto-reaped |
on_failure |
stop |
What to do after a task errors: stop drops the remaining tasks, continue runs them anyway |
catch_up |
scheduler default | Per-workflow override of the scheduler’s catch_up (replay of slots missed while cecli was closed) |
Task fields:
| field | default | meaning |
|---|---|---|
prompt |
— | Required. A list entry may also be a bare string, which is taken as the prompt. Slash commands (/mcp load, /model, …) work exactly as if typed |
name |
task-N |
Label used in run logs and status output |
save_to |
none | On success, writes the run’s final summary to a persistent per-run file under ~/.cecli/workflows/outputs/{workflow}/{run}/ (named after the key; the path is recorded in the run log) |
Run-time placeholders are expanded in task prompts in addition to load-time env interpolation: {date} (today as YYYY-MM-DD), {workflow} (the workflow name) and {run_id} (the scheduler run id, empty for manual runs). Other braces are left verbatim. A complete annotated sample is in the repository at examples/workflows/check-pipeline.yml — copy it to ~/.cecli/workflows/ or pass it via --workflows to try it.
How a run works
- The scheduler (or
/workflows run) creates thewf:{name}worker sub-agent, configured with the workflow’s model, hooks, skills and servers. - The task prompts are queued to that worker in order, using the same prompt queue as
/queue. Each prompt is processed to completion before the next. - If a task errors, the remaining queued prompts are dropped according to
on_failure(or the failing task’scontinue_on_error), then the worker is reaped. (Caveat: errors that abort the queue-drain chain behave likestopeven undercontinue_on_error, since the remaining prompts never get a chance to drain; errors handled inside a prompt continue normally.) - The run is detached: a workflow worker never injects summaries or status into the parent’s conversation (background operations stay out of its context). A finished
/workflows runis announced once in the terminal only; the durable record lives in the run log and outputs. - With
persist: true, the worker is not reaped when the run finishes: it stays in the TUI and the next run of the same workflow reuses it. Before staging the new tasks, the runner waits for any in-flight run, clears the worker’s conversation (re-adding its system prompt) and file context, resets the prompt queue and per-run status, and then runs the new task list on the same agent.
Because a run is a sub-agent, it doesn’t block the main conversation — watch it with /agent-tree or the TUI sub-agent pane. Every run also appends a JSON-lines audit log to ~/.cecli/workflows/runs/<YYYY-MM-DD>/<name>.<id>.jsonl: a start line (with the definition hash), one task line per task, and a finish line. Before the worker is reaped, its full session is archived to ~/.cecli/workflows/outputs/<workflow>/<run>/session.json using the standard session format (encrypted when session encryption is enabled) — so every run, including replayed or tool-less replies, is completely reviewable on disk without ever touching the parent’s context.
Isolated worktrees
Set worktree: true to give the run its own git worktree instead of operating directly in root. The worker starts on a generated branch cecli/<agent>-<uuid> (for example cecli/wf-nightly-tidy-<uuid>) based on branch (the starting base ref) or the source repository’s default branch. repo selects the source repository; if it is omitted, root (or the workflow file’s git root) is used. The agent’s actual root is the generated worktree checkout.
When the run finishes, a dirty worktree is committed with the subject Unfinished Dirty Worktree from agent <uuid> before the worktree is released. Branches with unmerged commits are kept for review; empty or already-merged branches are deleted. The worktree checkout under ~/.cecli/worktrees/ is removed after the commit/release step. See Worktrees for the full lifecycle and on-disk layout.
Commands
/workflows # List loaded workflows (same as 'list')
/workflows list # Show loaded workflows
/workflows run <name> # Trigger now, in the background
/workflows show <name> # Dump the resolved definition (YAML)
/workflows status [name] # Scheduler job state and active leases (config view if the scheduler isn't running)
/workflows last <name> # Summary of the most recent run
/workflows enable <name> # Enable for this session (synced to the scheduler DB)
/workflows disable <name> # Disable for this session (synced to the scheduler DB)
/workflows reload # Re-resolve all sources: --workflows value + files
Examples
Nightly repo tidy (manual + scheduled):
name: nightly-tidy
schedule: "0 3 * * *"
timezone: America/New_York
repo: /repos/app
worktree:
branch: main
skills: [code-review]
tasks:
- prompt: "Run the linter over the repo and fix obvious issues."
- prompt: "Summarize what changed into CHANGELOG-Unreleased.md."
Dependabot-style review, gated on an MCP server:
name: pr-review
schedule: "@every 20m"
servers: [github]
on_failure: continue
tasks:
- prompt: /mcp load github
continue_on_error: true
- prompt: "List open PRs I haven't reviewed; for each, summarize the diff and flag risky changes."
save_to: pr_review_notes
One-shot pipeline (no schedule), run via /workflows run build-doctor:
name: build-doctor
tasks:
- prompt: "Run the full test suite and report failures."
- prompt: "For each failure, propose a minimal fix as a diff."
Compatibility
- Workflows are additive: nothing changes until you create a
workflows:key or a file in~/.cecli/workflows/or~/.agents/workflows/. wf:agents show up in/agent-treealongsidews:agents; a workflow can itself delegate sub-agents (nested delegation enabled).- Existing
/queuesemantics are unchanged; workflows reuse the same queue.
Arguments
--workflows <json|yaml>: Provide an inline workflows configuration or a path to a workflows file, overriding all file-based sources.--scheduler <json>: Override scheduler configuration for this session (same object as thescheduler:config key).