CLI reference
The topic page linked from each section below defines that command’s behaviour. CLI help
defines accepted command and option syntax: enso --help for the root and
enso <command> --help for a command. A disagreement between them is a bug.
Commands that operate on Enso’s runtime data honour ENSO_HOME (default ~/.enso). A
custom home does not relocate the fixed operating-system units managed by service and
web install.
The CLI has two audiences. You use it to operate Enso. The agent uses it, from inside a
turn or a job, to send messages, read Slack, work with tables, and move tasks. Commands
intended for agent automation expose --json where shown; not every operator command does.
For commands with --json, expected configuration, database initialization,
transport-loading, job validation, and lookup failures print exactly one
{"ok": false, "error": "…"} object on stdout and exit 1, with no text error on stderr.
Independent problems appear together in error, separated by semicolons. Invalid command
syntax, such as a missing required argument or an unknown option, still uses CLI usage
errors on stderr and exits 2. Commands that report health or validation findings, such as
doctor, workspace audit, and the onboarding commands below, keep their documented report objects.
Text and file inputs are bounded: enso config apply accepts 1 MiB and enso connect
16 KiB. Limits count encoded UTF-8 bytes, not characters, and apply the same way to a text
argument, a file, and stdin (-); anything larger is refused in the command’s usual error
output, and so is input that is not UTF-8. Beta builds read every such input through one
shared reader, which reports input exceeds N bytes and also caps chat messages, task notes
and bodies, heartbeat definitions, messages, and checkpoints, Slack --rich envelope files,
and memory record files at 256 KiB (262,144 bytes), and knowledge bodies at 2 MiB. That
shared reader is not in v0.1.2,
which counts and words some of the same refusals differently.
Operating
enso --version the installed version
enso setup first-run wizard for a fresh home
enso init [--json] prepare a home offline; preserve existing content
enso serve [--debug] run every configured transport and the scheduler
enso service install|uninstall|start|stop|restart|status
enso logs [-f] [-n N] [--turn ID] [--job NAME] [--grep TEXT]
enso config show
enso config check [--json]
enso config apply --file FILE|- [--expected-hash HASH] [--json]
enso config set PATH VALUE [--expected-hash HASH] [--json]
enso config unset PATH [--expected-hash HASH] [--json]
enso providers [--json] bundled provider, model, and effort choices
enso slack manifest packaged app manifest, JSON on stdout
enso connect start|status|cancel|finish private owner pairing; see Connections for arguments
enso models [--all] [--json] look up OpenRouter models OpenCode can run
enso doctor [--json] config, home, workspaces, providers, transports, service, jobs, heartbeat
enso update check|apply|status|recover managed release checks and recovery; see Updates
doctor is one pass with one exit code: config check, the workspace audit
for the home and every workspace, whether each provider path is still an executable,
whether each configured transport’s extra is installed, the service state (installed,
loaded, running, and its pid, plus a warning when the unit runs a different enso than
the one on PATH), every JOB.md, and current heartbeat health. The heartbeat check reads
saved state without running gates or migrating the database. An error-level finding is a health problem and makes
doctor exit 1; warnings alone exit 0. That strict health result does not mean every Enso
operation is blocked: for example, a workspace layout error fails doctor, while serve
logs it and continues when the workspace directory itself still exists. Sections that need
a valid config.json are skipped until it is.
--json prints {"ok", "home", "sections": [...]}, one section per line above in that
order, each {"name", "status", "note", "problems", "warnings", "details"}. status is
ok, warning, error, or skipped; problems and warnings are the messages the text
output shows; details holds the facts (provider paths and whether they resolve, transport
extras, the service pid, job names, and beat counts and attention references).
Beta builds — those with the enso web install command — add a viewer_service
section between service and jobs. It reports that unit, the home it was installed for,
and whether it is serving this home. An uninstalled viewer service is healthy, one installed for this home
but not serving it is an error, and a unit belonging to another home is reported without a
health error.
serve logs to ~/.enso/enso.log (rotating) and, when stderr is a terminal, to the
terminal. --debug adds the full prompt and every raw provider event. Each chat turn is
tagged [t:<id>] and each job run [j:<name>], which enso logs --turn and --job
filter on; -f follows across rotations and -n defaults to the last 50 lines.
serve, setup, and the job, run-history, task, project, heartbeat, workflow, messaging,
Slack, Telegram, and table commands initialize enso.db. They reject a newer database schema with an error: line and exit 1
before modifying that database or starting a transport or job. This also means some CLI
reads can create or migrate a database. Filesystem-only operations such as workspace create
and service-unit management do not use this guard, and setup seeds files before it checks
the database; see Upgrading.
service install writes ~/Library/LaunchAgents/com.enso.agent.plist (macOS) or
~/.config/systemd/user/enso.service (Linux) for the enso on PATH, with a PATH
capturing Enso’s directory, the configured provider directories, Node’s directory when
found, the installing shell’s nonempty PATH entries, and standard system directories,
then starts it. See The service for when to reinstall the unit.
Updates
The installation and update lifecycle owns the behavior; the release format owns artifact validation and authenticated downloads.
enso update install --manifest SOURCE [--bin-dir DIR] [--extras slack,telegram,web]
[--token-file FILE] [--feed URL] [--viewer-service NAME] [--adopt] [--json]
enso update check [--manifest SOURCE] [--notify] [--quiet] [--json]
enso update apply [--manifest SOURCE] [--drain-timeout SECONDS] [--startup-timeout SECONDS] [--json]
enso update status [--json]
enso update recover [--json]
SOURCE is a trusted local release.json or HTTPS manifest URL. install selects the first
managed runtime under ENSO_HOME; the shell installer bootstraps uv and Python before
calling it. It writes a stable launcher into --bin-dir (default ~/.local/bin) and saves
the feed and chosen extras (--extras defaults to slack,telegram,web) for future
upgrades. --token-file supplies a private bearer
token, --feed saves a different future manifest source, and --viewer-service names the
user service owning a hosted viewer. --adopt preserves and replaces a previous launcher;
the old daemon and viewer must be stopped. It does not configure chat or install services.
An already managed home uses apply instead.
check is read-only unless --notify is given. That flag announces an available release
once to the default notify target and records delivery only after a successful send. It
does not inherit a calling job or chat’s destination. --quiet suppresses ordinary text
and expected check/delivery failures; a later check retries. --json always emits a report,
including an error when quiet checking fails. A check without a feed on an unmanaged
checkout succeeds with managed: false, available: null, and no available update.
apply queues an independent updater and returns before the upgrade finishes. An equal
release is a no-op; downgrades, reused release versions with different code, and a second
pending operation are refused. Managed releases use stable major.minor.patch versions.
--drain-timeout defaults to 300 seconds (1–3600); --startup-timeout defaults to 60
seconds (1–600). The updater defers when accepted work or another ordinary CLI command is
still using the home at the drain deadline. Commands retain that access while waiting for
stdin, so an update cannot migrate underneath a pending --file - operation.
status reports the installed receipt, recent running-daemon version when available, and
the latest operation. recover retries an interrupted operation’s recovery using its
original snapshot; it never starts a competing worker or downgrades a completed update.
Keep using status after queueing either command. All five commands support the standard
single-object JSON error contract.
| Command | Successful JSON fields |
|---|---|
install |
ok, managed, installed_version, binary |
check |
ok, managed, current, available, update_available, optional release_notes_url; notified when requested |
apply |
ok, operation; a no-op also includes current, update_available: false and has operation: null |
status |
ok, managed, installed_version, installed_commit, running_version, operation |
recover |
ok, operation |
An operation includes id, status, from_version, to_version, started_at,
updated_at, and an error or notification_error when applicable. Terminal statuses are
succeeded, failed, rolled_back, recovery_failed, and deferred. A recovery_failed
operation keeps new work paused until recovery succeeds. Internal worker commands are
implementation details; operators and agents use the commands above.
Onboarding contracts
The offline commands in this section work without an active config or transport extras;
connect is the networked pairing workflow and requires the selected transport’s extra.
init, config apply, config set, config unset, and config check print exactly one JSON report with --json;
ok: false exits 1 and ok: true exits 0. Errors never print submitted credentials. Their contract version is
1, independent of the package version and configuration schema version.
| Command | Report fields |
|---|---|
init --json |
version, ok, home (path), changes (strings), problems (strings) |
config check --json |
version, ok, config_hash, problems, warnings |
config apply --file FILE|- --json |
version, ok, applied, config_hash, jobs_complete, restart_required, changes, problems, warnings |
config set PATH VALUE --json, config unset PATH --json |
the config apply fields |
config_hash is the SHA256 of exact bytes, "missing" for an absent file, or null if
the file could not be read. Check’s hash describes the same byte snapshot it validated.
Apply’s hash describes the current document when it can read it, including after a
successful write with incomplete job installation. Errors reading submitted input do not
inspect the current file and use null. Set and unset describe the current document once
they hold the lock, and report a missing or unreadable one as a problem rather than
creating it; a refusal for a busy lock or an active pairing reports null, as apply does.
restart_required is true when the transports, web, or logging sections of the
replaced document differ from the applied one, which enso serve (for transports and
logging) and the viewer (for web) read only at start; it is false on a fresh home and
whenever nothing was applied. See
Applying configuration for replacement,
locking, revision checks, retry behavior, and what a running service picks up on its own.
enso providers --json prints an offline catalog:
{
"version": 1,
"enso_version": "0.1.2",
"providers": [{
"id": "claude", "label": "Claude Code", "installed": true,
"default_model": "opus", "default_effort": "high",
"models": [{"id": "opus", "label": "opus", "efforts": ["low", "medium", "high", "xhigh", "max"], "known": true}]
}]
}
The example is abbreviated: every supported adapter and its bundled models are included.
installed means its executable resolves on PATH, not that its account is authenticated.
Efforts come from each adapter and exclude combinations it would clamp. known means
Enso has bundled effort information, not verified account access. OpenCode has a dynamic
model/variant catalog, so its offline efforts is empty, known is false and
default_effort is null; use Models for that catalog. These choices do not read
or override operator-specific provider settings in config.json.
enso slack manifest always prints the packaged manifest JSON, without a wrapper, network
request, or --json flag. It works from an installed wheel without a source checkout.
Setup also seeds a local copy at ~/.enso/slack/manifest.json without replacing an existing
copy; the command always exports the package’s current canonical version.
The connection commands add token verification,
expiring owner pairing, configuration apply, and evidence of a delivered first reply.
Models
enso models [--all] [--json]
models is a read-only lookup for the models OpenCode can route through OpenRouter. Its
source of truth is the openrouter catalog in https://models.dev/api.json; it does not
combine other models.dev providers or OpenRouter’s separate catalog. Each MODEL is the
full, copy-ready OpenCode id, including the openrouter/ prefix, for use in
providers.opencode.models. By default the command keeps only models with tool calling,
because a model without it cannot act as an Enso agent; --all includes both kinds.
Text output has exactly these columns:
MODEL TOOLS CONTEXT INPUT $/M OUTPUT $/M EFFORTS
Rows are sorted by MODEL. TOOLS is yes or no; CONTEXT is the integer token limit
with thousands separators; input and output costs are compact decimal numbers in USD per
million tokens. An unavailable context or cost is -. EFFORTS is the model’s exact,
possibly sparse list of the variants OpenCode accepts for it, comma-separated, or - when
it has none. It is not a range: copy one of the listed values exactly.
Configuration says where that list comes from.
--json prints a top-level array with one object per displayed model. For example (catalog
values can change):
[
{
"id": "openrouter/deepseek/deepseek-v4-flash",
"tool_call": true,
"context": 1048576,
"cost": { "input": 0.08092, "output": 0.16184 },
"efforts": ["high", "xhigh"]
}
]
Those are the complete fields, and objects are sorted by id. context, cost.input, and
cost.output are JSON numbers or null when the source omits them; efforts holds the
same values in the same order as the text column, empty when the model has no variant.
Costs retain the same USD-per-million unit as text output.
Enso reuses the newest valid catalog no more than one hour old from either
$ENSO_HOME/cache/models.json or OpenCode’s $XDG_CACHE_HOME/opencode/models.json
(~/.cache/opencode/models.json when that variable is unset or empty). It only reads the
OpenCode cache. With no fresh cache it fetches models.dev and updates Enso’s cache. That
refresh gets 15 seconds in total, from name resolution through connecting, redirects,
response headers and the body, so a slow name server or a slow site can delay the command
by that much and no more, however slowly it answers.
If the fetch fails or runs out of time, the newest valid stale cache is used with a warning
on stderr; without a valid fallback the command fails. A successful fetch whose cache write
fails is still displayed, also with a warning. It never reads or writes config.json, so
selecting a model remains an explicit operator edit. Warnings stay on stderr, including with
--json; a JSON load failure prints {"ok": false, "error": "…"} and exits 1 instead of
printing an array.
Workspaces
enso workspace list names, bindings, jobs, and audit state
enso workspace create NAME scaffold the full layout
enso workspace audit [NAME] [--fix] [--json]
audit checks the home and the workspaces: the layout, the CLAUDE.md link, the skill
wiring, and whether each workspace is bound or used by a job. --fix creates and repairs;
it never deletes. The command exits 1 while any error remains; warnings alone exit 0. See
Workspaces.
Knowledge
These commands require the knowledge feature and work without transport configuration or database setup. See Knowledge for placement, links, imports, and formatting conventions.
enso knowledge roots
enso knowledge list [--scope SCOPE] [--folder PATH] [--limit N] [--offset N]
enso knowledge search QUERY [--scope SCOPE] [--folder PATH] [--limit N] [--offset N]
enso knowledge show REF [--scope SCOPE]
enso knowledge audit [--scope SCOPE]
enso knowledge create PATH --file BODY_FILE [--scope SCOPE]
enso knowledge adopt PATH [--scope SCOPE] [--expected-hash HASH]
enso knowledge update REF --file BODY_FILE --expected-hash HASH [--scope SCOPE]
enso knowledge move REF DESTINATION [--scope SCOPE] [--to-scope SCOPE] [--expected-hash HASH]
All support --json. A scope is general or workspace:<name>. list, search, and
audit cover all roots when scope is omitted; other commands default to general.
--folder includes descendants. Listings default to 50 results, allow 1–500, and use a
zero-based offset. Search matches every whitespace-separated term, case-insensitively,
against each note’s path and body.
PATH and DESTINATION are root-relative Markdown paths. REF can also be a unique
note UUID. move stays in the source scope unless --to-scope names another root.
create and update require a body-only file, with --file - accepting stdin; Enso
writes the frontmatter. Creation and moves refuse existing destinations.
Read before editing. For example, use jq to save an existing page and extract its body:
enso knowledge show "Reference/Domains.md" --scope general --json > /tmp/domains-current.json
jq -j .body /tmp/domains-current.json > /tmp/domains-body.md
Edit /tmp/domains-body.md, keeping the saved JSON unchanged. Then use the revision you
read before editing:
knowledge_revision=$(jq -r .sha256 /tmp/domains-current.json)
enso knowledge update "Reference/Domains.md" --scope general \
--file /tmp/domains-body.md --expected-hash "$knowledge_revision"
show --json returns metadata, body, backlinks, and sha256. An update with a stale hash
is refused. adopt normalizes an existing page while preserving unfamiliar original
metadata; move preserves its ID and repairs resolved links. See
recovery guidance before handling an interrupted move.
audit --json returns {ok, notes, problems}. The audit is read-only and exits 1 when
it finds problems; the editable style checker is a
separate check.
Memory
Requires the Memory feature; it is separate from Knowledge.
enso memory list [--workspace W | --all-workspaces] [--transport T] [--channel C] [--since DATE] [--until DATE] [--limit N] [--offset N] [--json]
enso memory search QUERY [--workspace W | --all-workspaces] [--transport T] [--channel C] [--since DATE] [--until DATE] [--limit N] [--offset N] [--json]
enso memory show REF [--sources] [--json]
enso memory status [--json]
enso memory forget REF --yes [--json]
Lists and searches default to ENSO_WORKSPACE when set, otherwise all workspaces.
Results are newest event first; search matches literal text in summaries. --limit
defaults to 20 and accepts 1–100; --offset defaults to 0. JSON returns entries,
total, limit, and offset. Date filters accept today, week, month, durations
such as 24h or 7d, and ISO dates or timezone-aware timestamps. The start is inclusive and
the end exclusive; calendar dates use memory.timezone. week begins Monday.
show --sources adds the original requests, agent replies, timestamps, origins,
outcomes, and truncation flags. forget permanently removes the selected memory,
its source exchanges, and any other memories connected through shared sources.
There is no summary-edit command. See Memory for corrections and
the scope of forgetting before using it.
The bundled refinement job uses these additional commands:
enso memory prepare --batch ID [--limit N] [--json]
enso memory record --batch ID --file FILE|- [--json]
enso memory check --batch ID [--json]
prepare returns a bounded batch of new completed exchanges; --limit defaults to 40,
which is also its maximum. It exits 1 without running a model when no work is available
or Memory is disabled, and 2 on an error.
record accepts an array of summaries and source IDs, derives dates and origin from
those sources, and atomically settles the batch, including an empty result [].
Replaying the same result is safe; changing an already recorded result is refused.
check exits 0 for a recorded batch, 10 when it still needs recording, and 2 on an
error. Agents should use the bundled enso-memory skill’s recording contract.
Skills
enso skill list [--available] [--json]
enso skill show NAME [--json]
enso skill install NAME [--json]
list reads only installed home skills and works offline. --available reads the
official geekforbrains/enso-skills catalog; show fetches one catalog entry. install
adds one named skill to $ENSO_HOME/skills/, refusing any existing destination. There
are no repository, URL, branch, force, update, or remove options. Commands do not load
transport configuration or initialize the database. See
Customizing § Official optional skills for
source restrictions, requirements, receipts, and preservation rules.
With --json, local list returns an array of
{name, scope, path, description, problems, collision, collides_with, origin, source, commit}.
scope is enso; origin is bundled, official, or manual. Only an official receipt
supplies source and commit; otherwise they are null. Invalid skill directories are
listed with problems, not silently omitted; use the audit for a failing health check.
Remote list returns {source, commit, skills: [...]} with each entry containing
{name, description, files, requires, installed}. installed means the home destination
is occupied, including an invalid copy or link; it does not certify that copy. show returns
{source, commit, name, description, files, requires}. files are paths relative to the
skill directory, and requires names bundled skills that must already be valid in the
home. install returns {ok: true, name, path, source, commit, files, requires}. Expected
network, catalog, validation, or filesystem failures follow the single JSON error/exit 1
contract at the top of this page; they never run code from the catalog.
Heartbeat
enso heartbeat status [--json]
enso heartbeat create --file FILE [--json]
enso heartbeat update REF --file FILE [--if-revision N] [--json]
enso heartbeat list [--all] [--state STATE] [--workspace W] [--limit N] [--offset N] [--json]
enso heartbeat show REF [--json]
enso heartbeat history REF [--limit N] [--before ID] [--after ID] [--unhandled] [--kind KIND] [--json]
enso heartbeat pause|resume REF [--message TEXT] [--json]
enso heartbeat cancel|expire REF --message TEXT [--json]
enso heartbeat complete REF --message TEXT [--checkpoint JSON] [--json]
enso heartbeat wait REF --message TEXT [--followup-at TIME] [--checkpoint JSON] [--json]
enso heartbeat note REF TEXT [--occurred-at TIME] [--json]
enso heartbeat action REF KEY --message TEXT [--json]
enso heartbeat action-result REF KEY --status STATUS --message TEXT [--receipt TEXT] [--json]
Heartbeat handles finite deferred work. Heartbeat owns its definition fields,
lifecycle, gate convention, history, and retention. JSON definition files can be read from
stdin with --file -. Creation is paused; the agent writes and validates any gate before
resume. No creation or validation command performs the future action.
Current instructions live in the beat record. list and history return up to 100 records
by default. history is paginated; a normal page is newest
first, while --after and --unhandled read forward. show includes a history pointer and
counts so an agent can fetch context on demand. wait records progress without fulfilling the
beat. A running agent can acknowledge only the events supplied to its run, preserving newer
events for later.
action records an attempt under a stable key. action-result records succeeded, failed,
or uncertain, with a receipt when available. Reusing a succeeded or unresolved key is
refused; reconcile uncertain outcomes before retrying. Actor and run identity come from
Enso’s environment, never from the JSON definition or CLI flags.
Jobs and runs
enso job list [--json]
enso job create --name N --provider P --model M --effort E --workspace W [--schedule S] [--project KEY --stage NAME] [--json]
enso job show NAME [--json]
enso job run NAME [--json]
enso runs list [--job NAME] [-n N] [--json]
enso runs show ID [--json]
job create needs --name --provider --model --effort --workspace and either --schedule
(five-field cron) or --project KEY --stage NAME, which make a stage job
and leave --schedule optional. It creates no job files when one of them is invalid. Like
the other job commands, it initializes the database before validating the job.
job run --json prints {"ok", "status", "run_id", "output", "error", "exit_code", "postrun_error", "session_id", "task"} for an executed or skipped trigger (task is the
reference a stage job claimed, else null) and exits 1 unless the final status is
ok or no_work. A failed postrun fills postrun_error and changes an otherwise ok,
no_work, or skipped result to error; primary provider/prerun failures keep their status.
The provider/prerun exit_code stays distinct from the hook’s exit code. Plain output prints
the hook diagnostic on stderr. A per-job lock collision has status: "skipped" and
run_id: null; a group collision has a run ID and runs its reaction hook. Configuration,
database initialization, job validation, and lookup failures use the error object described
above. JOB.md can override the default two postrun follow-ups with max_followups;
see Jobs.
runs list shows the 20 newest runs unless -n sets another count.
runs list --json returns the retained run rows, including final session_id and
postrun_error (null when absent). runs show --json adds an ordered attempts array,
whose entries carry number, status, exit_code, output, error, session_id,
duration_ms, postrun_exit_code, postrun_output, and postrun_error. Provider turns
start at 1; a reaction hook when no provider ran uses attempt 0. Plain runs show displays
these attempts too. History from before attempt recording was added has an empty array.
Tasks and projects
enso task add TITLE --project KEY [--body TEXT | --body-file PATH|-] [--priority N] [--backlog] [--after REF] [--from REF] [--json]
enso task list [--project KEY] [--stage NAME] [--ready] [--claimed] [--attention] [--all] [--idle-for 30m] [--json]
enso task show REF [--json]
enso task advance REF --message TEXT|- [--ref KIND:VALUE ...] [--force] [--json]
enso task return REF --message TEXT|- [--force] [--json]
enso task block REF --message TEXT|- [--after REF] [--force] [--json]
enso task resume REF [--message TEXT|-] [--to STAGE] [--json]
enso task drop REF --message TEXT|- [--json]
enso task release REF --message TEXT|- [--force] [--json]
enso task edit REF [--title T] [--body-file PATH|-] [--priority N] [--after REF] [--force] [--json]
enso task note REF TEXT|- [--attention] [--json]
enso task ref REF KIND VALUE [--json]
enso task land REF [--json]
enso task sweep [--project KEY] [--json]
enso project list [--json]
enso project add KEY --name NAME --workspace WS [--repo PATH] (--stages a,b,c:human | --flow F) [--setup CMD] [--copy PATH]... [--json]
The board, its moves, claim rules, and JSON task fields are in Tasks.
The transactional behavior below is part of the implemented beta changes; check
workflow availability for your installation.
A refused move exits 1 with its reason, or the error object described above with --json.
The actor comes from the environment: a job run acts as job:<name>, a chat turn as its
sender, and a terminal as the local user. --message - and --body-file - read stdin.
During a stage run, advance and return submit a handoff and retain the current stage
and claim. A successful CLI submission is not acceptance. Once provider and postrun work
stop, Enso evaluates the submission, records the outcome, and moves accepted work.
Configured checks must pass for a forward handoff; a return uses the earlier stage and
its finite return budget. block records the reason immediately but retains execution
ownership until the provider stops.
A run can only change its held task; it cannot use --force or drop. An operator cannot
force a move past a live execution claim or configured checks. Manual advance from a
checked or integration stage requires the stage job or workflow verify; resume --to
cannot skip required workflow stages. task land is refused when any project stage has
checks or engine integration. Such projects land through their integration stage.
Pending lifecycle work must finish before another stage or landing proceeds.
project add takes exactly one of --stages and --flow (basic, support, marketing),
validates the whole configuration, and writes it atomically. The former --flow dev
shortcut is rejected: create a project with --flow basic, then configure its checked
development workflow with workflow init --preset dev. See
project configuration.
Workflow commands
enso workflow init KEY [--preset basic|dev] [--lint COMMAND --test COMMAND] [--base BRANCH] [--worktree-root PATH] [--migrate] [--json]
enso workflow show REF [--json]
enso workflow verify REF --message TEXT|- [--json]
enso workflow retry REF --message TEXT|- [--json]
enso workflow approve-rules REF --message TEXT|- [--json]
| Command | Behavior |
|---|---|
init |
Configures an existing project and creates disabled, editable stage jobs. --preset basic creates one unchecked work stage. The default preset, dev, requires a Git repository and both real --lint and --test commands; it creates plan, implement, review, and engine integration. --base and --worktree-root set the project’s base and worktree_root in project configuration. |
show |
Shows persistent transactions and lifecycle events, including acceptance, candidate-bound check results, diagnostics, and repair budgets. JSON contains transactions and events. |
verify |
Submits an operator handoff and runs the current stage’s acceptance without a provider. It runs actual checks and performs engine landing for an integration stage; acceptance advances the task. |
retry |
Records a required reason to replenish repair/return budgets and retry failed lifecycle delivery. It does not turn failure into success or automatically resume blocked work. |
approve-rules |
Records operator review of changed protected acceptance inputs. Approval binds to the reviewed content and spec/workflow; later changes invalidate it. Required checks still have to pass. |
verify, retry, and approve-rules are operator actions, refused inside an agent run or
lifecycle script. Verification requires an unclaimed task in a configured stage, with no
pending lifecycle delivery; resume blocked work into its stage first. It holds the task’s
execution lock, but it does not acquire a stage job’s project-capacity slot or concurrency
group. Prefer the stage job when checks share ports or databases; coordinate shared
resources before manual verification. Use the stage job for a command stage so its
configured command executes. An interrupted manual claim can be recovered with
retry only after its execution lock proves no verifier is still running.
init --migrate retains task history and recorded worktrees, disables and preserves old
stage jobs, and maps legacy triage/todo to plan/implement. Inspect and drain active
work first; review custom stage meanings and generated prompts before enabling the new
jobs. If installation is interrupted, rerun workflow init KEY to resume its saved
operation; do not remove the admission gate manually. Use the
workflow guide for the complete setup and migration procedure and the
bundled enso-workflow skill for help designing a workflow.
Web
enso web start [--port N] [--host H] [--foreground]
enso web stop|status
enso web install|uninstall optional viewer user service; beta only
Read-only, and a separate process from serve. start runs the viewer in the background
(its output goes to ~/.enso/web.log, its lock to ~/.enso/web.pid), prints the URL
once it answers, and exits 0 saying already running when the same viewer is live;
--foreground runs it in the terminal instead. stop signals the live viewer and waits
for it; it says not running when there is nothing to stop. status prints one line and
exits 0 only while the viewer runs. For standalone and foreground starts, flags win over
web.host and web.port; a missing or
invalid config.json falls back to 127.0.0.1:8787 so the Health page can report it.
start and install need the web extra, without which start prints the install
command; status, stop, and uninstall work without it.
install and uninstall are beta commands, not included in
v0.1.2; run enso web --help
to see whether your installation lists them. install writes and starts a user service
(~/Library/LaunchAgents/com.enso.web.plist on macOS, ~/.config/systemd/user/enso-web.service
on Linux), which keeps the viewer running after a crash and at login. uninstall stops it
and removes the unit, leaving the home and its logs alone. While that service is installed
for this home, start and stop control it instead of a standalone process, and --host
and --port are refused: change web.host and web.port and restart it, or use
--foreground for a manual start with flags. Viewer lifecycle changes — install,
uninstall, stop, and any start other than --foreground — are refused while a managed
update is pending; retry afterward. See Web viewer and
the viewer.
Messages
enso message send TEXT|--file F|- [--to T] [--action-key KEY] [--json]
enso message attach FILE [CAPTION] [--to T] [--action-key KEY] [--json]
enso message list [-n N] [--json] the 20 newest out-of-band sends by default
message send and message attach use explicit --to first, then the current beat’s saved
notification destination/thread, then the conversation that started the current turn (from
ENSO_ORIGIN_*), then the first transport with a notify
target (Slack before Telegram). --to is slack:C…, telegram:123, or a bare id when one
transport is configured. An explicit target does not inherit the current Slack thread;
use enso slack send -c C… -t TS to target a particular thread.
Native sends and uploads require a stable --action-key inside a heartbeat run, and reject
that flag outside one. They reserve the action before connecting and record the result and
receipt automatically; do not also reserve it with heartbeat action. Repeated succeeded,
pending, or uncertain keys are refused with a history lookup. A heartbeat gate cannot send
messages. An unusable saved beat destination fails clearly instead of falling back elsewhere.
See Heartbeat for handling uncertainty and other external actions.
Text comes from exactly one of the argument, --file, or stdin (-); files and stdin
avoid having to shell-quote the message body.
Every out-of-band send is recorded. At the start of a turn, rows sent into that
conversation since its last turn are shown to the agent as [Background messages] and
marked consumed. A DM or Telegram chat hears everything sent to it; a channel thread hears
sends to the channel itself and to that thread. Rows the turn’s own agent sent are retired
when the turn ends.
Slack
enso slack send -c C [-t TS] (TEXT | --file F | - | --rich F) [--action-key KEY] [--json]
enso slack upload -c C [-t TS] FILE [--caption TEXT] [--action-key KEY] [--json]
enso slack edit -c C --ts TS (TEXT | --file F | -) [--json]
enso slack delete -c C --ts TS [--json]
enso slack react -c C --ts TS EMOJI [--json]
enso slack unreact -c C --ts TS EMOJI [--json]
enso slack thread C TS [-n N] [--all] [--json]
enso slack history C [--since 24h] [-n N] [--all] [--json]
enso slack lookup-user Q | lookup-channel Q | whois U | open-dm U|Q | refresh [--users|--channels] [--json]
enso telegram send TEXT|--file F|- [--to CHAT] [--action-key KEY] [--json]
enso telegram attach FILE [CAPTION] [--to CHAT] [--action-key KEY] [--json]
--rich F posts a enso-message envelope file (fenced or bare JSON) as native blocks.
Plain Slack replies, sends, edits, and rich-message fallbacks translate ordinary Markdown: web,
email, and Slack deep links stay clickable, while other link targets become inline code. A reply
or send whose fenced code block names a language Slack highlights is posted as a native Markdown
block instead, so the code is highlighted; edits and unlabelled fences keep the plain
translation.
Reads print time name (id) ts=… headers with names from the directory cache; --all
keeps joins, pins, and other lifecycle noise. It does not remove the result limit:
thread defaults to the root plus up to 99 replies from the fetched page; history requests at most 20 recent top-level messages by default, with no time
cutoff unless --since is given. Both display oldest first. Thread reads make one Slack API request capped at 100 messages
and do not follow its next cursor. -n 0 includes all fetched messages, not necessarily
the whole thread. Channel history also uses one API page. There is no search: Slack’s
search.messages accepts only user tokens, and Enso holds a bot token.
The message write contract
On success, a message, slack, or telegram write run with --json prints one object
and exits 0:
{"ok": true, "transport": "slack", "channel": "C…", "ts": "1725…", "thread_ts": null,
"permalink": "https://…"}
Telegram writes carry chat_id and message_id instead. An upload has no message ts: it
returns "ts": null and "file": "F…". edit, delete, react, and unreact return
ok, transport, channel, ts (and reaction for the two reaction commands);
open-dm returns user and channel;
refresh returns the users and channels counts. A failed write prints
{"ok": false, "error": "…"} and exits 1; Slack API failures use Slack’s own error code
when available. This also covers configuration, database initialization, and
transport-loading failures before a write starts. Without --json, successful writes print
non-empty result fields other than ok as key=value pairs on one line.
result=$(enso slack send -c C0123456789 "Starting" --json) || exit 1
TS=$(printf '%s' "$result" | jq -er 'select(.ok == true) | .ts') || exit 1
enso slack send -c C0123456789 -t "$TS" "Done"
Native Slack tables and charts
--rich FILE accepts a bare JSON object or one enso-message fenced block. The envelope must have exactly version, fallback_text, and blocks:
{
"version": 1,
"fallback_text": "North: 1,240 units; South: 980 units.",
"blocks": [
{
"type": "table",
"rows": [["Region", "Units"], ["North", "1,240"], ["South", "980"]],
"columns": [{}, {"align": "right"}]
},
{
"type": "chart",
"kind": "bar",
"title": "Units by region",
"categories": ["North", "South"],
"series": [{"name": "Units", "data": [1240, 980]}]
}
]
}
enso slack send -c C0123456789 --rich drafts/region-summary.json --json
Supported blocks and limits are:
markdown: nonblank text, with 12,000 characters total across Markdown blocks;table: rows of equal width, at most 100 rows, 20 columns, and 10,000 cell characters across all table blocks; cells are nonblank strings or numbers; optionalcolumnsentries may setaligntoleft,center, orrightandwrapto a boolean;piechart: 1 to 12 labeled segments with positive numeric values;barorlinechart: 1 to 20 unique categories and 1 to 12 uniquely named series, each containing exactly one number per category;x_labelandy_labelare optional;- all charts: at most 2 per message;
- the envelope: 1 to 50 blocks and nonblank
fallback_textno longer than 4,000 characters; - chart titles and axis labels: at most 50 characters; category, segment, and series labels: at most 20.
Slack renders table cells as text, so format display values yourself. Use ordinary Markdown unless a native table or chart materially improves comprehension, and make fallback_text a complete usable answer for transports or clients that cannot render blocks.
Tables
enso table list [--json]
enso table register NAME -d|--description D [--name N] [--json]
enso table schema NAME [--json]
Registers an existing ordinary table in enso.db so agents can find it with table list.
Re-registering it updates its description and display name; it does not change the schema.
schema prints columns, constraints, indexes, and the CREATE statement. Names beginning
_enso_ or sqlite_, and Enso’s own tables, are reserved.
Environment for agents
| Variable | Set for | Value |
|---|---|---|
ENSO_HOME |
everything | The home directory |
ENSO_WORKSPACE |
everything | The workspace name; the provider’s cwd is its directory |
ENSO_ORIGIN_TRANSPORT |
chat turns | slack or telegram |
ENSO_ORIGIN_USER_ID / _USER_NAME |
chat turns | Who sent the message |
ENSO_ORIGIN_CHANNEL / _CHANNEL_NAME |
chat turns | Where it came from (dm for direct messages) |
ENSO_ORIGIN_THREAD_TS |
chat turns | The Slack thread, when there is one |
ENSO_JOB / ENSO_RUN_ID |
jobs | The job’s directory name and this run’s id |
ENSO_TASK |
stage jobs | The reference of the task claimed for this run, such as EN-041 |
ENSO_TASK_DIR |
stages using a worktree | The recorded task worktree; the provider’s cwd is still the workspace |
ENSO_PROJECT_REPO |
repo stage jobs and lifecycle scripts | The regular repository path for context |
ENSO_TRANSACTION_ID / ENSO_CANDIDATE |
workflow checks | Transaction and stable candidate being evaluated |
ENSO_EVENT_ID |
lifecycle scripts | Stable event identity; use it to deduplicate effects |
ENSO_PROJECT / ENSO_FROM_STAGE / ENSO_TO_STAGE |
lifecycle scripts | Project and accepted transition |
ENSO_BRANCH / ENSO_BASE |
lifecycle scripts | Recorded task branch and target |
ENSO_ATTEMPT |
workflow checks and lifecycle scripts | Current verification or delivery attempt |
ENSO_LIFECYCLE |
lifecycle scripts | Marks lifecycle execution; recursive task moves are refused |
ENSO_BEAT |
beat gates and runs | The beat reference, such as HB-001 |
ENSO_BEAT_RUN_ID |
beat agent runs | This assessment’s run ID |
ENSO_BEAT_CHECKPOINT |
beat gates and runs | The saved source checkpoint as a JSON object |
ENSO_RUN_STATUS / _EXIT_CODE / _DURATION_MS |
postrun only | Current outcome and cumulative elapsed time before this hook; the row is still running |
ENSO_RUN_ATTEMPT / _FOLLOWUPS_REMAINING |
postrun only | Current provider turn (0 when none ran) and remaining extra turns |
Workflow-specific fields above require the workflow beta.
Chat-origin variables are empty when unknown. The job runner does not populate or clear
origin variables: normal scheduled runs have none, while a manual job run launched
inside a chat turn inherits the caller’s origin and can use it for untargeted sends.
A chat turn also opens its prompt with the origin block stating the same platform, sender,
location, and thread in words; see Concepts § Chat origin.
The block is what the model reads, and these variables are what a command it runs reads.
A stage job does the same with the Task block and ENSO_TASK;
ENSO_JOB and ENSO_RUN_ID are also how enso task knows it is acting for a run.
Heartbeat runs and the updater’s own notifications clear inherited job, task, run, beat, and chat-origin variables before starting a child, so the child reports as its own source rather than the caller’s. Heartbeat’s background context comes from the saved beat; a prior conversation is not presented as a new incoming message.
