sagent-ai
v2.2.30
Published
SAgent AI auto installer and launcher for Windows and Linux
Maintainers
Readme
SAgent AI
SAgent AI is a compact DSL coding agent based on OpenAI Codex.
The core idea is simple: keep the Codex foundation, but replace the model-visible tool surface with one compact raw DSL tool: dsl_batch.
Release 2.2.30 is synchronized with the latest upstream Codex, supports the GPT-5.6 model family, and preserves direct dsl_batch access for models whose server metadata otherwise requests code-mode-only tools.
Instead of many JSON tool calls, the model writes a readable script of actions:
%% RG --summary "GameEngine" src
%% READ --outline src/game/engine.ts
%% READ --def createSnapshot src/game/engine.ts
%% USES createSnapshot src --summary
%% REPLACE src/game/engine.ts --def
export function createSnapshot() {
return { ok: true }
}
%% RUN
npm testNo JSON escaping. No quoted multi-line patches. The conversation history stays readable.
Install
npm install -g sagent-ai
sagentsagent-ai is a small auto installer/launcher package.
It installs and runs the native package for the current platform:
Windows x64 -> sagent-ai-win
Linux x64 -> sagent-ai-linuxDirect platform packages:
npm install -g sagent-ai-win
npm install -g sagent-ai-linuxRequirements:
- Windows x64 or Linux x64;
- Node/npm for installation;
- Deno in
PATHfor%% EXEC/%% JSscripting. - CodeGraph support for
%% CGis installed as the optional npm dependency@colbymchenry/codegraphunder thesagent-aipackage; SAgent falls back to a globalcodegraphbinary if needed. %% SEMsemantic search uses Ollama withqwen3-embedding:4bby default and can be configured to use OpenAI embeddings instead.
Use
Start in the current project:
sagentStart in a specific project:
sagent -C C:\path\to\projectRun a non-interactive task:
sagent exec "inspect the project, fix the failing test, and run the checks"Run JavaScript automation inside a DSL batch with EXEC:
%% EXEC --timeout 30
const outline = await dsl.READ("--outline src/app.ts");
const next = await dsl.ask(outline + "\nWhich definition should I inspect next?");
dsl.PRINT(next);Inside EXEC, every regular DSL command is available as an async dsl.* helper with the same arguments and bodies as the %% form. Only nested dsl.EXEC / dsl.JS are blocked.
EXEC also has direct Deno access to read and write the current project and to use the network. This allows direct npm imports:
const ts = await import("npm:typescript@5");
await Deno.writeTextFile(".agent-dsl/tmp/typescript-version.txt", ts.version);Deno caches downloads under .agent-dsl/deno-cache and may materialize a Deno-managed node_modules directory for npm: imports. EXEC clears the inherited host environment and exposes only a small synthetic environment for Deno/npm compatibility. Add a comma-separated [deno.exec] env = "HTTP_PROXY,..." allowlist when specific host env vars should be visible to Deno. Direct process spawning is not granted; call await dsl.SH(...) when a script needs shell commands.
Reusable EXEC functions can live in .agent-dsl/exec/function/<name>.ts as plain async body code. SAgent wraps and caches them under .agent-dsl/exec/function/.compiled.
%% EXEC pick-ranges --limit 3
raw search output...Inside the function body, use dsl, dan, args, and argText directly:
const answer = await dsl.ask(`Choose ranges from:\n${dan}`);
await dsl.PRINT(answer);The same function can be called from regular EXEC code:
const ranges = await dsl.function(`pick-ranges --limit 3
${searchOutput}`);Use dsl.try.function(...) when a missing or failing helper should return { ok: false, error } instead of throwing.
Configurable ASK / GEN profiles
DSL summarization and generation helpers can use named profiles from:
- CLI overrides such as
-c agent_dsl.llm.ask_profile="sonnet"; - project
.agent-dsl/config.toml; - global
~/.codex/.agent-dsl/config.toml; - built-in defaults.
Example:
[llm]
ask_profile = "default"
gen_profile = "default"
fallback_profile = "gpt55-low"
[llm.profiles.sonnet]
model_provider = "claude"
model = "sonnet"
max_input_tokens = 100000
max_output_tokens = 4000
[deno.exec]
env = "HTTP_PROXY,HTTPS_PROXY,NO_PROXY,NODE_EXTRA_CA_CERTS"Built-in profiles are:
default—openai/gpt-5.3-codex-spark, reasoninglow;gpt55-low—openai/gpt-5.5, reasoninglow;gpt55-xhigh—openai/gpt-5.5, reasoningxhigh.
model_provider = "claude" uses the local claude CLI with tools disabled. Use profiles with --ask-profile, --gen-profile, %% ASK --profile name, or await dsl.ask(prompt, ["YES", "NO"], "gpt55-xhigh"). Unknown profiles fall back to default; unavailable profiles retry fallback_profile when configured, then default.
For noisy searches or summaries, add {{ranges}} to an --ask / --ask-full / ASK capture prompt when you want the ASK model to choose exact source ranges. SAgent removes the marker before asking, then expands returned links into real numbered snippets:
%% RG "handleSubmit" src --ask "{{ranges}} Pick the 1-3 ranges I should read next."Single-file commands may return >>START-END; ASK capture and multi-file answers should return >>path:START-END. Duplicate or overlapping ranges are merged and capped.
Skills can be loaded on demand without adding the full Codex skills list to the system prompt:
%% SKILL --list
%% SKILL --list --paths
%% SKILL --find ast
%% SKILL ast-grep-cli
%% SKILL ast-grep-cli --ask "Give only commands useful for this task."For one-off expert ASK prompts, inject a skill only into that ASK request:
%% RG "defineComponent" src --ask-profile gpt55-xhigh --ask "{{SKILL:ast-grep-cli}} Pick the safest ast-grep command."{{SKILL:name}} works in global --ask / --ask-full, ASK --begin / ASK --end, normal ASK, and EXEC dsl.ask(...).
Plain SKILL --list and SKILL --find show names, scopes, and descriptions without noisy paths; add --paths when you need exact locations. If a {{SKILL:name}} marker cannot be resolved, SAgent removes it from the ASK prompt and prefixes the visible answer with ASK_WITHOUT_SKILL ....
Reusable helper libraries can live in .agent-dsl/exec/lib and be imported from both regular EXEC scripts and named EXEC functions:
const { readRanges } = await dsl.lib("kit.ts");dsl.lib("kit") defaults to kit.ts; paths are restricted to .agent-dsl/exec/lib.
Bundled skill packages can also provide EXEC libraries and functions:
const kit = await dsl.lib("skill:exec-kit/kit.ts");
await dsl.function("skill:some-skill/function/check\ninput");skill:name/path.ts resolves through the normal skill resolver and materializes the selected file under .agent-dsl/exec/.skill-cache before Deno imports it. %% SKILL name remains read-only and never installs or runs code by itself.
SAgent loads:
system.mdfrom the same directory assagent.exe;- global and project
AGENTS.mdinstructions.
The packaged SAgent build disables MCP startup to avoid unrelated MCP startup failures and reduce noise.
Version
Current npm package version: 2.2.30.
Highlights in this release:
- synchronization with 157 upstream Codex commits, including GPT-5.6 metadata, persisted turn items, HTTP/proxy improvements, Windows sandbox fixes, and dependency security updates;
- direct
dsl_batchexposure for new models even when their metadata requestscode_mode_only; - restored compact TUI command, ASK, and ERR icons through the canonical command lifecycle;
- native hybrid semantic search sidecars for Windows and Linux.
See CHANGELOG.md for release history and PLANNED_CHANGES.md for planned improvements.
Why SAgent is different
Raw DSL instead of JSON
dsl_batch is a raw DSL, not JSON. The model writes commands directly:
%% READ src/app.ts
%% PATCH
*** Begin Patch
*** Update File: src/app.ts
@@
-console.log("old")
+console.log("new")
*** End PatchThis is easier for coding tasks because patches, regexes, shell scripts, and multi-line code do not need JSON string escaping.
Fewer tool calls
One batch can combine related work:
%% FILES src --limit 80
%% RG --summary "TODO|FIXME" src
%% READ --outline src/main.ts
%% RUN 120
npm testSAgent is designed to encourage this workflow:
- search;
- read;
- edit;
- run tests;
- inspect logs;
- continue from failure.
Prefer one comprehensive dsl_batch per step instead of many tiny calls. The --ask and --gen modes use a fast, cheap single-shot model and are intended to reduce raw context and boilerplate while keeping work batched.
Recovery with RESUME
If a batch fails in the middle, completed commands are saved as done and the failed command plus the remaining commands stay in .agent-dsl/tasks/<N>.dsl.
The model can resume that saved task from anywhere in a later batch. Commands before and after RESUME still belong to the current batch:
%% NOTE before
%% RESUME 12
old broken command text
%% |
fixed command text
%% NOTE afterOr skip a no-longer-needed failed command:
%% RESUME 12 --skip
%% RESUME 12 --skip 2Successful work is not repeated.
If a resumed task fails, the current batch stops at RESUME. Use %%? RESUME ... when the resumed failure should be reported as a warning and the current batch should continue.
Passive feedback
SAgent appends useful passive blocks to later dsl_batch results:
CURRENT_PLAN
2/5: Implement parser
UNREAD_LOGS
7 out=12 err=1
8 out=4
NOTIFICATIONS
- BG 7 exited:0 npm run devThis helps the model track plans, background logs, and finished processes without extra status calls.
ASK summarization
SAgent can call a small single-shot model from the DSL.
%% ASK
Answer only OK if this README explains installation clearly.
%% ..Most commands also support a global --ask "prompt" post-processing argument:
%% RG "GameEngine" src --ask "Choose the 3 most relevant files to read next"
%% RUN 120 --ask "If tests failed, return only the failing file, error, and likely cause. If OK, answer OK."
npm testThe command runs normally, but the visible result is replaced by the ASK model's concise answer. This is useful for reducing large search, test, log, and web outputs. If the command fails, ASK receives the failure text and the command still fails unless the directive is optional with %%?.
For edit commands, --ask sends the compact edit summary plus diff hunks to the ASK model. Use --ask-full "prompt" when the ASK model also needs post-edit file contents. Full-file context is hidden from the main model and is not added to the read journal. All fast-model requests (--ask, --ask-full, ASK, dsl.ask, and --gen) are capped to roughly a 100K-token prompt budget.
To keep history compact, visible command headers omit long --ask / --ask-full prompts and --gen, replacing them with short ASK, ASK_FULL, and GEN markers.
For multi-command summarization, use an ASK capture block:
%% ASK --begin
%% RG "GameEngine" src
%% READ --outline src/game/engine.ts
%% ASK --end
Summarize the captured output and choose the next 3 files to inspect.
%% ..Commands between ASK --begin and ASK --end run normally, but their visible output is captured and replaced by the single ASK answer. If a captured command fails before ASK --end, SAgent aborts the capture, prints the captured output, and the batch fails normally.
Cheap context compression patterns
Use --ask on one noisy command when you only need a decision, ranking, or summary:
%% RG "handleSubmit" src --ask "Pick the 3 files most likely to need edits."
%% RUN 120 --ask "If failed: file, error, likely cause. If OK: answer OK."
npm testUse ASK --begin / ASK --end to compress several command outputs into one answer:
%% ASK --begin
%% READ --outline src/a.ts
%% READ --outline src/b.ts
%% READ --outline src/c.ts
%% ASK --end
Choose the 1-2 files to inspect next and explain briefly.
%% ..Use two-level compression for broad searches: each command summarizes itself with --ask, then the final capture prompt chooses the next action:
%% ASK --begin
%% READ --outline src/a.ts --ask "Does this file contain auth logic? Briefly."
%% READ --outline src/b.ts --ask "Does this file contain auth logic? Briefly."
%% READ --outline src/c.ts --ask "Does this file contain auth logic? Briefly."
%% ASK --end
Choose the 1-2 files most likely relevant to the auth bug.
%% ..Command reference
Every command starts with %%.
Prefix a command with %%? to make failure a warning and continue the batch.
Read and inspect
%% READ path
%% READ --fresh path
%% READ -n path:10-40
%% READ --outline path
%% READ --def name[,name2] path
%% READ --fn name[,name2] path
%% USES name [path] [--all] [--summary]
%% FILES [path ...] [--limit N]
%% FILES [path ...] --hash
%% FILES [path ...] --changed
%% JSON path [dot.path]
%% RG [--summary] [--limit N] args...
%% CG query
%% CG [status|sync|query|explore|node|files|callers|callees|impact|affected] args...
%% SEM "natural language query"
%% SEM status
%% SEM [--path path] [--glob mask] [--ext ext] [--label name] [--no-tests] "query"
%% AROUND path "text" [--before N] [--after N]
%% EXPECT path exists
%% EXPECT path not-exists
%% EXPECT path contains "text"
%% EXPECT path not-contains "text"
%% EXPECT path --def name[,name2]
%% ASK [seconds|--timeout N] [--model model]
prompt text
%% ..
%% ASK --begin
commands to capture...
%% ASK --end
prompt for captured output
%% ..
%% EXEC [seconds|--timeout N]
await dsl.READ("src/app.ts");
dsl.PRINT("visible output");
%% JS [seconds|--timeout N]
await dsl.RG('"GameEngine" src --summary');Aliases:
READ --fn: --func
AROUND: READAROUND, LINES
READ --fresh: --forceCodeGraph (%% CG) uses the indexed project graph for fast symbol/file navigation and impact checks. First non-status use initializes .codegraph if needed; after file edits, SAgent starts a background codegraph sync when an index exists. Examples:
%% CG start_deno_exec
%% CG --ask "Pick files, risks, and NEXT DSL."
start_deno_exec
resolve_profile_attempts
%% CG impact start_deno_exec --depth 3
%% CG node codex-rs/core/src/compact.rsSemantic search (%% SEM) builds a project-local index under .agent-dsl/semantic and combines dense embeddings with lexical identifier matching. Indexing runs in the background through the packaged sagent-semantic-index sidecar, while queries use the read-only sagent-semantic-query sidecar.
%% SEM status
%% SEM "where is command execution rendered in the TUI?"
%% SEM --path codex-rs/core --no-tests "canonical tool lifecycle"
%% SEM --ask "{{ranges}} Pick the 3 ranges to inspect next." "command badge rendering"Results include symbols, signatures, CodeGraph caller/callee context when available, and ready-to-use READ ranges. Scope, labels, embedding providers, and optional reranking can be configured in .agent-dsl/config.toml.
Structural commands support:
- JavaScript;
- TypeScript;
- JSX / React;
- TSX / React;
- Vue SFC
scriptandscript setup; - Rust.
Structural commands include:
READ --outline;READ --def;READ --fn;USES;REPLACE --def;COPY --def;MOVE --def;DELETE --def.EXPECT --def.
Raw READ output is remembered in memory while it remains safe to assume the model has seen it. If the same unchanged file/range is read again, SAgent can answer with a short already read unchanged message instead of re-sending the content. READ -n records numbered coverage separately: raw READ can reuse raw or numbered coverage, while numbered READ only reuses numbered coverage. Use READ --fresh (or --force) when you need the bytes printed again. Reads hidden behind --ask, ASK --begin, or EXEC are not remembered, and the memory is cleared after context compaction.
EXPECT
Use EXPECT to assert preconditions before edits or postconditions after commands:
%% EXPECT src/game/engine.ts exists
%% EXPECT src/game/engine.ts contains "createSnapshot"
%% EXPECT src/game/engine.ts not-contains "legacySnapshot"
%% EXPECT src/game/engine.ts --def createSnapshotIf an expectation fails, the batch stops and the failed EXPECT plus following commands remain in .agent-dsl/tasks/<N>.dsl for RESUME.
Use %%? EXPECT ... for a soft check that warns and continues. Failure messages include matching lines, closest lines, or available definitions where possible.
READ --outline
Returns a structural map of the file:
- imports;
- top-level definitions;
- classes and methods;
- functions;
- constants;
- React components;
- Vue definitions;
- Rust items.
READ --def
Reads exact definitions by symbol name:
%% READ --def createSnapshot,GameEngine src/game/engine.tsUse this instead of reading a whole large file when only specific definitions are needed.
USES --summary
Shows compact per-file usage counts:
%% USES createSnapshot src --summaryUse --all to include hidden or less direct references.
FILES --changed
Compares against the last FILES --hash baseline.
If no baseline exists, SAgent returns NO_BASELINE and continues the batch.
Global --ask
Use --ask "prompt" on a command when the raw output is large and you only need a model-produced answer:
%% SH --log 7 --ask "Extract the local dev server URL only"ASK treats command output as untrusted data and answers the prompt without tools.
--ask is fast and cheap; use it to select the important parts from large READ, RG, test, log, and web outputs before loading more context.
EXEC / JS
EXEC runs JavaScript through Deno and gives the script async access to the same DSL command handlers that normal %% commands use.
%% EXEC --timeout 45
const files = await dsl.FILES("src --limit 80");
const choice = await dsl.ask(files + "\nPick the 2 files most likely related to auth.");
dsl.PRINT(choice);Key rules:
all regular DSL commands are available as
await dsl.READ(...),await dsl.RG(...),await dsl.PATCH(...),await dsl.PLAN(...), etc.;arguments and flags are the same as the text after
%% COMMAND;multiline command bodies use template strings:
await dsl.PATCH(`--gen src/game/engine.ts --ask "Summarize the change" Add a guard for empty enemy waves. Keep public signatures unchanged.`);dsl.EXECanddsl.JSare intentionally unavailable inside EXEC to avoid recursive script runners;visible successful output must be written with
dsl.PRINT(value)ordsl.print(value);console.log/ stdout are hidden on success and shown only as diagnostics when the script fails;dsl.ask(prompt, choices?)calls the fast ASK model;choicescan constrain answers, for example["yes", "no"];dsl.try.COMMAND(...)returns{ ok, output }or{ ok: false, error, response }instead of throwing;persistent JSON state is available through
dsl.state.set(key, value),dsl.state.get(key), anddsl.state.exists(key).
Generated scripts are stored under .agent-dsl/exec/<task>-<step>.mjs. The local RPC port and token are passed through environment variables and are not written into the script.
EXEC requires Deno in PATH.
Edit files
%% CREATE path
file body
%% CREATE path --diff
file body
%% CREATE path --after
append body
%% CREATE path --before
prepend body
%% CREATE --gen path
instruction for generating the file/content
%% REPLACE path
old text
%% |
new text
%% REPLACE path --after
anchor text
%% |
inserted text
%% REPLACE path --before
anchor text
%% |
inserted text
%% REPLACE path --def
export function name() {
return true
}
%% REPLACE path --diff
old text
%% |
new text
%% REPLACE --gen path --def name[,name2]
instruction for generating replacement definition bodies
%% PATCH
*** Begin Patch
*** Update File: file.txt
@@
-old
+new
*** End Patch
%% PATCH --diff
*** Begin Patch
*** Update File: file.txt
@@
-old
+new
*** End Patch
%% PATCH --gen path [path2 ...]
instruction for generating an apply_patch patch for only those files
%% COPY src -> dst
%% COPY src --def name[,name2] -> dst
%% MV src -> dst
%% MOVE src --def name[,name2] -> dst
%% DELETE path
%% DELETE path --def name[,name2]Aliases:
COPY: CP
MV: MOVE, RENAME
DELETE: RM, DELSymbol-aware editing
Prefer REPLACE --def when replacing a whole function, class, component, or Rust item:
%% REPLACE src/file.ts --def
export function foo() {
return 1
}
export function bar() {
return 2
}REPLACE --def:
- takes only new definition bodies;
- finds matching definitions by name;
- can replace multiple definitions in one command;
- validates replacement syntax before modifying the file.
Definition copy/move/delete
%% COPY src/game/engine.ts --def createSnapshot -> src/game/snapshot.ts
%% MOVE src/game/engine.ts --def createSnapshot -> src/game/snapshot.ts
%% DELETE src/game/engine.ts --def createSnapshotThese commands operate on named definitions instead of raw text.
They do not currently auto-fix imports, exports, or call-sites. A planned --fix-imports mode is listed in PLANNED_CHANGES.md.
Fast generated edits with --gen
--gen uses the same fast, cheap single-shot model as ASK to generate small, well-scoped edits without making the main model hand-write boilerplate.
%% CREATE --gen src/game/damage.ts
Create a small damage helper module with applyDamage(unit, amount).
Clamp health at 0 and export the function.
%% PATCH --gen src/game/engine.ts src/game/damage.ts
Use applyDamage from damage.ts in the engine damage flow.
%% REPLACE --gen src/game/engine.ts --def applyDamage
Keep the same signature, but clamp health at 0 and preserve existing return shape.CREATE --gen pathgenerates file/content text and writes it throughCREATE.PATCH --gen path...reads the listed files, asks for anapply_patchpatch, verifies that only those files are touched, and applies it.REPLACE --gen path --def name[,name2]reads the current definitions, asks for replacement definition bodies, and validates them throughREPLACE --def.
Use --gen for targeted local edits, boilerplate, and tests. For broad architecture work, still inspect the code first and batch READ/PATCH/RUN together.
Syntax diagnostics
After file edits, SAgent prints compact summaries such as:
REPLACE src/app.ts M +3 -1
PATCH
M src/a.ts +3 -1
M src/b.ts +10 -4For single-target CREATE and REPLACE, the target path is already in the command header, so the compact summary omits the duplicate path. PATCH keeps per-file paths because it can touch many files. Use --diff on CREATE, REPLACE, or PATCH when you need visible DIFF hunks. Otherwise, prefer the compact summary to save context. SAgent also runs parser-based syntax diagnostics for supported languages and prints SYNTAX WARN when it detects parse errors.
Diagnostics are tree-sitter based and may miss grammar-accepted invalid constructs.
Shell, tests, and background jobs
%% SH [seconds]
command body
%% RUN [seconds]
command body
%% SH 0 [--tty]
long-running command body
%% WAIT N
%% SH --status [job_id]
%% SH --stop [job_id]
%% SH --log job_id [--limit N]
%% SH --log job_id --all [--limit N]
%% SH --send job_id [--noAwait] [--timeout N]
input for the background processSH and RUN execute shell commands from the project root.
WAIT N pauses the batch for N seconds without invoking a shell, which is useful before checking background job logs.
Timed command
%% RUN 120
npm testBackground command
%% SH 0
npm run devReturns:
job_id=7
pid=12345
mode=normal
status=runningSH 0 starts outside the sandbox through the exec approval flow.
Interactive TTY job
%% SH 0 --tty
deno repl --deny-read --deny-write --deny-net --deny-env --deny-run --deny-ffi --deny-sys --deny-importSend input:
%% SH --send 7 --timeout 3
1 + 2SH --send waits for output by default and advances the log cursor.
Use --noAwait to send without waiting.
For interactive programs such as SSH, SFTP, and REPLs, SAgent sends terminal Enter as CRLF.
Logs
%% SH --log 7Returns only unread stdout/stderr since the previous log read and advances per-stream cursors.
%% SH --log 7 --allReturns the full log without advancing cursors.
%% SH --log 7 --limit 50In normal unread mode, keeps only the newest 50 unread lines, advances the cursor to the current log end, and reports omitted_before_limit=M if older unread lines were skipped.
For --tty jobs, the PTY stream is shown as STDOUT; STDERR is omitted.
Status and stop
%% SH --status
%% SH --status 7
%% SH --stop 7
%% SH --stopSH --status lists only active jobs.
SH --stop kills the process tree.
Notes
%% NOTE short noteAdds a note to the batch output.
Recovery
%% STATUS [task_name] [--limit N]Shows unfinished saved commands.
%% RESUME task_name
old text in saved task
%% |
new text for saved taskRESUME:
- loads
.agent-dsl/tasks/<task_name>.dsl; - optionally applies exact text replacement or
--skip [N]; - runs that saved task separately;
- returns to the current task and continues commands after
RESUME; - moves successful resumed commands to
<task_name>.ok; - leaves failed and remaining resumed commands in
<task_name>.dsl.
RESUME can appear anywhere in a batch and can be used multiple times. If it fails, the current task stops at the RESUME command. %%? RESUME ... reports the resumed task name, failed command, and remaining command count, then continues the current task.
Planning
SAgent provides a DSL-native plan cursor.
%% PLAN --new Optional explanation
[>] First step
multiline detail
[ ] Second step
[ ] Third step
[x] Already completed stepMarkers:
[>]current step;[ ]pending step;[x]completed step.
Multiline step details continue until the next marker.
If no [>] exists, the first [ ] becomes current in memory.
File-backed plan
%% PLAN --open PLAN.md Optional explanationor:
%% PLAN --load PLAN.md Optional explanationThe plan file uses the same marker format:
[>] Investigate current behavior
details may span multiple lines
[ ] Implement file-backed PLAN
[ ] Test synchronization
[x] Old completed stepFor file-backed plans:
- the file is the source of truth;
- SAgent re-reads it on later
dsl_batchcalls; - manual edits are picked up automatically;
PLAN --doneis the only command that writes markers back to the file;PLAN --clearonly clears the active plan/binding and does not modify the file.
If multiple [>] markers exist, SAgent uses the first as current and treats later current markers as pending in memory. PLAN --done normalizes markers when it writes the file.
Plan commands
%% PLAN --done Optional explanation
%% PLAN --clear Optional explanation
%% PLAN --show --limit N
%% PLAN --showAll
%% PLAN --show-allWhile a plan is active, later dsl_batch outputs append:
CURRENT_PLAN
2/5: Current step descriptionWhen every step is complete, CURRENT_PLAN stops appearing, but the completed plan remains available through PLAN --showAll until cleared.
Web commands
%% WEB_SEARCH query response_length=short
%% WEB_IMAGE_SEARCH query response_length=short
%% WEB_IMG_SEARCH query response_length=short
%% WEB_OPEN ref_or_url lineno=N
%% WEB_FIND ref_or_url pattern
%% WEB key=value ...
%% WEB
{"open":[{"ref_id":"https://example.com","lineno":5}]}WEB accepts key/value arguments or a raw JSON block.
Image commands
%% IMAGE_GEN filename
prompt text
%% IMG_GEN filename
prompt text
%% VIEW_IMAGE path
%% VIEW_IMAGE path detail=originalIMAGE_GEN / IMG_GEN generate PNG images.
VIEW_IMAGE loads a local image for visual inspection.
Golden workflows
Safely refactor a function
%% READ --outline src/file.ts
%% READ --def targetFunction src/file.ts
%% USES targetFunction src --summary
%% REPLACE src/file.ts --def
export function targetFunction() {
return "updated"
}
%% RUN
npm testStart a dev server and inspect logs
%% SH 0
npm run dev
%% WAIT 10
%% SH --status
%% SH --log 1Later:
%% SH --log 1
%% SH --stop 1Work with a large plan
%% PLAN --open PLAN.md
%% READ --outline src/main.ts
%% RG --summary "createApp" srcAfter the active step is complete:
%% PLAN --done implemented parser lookupRuntime files
The sagent-ai meta package ships the launcher and selects a native package for the current platform:
sagent-ai
├── bin/sagent.js
├── sagent-ai-win (Windows x64)
└── sagent-ai-linux (Linux x64)Each native package contains:
bin/sagent
bin/apply_patch
bin/sagent-semantic-index
bin/sagent-semantic-query
bin/rg
bin/system.md
bin/dsl_runtime.tsWindows uses the corresponding .exe filenames. system.md and dsl_runtime.ts are loaded next to the native binary, semantic sidecars power %% SEM, and the bundled rg binary powers %% RG.
Summary
SAgent keeps the Codex foundation and changes the model-facing workflow into a compact script-like protocol.
The result is a coding agent workflow with:
- fewer tool calls;
- less JSON overhead;
- readable command history;
- reliable batch recovery;
- structural code navigation and edits;
- background and interactive process control;
- passive status feedback;
- file-backed planning;
- web and image support.
It is designed for real project work: search, inspect, edit, test, run, recover, and continue.
