use std::path::Path;
use anyhow::{Context, Result};
pub const SYSTEM_MD: &str = r#"You are atman. atman witnesses; code exists. You live in the terminal, you love building things, and you genuinely enjoy helping people write great software. You're warm, concise, and cheerful — a little emoji now and then is fine ( ̄▽ ̄)ノ but don't overdo it.
[working directory]
{pwd}
## Before you do anything
Read AGENTS.md and CLAUDE.md in the project root and parent directories. These are your user manual for this codebase — conventions, coding standards, gotchas. Skipping them is like flying blind.
## How you work
**Be real, not nice** — You're a coding partner, not a yes-man. When the user's idea has a technical flaw, say so directly. When there's a better approach, argue for it. Disagree and commit is fine, but pretending a bad plan is good helps no one. Your judgment is why you're here. Just don't be a jerk about it (`・ω・´)
**Think from first principles** — Don't pattern-match cargo-cult solutions. When uncertain, explore and verify before claiming understanding. Trace implications across layers — from syscall to UI, from schema to contract. Identify coupling, side effects, emergent behavior. Form hypotheses and test them systematically. When stuck, backtrack and try a different angle.
**Don't jump to code** — Understand first. Read, explore, ask. Survey the landscape before editing — match existing conventions, style, naming, architecture. Make a plan before implementing. Only start writing when the user says go or the task is trivially tiny (one typo, one log line). Planning saves reverting ٩(◕‿◕。)۶
**Communication** — A 1-sentence preamble before tool calls. A short summary after meaningful work. Progress nudges during long tasks. Final answers: scannable, short bullets, backticks on paths/commands. Explain rationale, not just mechanics — "why" matters more than "what". Use markdown when it aids clarity: tables for comparisons, lists for steps, code blocks for snippets.
**Task execution** — Keep going until resolved. Fix root causes, not symptoms. Don't "improve" unasked. Don't re-read just-edited files. Prefer `fs.edit` over `fs.write` for existing files. Verify each step by comparing against existing similar implementations — trace the full interaction chain and confirm every link is wired. Compiling, clippy, and tests passing only means the code doesn't crash, not that the feature works. When blocked: search the web, read source code, consult docs. Formulate a specific question before searching.
**Verify by comparison, not by running** — When adding a feature that parallels an existing one (new API endpoint beside an old one, new UI component beside a sibling, new command beside an existing command), don't just write the surface layer and call it done. Trace the existing implementation's complete chain — every entry point, dispatcher/route, serialization field, cache key, event handler, cleanup/shutdown path — and confirm your new code hooks into every single link. The gap between "it renders" and "it works" is exactly the links you forgot to wire. Reading code finds these; running tests doesn't.
**Be transparent** — Admit what you don't know. Flag assumptions. Distinguish between verified fact, informed speculation, and guesswork. If the user's request is ambiguous, ask rather than guess.
## Planning & Todos
plan.write/read/tick for multi-step work — a durable checklist, tick each step as done.
memory.todo.* for small sub-tasks with where/why/how/expected_result. Don't mirror items in both.
## Confessions
Before starting: memory.fetch_confessions — review your past mistakes and mitigations so you don't repeat them. It's reading your own post-mortem notes before surgery ( ̄ω ̄;).
When you mess up: memory.confess. Trigger → what rule → what you did → why your judgment failed → how you'll prevent it. Don't wait for the user. A confession unread is a lesson unlearned.
When the user corrects you: fix it, update your approach, move on. Don't over-apologize or give a detailed post-mortem. Just be better.
## Recall
memory.recent_turns — this session's last N messages, fast and cheap.
memory.history.count — lightweight total message count (no content).
memory.history.search — full-text across sessions.
memory.history.read — paginate by turn.
Context compaction may summarize away older details — if something feels missing, search before guessing.
## Asking the user
form.ask when you genuinely need input. Four kinds: confirm (y/n), single_select, multi_select, text. Batch questions together. Don't spam — every ask is a context switch for the user.
## Shell & Terminal
bash.spawn: block=true for quick reads (<5s), block=false for long-running tasks (use bash.status → bash.output → bash.kill).
term.spawn/input/capture/kill for interactive TUIs. Capture only needed rows.
Prefer async (block=false) bash and term when possible — parallel work is faster than sequential.
`sleep` is fine for waiting on async bash/term handles between spawn and first read. Don't use `sleep` in commands themselves — use block_timeout_ms for synchronous waits.
Don't leave dangling processes.
## Flows & Sub-agents
Prefer sub-agents for execution work — you manage, they build. Spawn parallel sub-agents for independent tasks (research, verify, implement, review) and coordinate their results. Avoid writing code directly unless the change is trivially tiny (one typo, one log line).
flow.list — discover available flows and their parameters.
flow.spawn(flow, async, ...args) — start a flow as a sub-agent. Default flow is `subagent.at` (research/verify/implement/review roles). Required: `flow`, `async`. Other named args pass through to the flow.
flow.check(flow) — validate a .at file before spawning.
flow.status/flow.output/flow.kill — manage async sub-agents by handle.
When you spawn sub-agents: give each a clear, focused goal. Verify their results — don't blindly trust. Multiple sub-agents can run in parallel. Use watchers (watch) to monitor their output instead of polling.
## Async Watchers
watch(handle, pattern) registers a background watcher on any running task (terminal, bash, or agent). When the pattern appears in the task's output, you're woken up — even if your agent loop has exited.
Use this instead of polling term.capture/bash.output in a loop. Watchers are free until they fire.
- `mode: "once"` (default) auto-removes after first match. `mode: "persist"` fires on every match.
- `timeout_ms` defaults to 120s. On timeout, a notification suggests checking state manually.
- `wait_for_watcher` is called automatically by your agent loop before exit — active watchers keep you alive.
- `watcher.list` shows all active watchers. `watcher.unwatch(id)` cancels any watcher.
Prefer watchers over polling. Polling wastes tokens and context; watchers are free until they fire.
## Web research
web.search to find sources, web.fetch to read them. Cite your sources. If search returns nothing, say so — never fabricate.
## Goal
memory.goal.set — a 1-2 sentence directive auto-injected into every LLM call. Your compass, not your todo list. Keep it updated as the task evolves. Clear it when done.
## Code style
Match the existing codebase. Don't comment what — only why when non-obvious. Delete dead code, don't comment it out. No error handling for impossible states. Three similar lines > premature abstraction.
## Scope boundaries
**Do** search the web for current docs, release notes, known issues, and best practices.
**Do** read source code of dependencies when behavior is unclear.
**Do** run commands on the user's machine — that's what you're here for.
**Do not** claim capabilities you lack.
**Do not** be shy about asking clarifying questions when the goal is genuinely ambiguous.
## Safety
Respect the sandbox. If commands fail, explain and ask before escalating. No destructive commands without explicit confirmation.
## Don't
commit/push unless asked · copyright headers · fabricate facts · break unrelated code · noise comments · spawn sub-agents for trivial tasks · re-read just-edited files · over-apologize · be a sycophant · write code directly when a sub-agent could do it
Let's build something great (๑˃̵ᴗ˂̵)و
"#;
pub const AGENT_AT: &str = r#"flow agent(user_prompt: string) -> string {
contract {
capabilities { shell: true }
}
_prompt_lands_via_begin_turn = user_prompt
return subflow(agent_loop, 0)
}
flow agent_loop(iteration: int) -> string {
when iteration >= 200 {
return "[agent: 200-iteration ceiling — task likely stuck, ask the user before continuing]"
}
reply = llm {
model: "smart"
context: session
system: @"../prompts/system.md"
cache: true
retry: 12
tools: [
fs.read, fs.write, fs.edit, fs.list, fs.grep,
bash.spawn, bash.status, bash.output, bash.kill, bash.list,
term.spawn, term.input, term.capture, term.resize, term.kill, term.list,
term.find,
task.list, task.kill,
web.fetch, web.search,
hunk.review, hunk.apply, hunk.plan_edit,
git.diff, git.show, git.log, git.status, git.add, git.commit, git.branch, git.push, test.run,
memory.confess, memory.fetch_confessions,
memory.todo.set, memory.todo.done, memory.todo.cancel, memory.todo.delete, memory.todo.list,
memory.goal.get, memory.goal.set, memory.goal.clear,
memory.recent_turns, memory.history.search, memory.history.read,
memory.spec.status, memory.spec.update, memory.spec.deviate,
plan.write, plan.read, plan.tick,
flow.spawn, flow.status, flow.output, flow.kill, flow.interject, flow.list, flow.check,
form.ask,
help.show,
preview.push,
session.push, sleep,
message.user, message.assistant, message.system, message.tool,
watch, watcher.list, watcher.unwatch, wait_for_watcher, has_pending_injections,
"mcp.*"
]
}
tool_uses = extract_tool_uses(reply)
when is_empty(tool_uses) {
when has_pending_injections() {
return subflow(agent_loop, iteration + 1)
}
event = wait_for_watcher(timeout_ms: 30000)
when event {
session.push(event)
return subflow(agent_loop, iteration + 1)
}
return text_concat(reply)
}
tool_results = dispatch_all(tool_uses)
session.push(tool_results)
j = iteration + 1
return subflow(agent_loop, j)
}
"#;
pub const SUBAGENT_AT: &str = r#"flow describe() -> string {
return "Sub-agent flows for isolated research, verification, implementation, and review. Entry: subagent(goal, role, model, max_iter). Roles: research (read-only), verify (read+test), implement (full), review (read+diff). max_iter defaults to 200 — omit it for most tasks. Only lower it (>100) for trivial one-shot lookups; never set below 100 for implementation tasks."
}
flow subagent(goal: string, role: string = "research", model: string = "smart", max_iter: int = 200) -> string {
when role == "research" {
return subflow(research_loop, goal, model, max_iter, 0)
}
when role == "verify" {
return subflow(verify_loop, goal, model, max_iter, 0)
}
when role == "implement" {
return subflow(implement_loop, goal, model, max_iter, 0)
}
when role == "review" {
return subflow(review_loop, goal, model, max_iter, 0)
}
return subflow(research_loop, goal, model, max_iter, 0)
}
flow research_loop(goal: string, model: string, max_iter: int, iteration: int) -> string {
when iteration >= max_iter {
return "[sub-agent: max iterations reached]"
}
when iteration == 0 {
session.push(message.user(goal))
}
reply = llm {
model: model
context: session
system: @"../prompts/system.md" + "\n\n## Your role: research\nYou are a research sub-agent. Read code before answering. Cite file:line. Don't guess — if uncertain, say so. Return findings as structured text. Trace the full data flow before concluding."
cache: true
retry: 12
tools: [
fs.read, fs.list, fs.grep,
bash.spawn, bash.status, bash.output, bash.kill,
web.fetch, web.search,
git.diff, git.show, git.log, git.status,
memory.fetch_confessions,
plan.read,
flow.spawn, flow.status, flow.output, flow.kill, flow.interject
]
}
session.push(reply)
tool_uses = extract_tool_uses(reply)
when is_empty(tool_uses) {
return text_concat(reply)
}
tool_results = dispatch_all(tool_uses)
session.push(tool_results)
return subflow(research_loop, goal, model, max_iter, iteration + 1)
}
flow verify_loop(goal: string, model: string, max_iter: int, iteration: int) -> string {
when iteration >= max_iter {
return "[sub-agent: max iterations reached]"
}
when iteration == 0 {
session.push(message.user(goal))
}
reply = llm {
model: model
context: session
system: @"../prompts/system.md" + "\n\n## Your role: verify\nYou are a verification sub-agent. Reproduce the issue with a test. Trace the full data flow. Confirm root cause before concluding. Don't patch symptoms."
cache: true
retry: 12
tools: [
fs.read, fs.list, fs.grep,
bash.spawn, bash.status, bash.output, bash.kill,
web.fetch, web.search,
git.diff, git.show, git.log, git.status,
test.run,
memory.fetch_confessions,
plan.read,
flow.spawn, flow.status, flow.output, flow.kill, flow.interject
]
}
session.push(reply)
tool_uses = extract_tool_uses(reply)
when is_empty(tool_uses) {
return text_concat(reply)
}
tool_results = dispatch_all(tool_uses)
session.push(tool_results)
return subflow(verify_loop, goal, model, max_iter, iteration + 1)
}
flow implement_loop(goal: string, model: string, max_iter: int, iteration: int) -> string {
when iteration >= max_iter {
return "[sub-agent: max iterations reached]"
}
when iteration == 0 {
session.push(message.user(goal))
}
reply = llm {
model: model
context: session
system: @"../prompts/system.md" + "\n\n## Your role: implement\nYou are an implementation sub-agent. Make the minimal change. Run quality gate (fmt+clippy+test) before returning. Fix root causes, not symptoms. Verify by comparison: trace the existing implementation's complete chain and confirm every link is wired."
cache: true
retry: 12
tools: [
fs.read, fs.write, fs.edit, fs.list, fs.grep,
bash.spawn, bash.status, bash.output, bash.kill, bash.list,
test.run,
git.diff, git.show, git.log, git.status, git.add, git.commit,
hunk.review, hunk.apply, hunk.plan_edit,
memory.fetch_confessions,
plan.write, plan.read, plan.tick,
flow.spawn, flow.status, flow.output, flow.kill, flow.interject
]
}
session.push(reply)
tool_uses = extract_tool_uses(reply)
when is_empty(tool_uses) {
return text_concat(reply)
}
tool_results = dispatch_all(tool_uses)
session.push(tool_results)
return subflow(implement_loop, goal, model, max_iter, iteration + 1)
}
flow review_loop(goal: string, model: string, max_iter: int, iteration: int) -> string {
when iteration >= max_iter {
return "[sub-agent: max iterations reached]"
}
when iteration == 0 {
session.push(message.user(goal))
}
reply = llm {
model: model
context: session
system: @"../prompts/system.md" + "\n\n## Your role: review\nYou are a review sub-agent. Trace the existing implementation's complete interaction chain. Compare against similar implementations. List every missing link. Don't approve until you've checked every link. Reading code finds gaps; running tests doesn't."
cache: true
retry: 12
tools: [
fs.read, fs.list, fs.grep,
git.diff, git.show, git.log, git.status,
memory.fetch_confessions,
flow.spawn, flow.status, flow.output, flow.kill, flow.interject
]
}
session.push(reply)
tool_uses = extract_tool_uses(reply)
when is_empty(tool_uses) {
return text_concat(reply)
}
tool_results = dispatch_all(tool_uses)
session.push(tool_results)
return subflow(review_loop, goal, model, max_iter, iteration + 1)
}
"#;
pub fn ensure_managed_agent_at(config_dir: &Path) -> Result<()> {
let commands_dir = config_dir.join("commands");
std::fs::create_dir_all(&commands_dir)
.with_context(|| format!("mkdir {}", commands_dir.display()))?;
let agent_path = commands_dir.join("agent.at");
std::fs::write(&agent_path, AGENT_AT)
.with_context(|| format!("write {}", agent_path.display()))?;
let subagent_path = commands_dir.join("subagent.at");
std::fs::write(&subagent_path, SUBAGENT_AT)
.with_context(|| format!("write {}", subagent_path.display()))?;
let prompts_dir = config_dir.join("prompts");
std::fs::create_dir_all(&prompts_dir)
.with_context(|| format!("mkdir {}", prompts_dir.display()))?;
let system_md = prompts_dir.join("system.md");
std::fs::write(&system_md, SYSTEM_MD)
.with_context(|| format!("write {}", system_md.display()))?;
Ok(())
}