Syntax Reference
agent.lua Contract
Section titled “agent.lua Contract”local nudges = require("ghost.nudges") -- optionallocal template = require("ghost.template")
return { -- Required name = "my-agent", description = "What this agent does",
-- Model settings (all optional) model = nil, -- nil = default, or "fast", "strong" reasoning_effort = nil, -- nil, "low", "medium", "high" max_iterations = 30, -- max tool loop iterations
-- Tools and skills tools = { "web_search", "web_fetch", "file_read", "todo" }, skills = { "note-writer" },
-- Hooks (all optional except build) build = function(ctx, args) ... end, -- required pre_turn = nudges.compose(...), -- before each turn on_end_turn = nudges.progress_gate(...), -- gate final response post_completion = function(ctx) end, -- after agent finishes}Agents declare which tools they can use via the tools list:
| Tool | Description |
|---|---|
web_search | Search the web |
web_fetch | Fetch and extract web page content |
file_read | Read files from the workspace |
file_write | Write files to the workspace |
file_edit | Edit files in place |
shell | Execute shell commands |
knowledge_search | Search the knowledge base |
note_write | Create knowledge notes |
todo | Manage a TODO checklist |
agent | Start, stop, and check agents |
If an agent declares skills, file_read is automatically added.
Custom Tools
Section titled “Custom Tools”Define agent-specific tools directly in agent.lua:
custom_tools = { { name = "my_tool", description = "What the tool does", parameters = { { name = "input", type = "string", description = "The input", required = true }, }, terminal = false, -- if true, result ends the session handler = function(ctx, args) return "tool result" end, },},| Hook | When | Return |
|---|---|---|
build | Before first turn | { system_prompt, messages } |
pre_turn | Before each model call | Nudge message or nil |
on_end_turn | After model’s final response | Rejection message or nil |
post_completion | After agent finishes | — (may call ctx:spawn_agent) |
build(ctx, args)
Section titled “build(ctx, args)”Required. Returns the system prompt and initial messages:
build = function(ctx, args) return { system_prompt = template.render(read_file("prompt.md"), { date = os.date("%Y-%m-%d"), }), messages = { { role = "user", content = args.prompt or "Begin." }, }, }end,The args table comes from the caller — for dispatch agents it
contains { prompt = "..." }, for spawned agents it contains
whatever the parent passed to ctx:spawn_agent().
post_completion(ctx)
Section titled “post_completion(ctx)”Runs after the agent finishes. Can perform cleanup or spawn child agents:
post_completion = function(ctx) ctx:curate_web_cache()end,For spawning child agents with structured data, prefer terminal
custom tool handlers over post_completion — this lets the agent
decide what data to pass:
custom_tools = { { name = "report_findings", description = "Submit your final report", parameters = { { name = "report", type = "string", required = true }, { name = "sources", type = "string", required = true }, }, terminal = true, handler = function(ctx, args) ctx:spawn_agent("deep-research-reflection", { report = args.report, sources = args.sources, }) return "Reflection spawned." end, },},Nudge Library (ghost.nudges)
Section titled “Nudge Library (ghost.nudges)”The nudge library provides composable functions for pre_turn and
on_end_turn hooks.
nudges.compose(...)
Section titled “nudges.compose(...)”Combines multiple nudge functions into one. Each function is called
in order; non-nil results are collected and wrapped in a
<system-reminder> block.
nudges.todo_list()
Section titled “nudges.todo_list()”Injects the current TODO list into the agent’s context.
nudges.iteration_countdown
Section titled “nudges.iteration_countdown”Count down remaining iterations:
nudges.iteration_countdown({ { remaining = 10, message = "{remaining} iterations left. Prioritize." }, { remaining = 5, message = "Only {remaining} left. Stop new work." }, { remaining = 2, message = "FINAL: {remaining} left. Write your report." },})nudges.temporal
Section titled “nudges.temporal”Fire after a wall-clock duration with escalating messages:
nudges.temporal({ after_seconds = 300, messages = { "Working for {minutes} minutes. Start wrapping up.", "STOP new work. Write your report NOW.", },})nudges.context_pressure
Section titled “nudges.context_pressure”Fire when context window usage exceeds a threshold:
nudges.context_pressure({ threshold_pct = 0.80, message = "Context over 80% full. Finish with what you have.",})nudges.progress_gate
Section titled “nudges.progress_gate”Block end-of-turn until TODO items are completed:
nudges.progress_gate({ no_todo = "REJECTED — create a TODO plan first.", incomplete = "REJECTED — {incomplete} items remain.",})nudges.tool_count
Section titled “nudges.tool_count”Nudge when a tool hasn’t been called enough:
nudges.tool_count({ tool = "web_fetch", min = 7, nudge = "Need {min} {tool} calls (have {count}).",})nudges.recency
Section titled “nudges.recency”Nudge when a tool hasn’t been used recently:
nudges.recency({ tool = "web_fetch", window = 3, message = "You haven't fetched any pages recently.",})prompt.md Template
Section titled “prompt.md Template”Write a focused system prompt using {{variable}} interpolation:
# Agent Name — Purpose
You are in autonomous mode. Today is {{date}}.
**A text-only response (no tool calls) ends your session.**
## Workflow
1. First step2. Second step3. Handoff (text-only final message)