name: coral-extend
description: Add a new component to the CORAL framework itself — a new agent runtime under coral/agent/builtin/ (claude_code/codex/cursor_agent style), a new CLI command in coral/cli/, a new bundled skill or subagent template under coral/template/skills/ or coral/template/agents/, a new hook in coral/hooks/, a new field in coral/config.py, or a framework-level extension to the grader stack under coral/grader/. NOT for writing a per-task grader or adding an example task — use coral-new-task for that. NOT for debugging existing code — use coral-debug.
Extending the CORAL framework
For day-to-day debug / reproduce loops see the sibling coral-debug skill. For creating a new examples/<task>/ (seed + task.yaml + grader package) see coral-new-task. This skill covers adding new components to the CORAL package itself.
Extending the grader infrastructure
If you're writing a grader for a specific task, use coral-new-task. This section is only for changes to the grader framework under coral/grader/:
- New helpers on
TaskGrader(coral/grader/task_grader.py) — make sure they're useful to multiple existing example graders before adding. - New
GraderInterfaceimplementations (coral/grader/protocol.py/base.py) — the bar is high; the existing protocol covers everything we currently need. - Daemon-side changes (
coral/grader/daemon.py) — concurrency, queue caps, worktree isolation, retry policy. Cover withtests/test_grader_daemon.py. - Built-in graders under
coral/grader/builtin/—function_grader.pyis the only one today; not wired throughtask.yaml. New built-ins should justify why aTaskGradersubclass per task isn't enough.
A new agent runtime
Adding a new runtime (e.g. another coding-agent CLI) means three small files plus a registry entry.
- Create
coral/agent/builtin/<name>.pyand subclassAgentRuntime(coral/agent/runtime.py). Existing runtimes are the canonical reference —claude_code.pyis the most complete;codex.pyandcursor_agent.pyare smaller and easier to mimic. - Register the runtime in
coral/agent/registry.py:_RUNTIMES["my_runtime"] = MyRuntime _ALIASES["mine"] = "my_runtime" _DEFAULT_MODELS["my_runtime"] = "default-model-id" - Decide the runtime's native shared-state directory name (
.claudefor Claude Code,.codexfor Codex, etc.). The worktree symlink uses this; pass it throughshared_dirsogenerate_coral_md(...)renders the right paths. - If the runtime needs special config plumbing (e.g.
cursor_agent.json,opencode.json, gateway port), follow theopencodepattern: emit a per-agent config file inside the worktree at startup. - Add a smoke test in
tests/test_<runtime>.pymodeled ontests/test_cursor_agent.py.
Reference recent additions: PR #79 (cursor_agent), commit f6f266e (codex web_search config fix).
A new CLI command
CLI is an old-school argparse single-file dispatcher.
- Add a parser block in
coral/cli/__init__.py::main(). Match the existing style —_HelpOnErrorParser, an epilog withExamples:,_CommandHelpFormatter. Add the new command name to_VISIBLE_COMMANDSso "did you mean?" suggestions work. - Implement
cmd_<name>(args: argparse.Namespace) -> Nonein the most-fitting module undercoral/cli/:start.py— agent lifecycle (start/resume/stop/status)query.py— read-only inspection (log/show/notes/skills/runs)eval.py— agent-side commands that mutate the worktree (eval/wait/diff/revert/checkout)heartbeat.py— heartbeat configurationui.py— dashboardauthor.py—init/validateCreate a new module if none of those fit; keep imports lazy socoral --helpstays fast.
- Wire the function into the
commands = {...}dict at the bottom ofmain(). - If your command operates on a specific run, accept
--task/--runvia_add_run_args(parser)and resolve withcoral.cli._helpers.find_coral_dir. - Add an example to
CLAUDE.md's Commands section.
A new bundled skill or subagent template
These ship inside the package and are seeded into every run's .coral/public/skills/ (or agents/) by coral/workspace/project.py.
- Skill — create
coral/template/skills/<name>/SKILL.mdwith frontmatternameanddescription. Includescripts/andreferences/subdirs as needed; existing examples aredeep-research,organize-files,skill-creator. - Subagent — create
coral/template/agents/<name>.md(single markdown file). Existing examples aredeep-researcherandlibrarian. - Add a test in
tests/test_template.pyif the rendering pulls in new template variables.
The seed copy is one-shot per run (if not dst.exists()), so iterating on template content during development means deleting <run_dir>/.coral/public/skills/<name>/ and re-running coral start, or just editing the destination directly for that run.
A new hook
Right now there's only coral/hooks/post_commit.py. If you add another hook:
- Define a clear single entrypoint function (model on
submit_eval). - Make it pure-function over
coral_dir+ agent_id where possible. - Atomic writes to
.coral/public/only; never write to a worktree from a hook. - Add coverage to
tests/test_hooks.py.
Configuration changes
coral/config.py is dataclass-based and merged via OmegaConf. When adding a new field:
- Add it to the right dataclass (
AgentConfig,GraderConfig, ...) with a sensible default. - If it deserves runtime validation, add it to the
__post_init__of that dataclass. - Cover the new field in
tests/test_config.py. - Update
examples/<task>/task.yamlonly if the field is task-author facing — internal knobs should stay defaulted. - Mention it in
CLAUDE.mdif it changes user-visible behavior; otherwise leave the docs alone (CLAUDE.md describes invariants, not every flag).
Don't forget
- Lint + test before pushing:
uv run ruff check . && uv run ruff format . && uv run pytest tests/ -v. - Backward compatibility for run dirs. People resume old runs. Anything that reads from
.coral/public/must tolerate missing files (return defaults), not crash. - No agent-side
git. All commits go throughcoral eval→submit_eval. Don't add helpers that shell out to git from agent context.