Compare commits

932 Commits
Author SHA1 Message Date
Dark-Alex-17 00a21ee777 Merge branch 'main' of github.com:Dark-Alex-17/coyote
CI / All (macos-latest) (push) Waiting to run
CI / All (windows-latest) (push) Waiting to run
CI / All (ubuntu-latest) (push) Failing after 32s
2026-08-31 13:49:09 -06:00
Dark-Alex-17 016f2654e4 fix: make binary shims for custom tools cross-device compatible so users can't accidentally break sandboxes via sbx cp ~/.config/coyote <sbx-name>:/home/agent/.config 2026-08-31 13:48:56 -06:00
Dark-Alex-17 9a1d00f5e6 docs: fixed a broken link in the readme 2026-08-29 13:16:34 -06:00
Dark-Alex-17 a14c768f9a docs: corrected a typo in the README 2026-08-29 12:59:33 -06:00
github-actions[bot] c102da5665 chore: bump Cargo.toml and sandbox image to 0.10.0
CI / All (ubuntu-latest) (push) Failing after 29s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
2026-08-29 05:49:07 +00:00
github-actions[bot] 4402d254f6 bump: version 0.9.0 → 0.10.0 [skip ci] 2026-08-29 05:49:04 +00:00
Dark-Alex-17 02974c8abc docs: reset changelog 2026-08-28 23:48:33 -06:00
Dark-Alex-17 9f7eeb7dc6 fix: Harden install scripts: detect libssl3 without ldconfig on PATH, survive noexec tmp dirs, and guard against partial curl|bash execution 2026-08-28 23:46:14 -06:00
Dark-Alex-17 f3d44212d5 fix: ldconfig on Debian-based distros lives in /usr/sbin 2026-08-28 23:04:35 -06:00
Dark-Alex-17 f0661b4ea4 feat: Support Linux ARM64 GNU 2026-08-28 22:49:58 -06:00
Dark-Alex-17 0946e3ec64 feat: Note that duckdb RAG driver is unavailable on MUSL Linux builds when initializing a RAG so users know when using the binary, not just based on docs 2026-08-28 22:35:39 -06:00
Dark-Alex-17 65e6249adb feat: updated coyote installation scripts to detect when gnu is installable 2026-08-28 22:23:31 -06:00
Dark-Alex-17 18c2cfeb41 fix: Prefer GNU linux builds in install scripts for duckdb support, and gate duckdb support on linux hosts that are MUSL until MUSL support is added 2026-08-28 20:06:39 -06:00
Dark-Alex-17 50d2a74e80 docs: updated README duckdb install script 2026-08-28 20:06:09 -06:00
github-actions[bot] ea001ade7a bump: version 0.8.3 → 0.9.0 [skip ci] 2026-08-29 00:36:51 +00:00
Dark-Alex-17 baa45970da feat: upgraded to v2 mixin schema
CI / All (ubuntu-latest) (push) Failing after 29s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
2026-08-28 18:24:58 -06:00
Dark-Alex-17 5039b74926 test: fix Windows crlf issue and pin the template to lf to prevent future regressions 2026-08-28 18:03:12 -06:00
Dark-Alex-17 176a81412a feat: improved first-time run experience and included a templated configuration file that now has comments like the config.example.yaml so users don't have to go to the repo to see all the knobs
CI / All (ubuntu-latest) (push) Failing after 29s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
2026-08-28 15:15:10 -06:00
Alex Clarke cebc32f70a Merge pull request #22 from Dark-Alex-17/feat/PLAN-rigor-surfaces
PLAN-rigor-surfaces: Rigor & Surfaces — Quality Calibration for the Architect/Sisyphus Suite
2026-08-28 14:54:33 -06:00
Dark-Alex-17 d3914955e2 feat(TASK-005): carry the quality bar through sisyphus and architect
sisyphus: Quality bar line in the code-reviewer spawn prompt (omitted when
the task prompt carried none); rigor-aware WARNING handling with
(deferred by quality bar) FOLLOW-UPS tagging; evidence-cited rejection
protocol for [convention] findings (repo convention at file:line or a
recorded plan decision; never for CRITICAL/[correctness]; escalate on
re-raise); rigor-to-posture default map for security-reviewer spawns.

architect: Phase B quality-bar round + other:<label> librarian lane;
Phase E Sisyphus CONTEXT and adversary prompts carry rigor/surfaces and
the plan's Quality bar excerpt; task-close logs rejected-finding lines;
Phase F PR body gains the Quality bar line and Review decisions section;
two new anti-patterns (bare rejections, rigor suppressing CRITICAL).
2026-08-28 14:47:57 -06:00
Dark-Alex-17 a8d730cf01 feat(TASK-004): reviewer routing — code-reviewer quality-bar resolution, domain linter pass, surface-skill routing, rigor folding; file-reviewer surface-skill whitelist + convention/correctness marker 2026-08-28 14:47:57 -06:00
Dark-Alex-17 cf638305e6 feat(TASK-003): add surface skills batch B — worker-review, iac-review, migration-review, cicd-review 2026-08-28 14:47:57 -06:00
Dark-Alex-17 3601faa969 feat(TASK-002): add surface skills batch A — rest-api-review, cli-review, library-review
Three new read-only review skills under assets/skills/, mirroring the
transactional-integrity canon: frontmatter load triggers, read-only
enabled_tools, production-bar severity checklists with
[convention]/[correctness] markers, marker-semantics and
orchestrator-linter paragraphs, and aspect-boundary lists.
rest-api-review includes gRPC and GraphQL sections.
2026-08-28 14:47:57 -06:00
Dark-Alex-17 e4c5f42a25 feat(TASK-001): add rigor/surfaces declaration layer to planning skills
design-session: Quality bar round in Step 2 (rigor/surfaces proposal,
per-surface confirm/drop, other:<label> lane, autonomous fallback),
rigor+surfaces frontmatter and Quality bar section in the Step 3 PLAN
template, poc-dodge anti-pattern.
task-tracking: optional per-task surfaces key (inherits the plan's list);
rigor stays run-level only.
plan-gatekeeping: manifest category 11 (Quality bar) with
FRICTION/BLOCKING severity guidance.
2026-08-28 14:47:57 -06:00
Alex Clarke 08dddb09fb Merge pull request #21 from Dark-Alex-17/feat/mcp-tool-whitelist
CI / All (ubuntu-latest) (push) Failing after 29s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
feat(mcp): per-server MCP tool allowlists with glob support
2026-08-28 12:56:42 -06:00
Dark-Alex-17 f323c919f5 feat(repl): color .info mcp-server verdicts and complete only running servers 2026-08-28 12:32:45 -06:00
Dark-Alex-17 544a6bbbbc feat(cli): rename mcp_config asset category to mcp-config with snake_case alias 2026-08-28 12:22:21 -06:00
Dark-Alex-17 772c19f2bd docs: Added example MCP server allowlisting to the example configuration files 2026-08-28 12:06:58 -06:00
Dark-Alex-17 1e15bc31d5 style: applied uniform style across files 2026-08-28 11:35:45 -06:00
Dark-Alex-17 c170d08654 fix(mcp): omit declared-but-empty prompt/resource capabilities from .info mcp-server 2026-08-28 11:33:03 -06:00
Dark-Alex-17 c707aaf3ee fix(mcp): annotate declared-but-empty prompt/resource capabilities in .info mcp-server 2026-08-28 10:48:43 -06:00
Dark-Alex-17 2d9829106d Merge remote-tracking branch 'origin/main' into feat/mcp-tool-whitelist 2026-08-28 09:55:05 -06:00
Dark-Alex-17 3d24719a71 docs: changed Sharing-Configurations to Bundles
CI / All (ubuntu-latest) (push) Failing after 29s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
2026-08-27 16:13:05 -06:00
Dark-Alex-17 290f6b257f docs: refined the description of coyote better 2026-08-27 15:55:13 -06:00
Dark-Alex-17 e7811a6b87 feat(mcp): add .info mcp-server, .set mcp_tools, and filtered-server listing to the REPL 2026-08-27 15:45:39 -06:00
Dark-Alex-17 94ede264e5 docs: updated links to be fully qualified in main README
CI / All (ubuntu-latest) (push) Failing after 30s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
2026-08-27 15:44:09 -06:00
Dark-Alex-17 92c8ff8934 Merge branch 'main' of github.com:Dark-Alex-17/coyote
CI / All (ubuntu-latest) (push) Failing after 30s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
2026-08-27 15:36:42 -06:00
Dark-Alex-17 af697d129b docs: updated the coyote description to be more accurate to its true use and purpose 2026-08-27 15:34:38 -06:00
Dark-Alex-17 895ecc812f docs: updated the Coyote tagline 2026-08-27 15:30:30 -06:00
Dark-Alex-17 c0224e20cb test(mcp): drive session re-attach and RAG attach filter guards through their real entry points
- use_session_applies_persisted_mcp_tools_immediately now loads a session
  file from disk through the real use_session, proving the post-assignment
  filter refresh applies a re-attached session's persisted allowlist.
- New use_rag_does_not_drop_role_filters loads a yaml-driver RAG through
  the real use_rag and proves the tool-scope rebuild recomputes the role's
  filter layer instead of dropping it.
- Seed the process-wide client/model registries in a pre-main ctor (new
  ctor dev-dependency) so model resolution is deterministic across test
  orderings; the seed exposes only an embedding model so tests that assert
  'no chat model available' keep their premise.
2026-08-27 15:12:22 -06:00
Dark-Alex-17 92d9464b38 feat(mcp): enforce per-server tool allowlists across runtime, jobs, agents, and graph nodes 2026-08-27 14:43:18 -06:00
Dark-Alex-17 a92ebb5b94 feat(mcp): add mcp_tools allowlist config surfaces across roles, sessions, agents, graphs, and skills 2026-08-27 14:13:35 -06:00
Dark-Alex-17 93e90106a8 feat: create a new probe agent to probe code verifications using usage-pattern testing 2026-08-27 13:59:28 -06:00
Dark-Alex-17 35e4c82f27 feat: Add in the web_search_coyote tool where helpful 2026-08-27 13:59:09 -06:00
Dark-Alex-17 5711432ac4 feat(mcp): add per-server tool allowlist policy module and allowedTools config field 2026-08-27 13:37:46 -06:00
Dark-Alex-17 ee45e42013 fix: also support the .continue edge case for session crashing checkpointing 2026-08-27 12:55:26 -06:00
Dark-Alex-17 3d640b9efa fix: checkpoint sessions that crash for easy resuming 2026-08-27 12:49:07 -06:00
Alex Clarke 730f942bab Merge pull request #20 from Dark-Alex-17/fix/job-output-tailing
fix(jobs): stream the tool's output file into the job peek ring
2026-08-27 12:31:13 -06:00
Dark-Alex-17 2efeda0ba7 fix: job__check ring buffer also collects file output for LLM_OUTPUT as well as stdout 2026-08-27 12:17:34 -06:00
Dark-Alex-17 df62c32822 feat: support mcp.json in the root of bundle repos as well as in the legacy functions directory 2026-08-26 15:48:16 -06:00
Dark-Alex-17 3762bbe09f feat: prefer ~/.config/coyote/mcp.json for the user-scope MCP config
The user-scope MCP config historically lived at
<config-dir>/functions/mcp.json, a leftover from when MCP support was
part of the llm-functions tooling. It now resolves through a single
choke point with these semantics:

- Preferred location: <config-dir>/mcp.json (created there on first run)
- Historical <config-dir>/functions/mcp.json still honored when the
  preferred file does not exist, so existing installs are unchanged
- If both exist, the preferred location wins

--info/.info now reports the resolved location as mcp_config_file, and
the --scope help text plus config.agent.example.yaml reference the new
default. This also removes the asymmetry with the workspace scope,
which already used .coyote/mcp.json directly.
2026-08-26 15:34:20 -06:00
Dark-Alex-17 ebe7816600 feat: claude and openai native web search via web_search_coyote
CI / All (ubuntu-latest) (push) Failing after 30s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
2026-08-26 15:22:24 -06:00
Alex Clarke 3ebe11a4f0 Merge pull request #19 from Dark-Alex-17/feat/background-jobs
feat: background jobs (job__* tools) with push notifications
2026-08-26 15:13:56 -06:00
Dark-Alex-17 429ae3cc8e refactor(function): finish supervisor-to-agent vocabulary migration
The supervisor registry went kind-generic (TaskHandle::Agent | Job)
earlier in this branch, but the module holding the agent__* handlers
and two model-facing error strings still carried the old name:

- src/function/supervisor.rs -> src/function/agents.rs (it contains
  only agent__* tool handlers, pairing with function/jobs.rs; the
  kind-generic src/supervisor/ registry keeps its name)
- 'Supervisor tool failed' -> 'Agent tool failed'
- 'Unknown supervisor action' -> 'Unknown agent action'
2026-08-26 14:57:36 -06:00
Dark-Alex-17 304b8f635f fix(tools): interactive-shell semantics and stderr capture in execute_command
Two long-standing agent-facing defects:

1. bash -e aborted the model's script at the first intermediate
   non-zero status (grep with no matches exits 1, inspecting a failing
   test run, a probing subshell), so trailing guards like '; exit 0'
   never executed and output was partially or entirely lost. Dropped
   -e: the last statement now decides the exit code, matching the
   interactive-shell semantics models expect. pipefail is kept so a
   failing pipeline stage still surfaces in the exit code.

2. Only stdout was redirected into $LLM_OUTPUT, and the harness
   returns just $LLM_OUTPUT on success, so commands whose useful
   output goes to stderr (git push, cargo progress, curl -v) returned
   empty on success. Added 2>&1.
2026-08-26 14:15:34 -06:00
Dark-Alex-17 198c9f42df fix(function): gate test-only declaration appender behind cfg(test)
append_declaration is exercised only by unit tests; in the plain bin
target it tripped dead_code under CI's RUSTFLAGS --deny warnings.
2026-08-26 13:50:44 -06:00
Dark-Alex-17 404a45a311 feat(jobs): node-local job ownership and capability-gated job__* visibility
Graph LLM nodes now own the jobs they start, on every exit path. A new
node_job_scope on RequestContext records job ids started while a node
runs: the turn-end guardrail nags only about the node's own jobs
(parallel branches no longer see each other's), and the node executor
reaps — cancels and deregisters — anything left registered when the
node exits, including error, timeout, and retry-exhaustion paths.
Cross-node job handoff is no longer possible; a crashed node takes its
in-flight jobs with it.

With inheritance gone, job__* declarations are gated on capability:
the family is only declared when at least one declared tool would pass
job__start's whitelist (shared predicate: is_backgroundable_tool). One
carve-out — while a context still owns registered jobs (job started,
tool disabled mid-session), the lifecycle verbs stay declared so a
running job can never become unreachable; job__start alone disappears.
A graph node with tools: [] now sees no job__* tools at all.

Prompt instructions, tool declarations, and graph.example.yaml updated
to the node-local semantics; +7 tests, 8 visibility pins rewritten.
2026-08-26 13:43:05 -06:00
Dark-Alex-17 bfb8105682 fix(graph): gate unix-only test imports behind cfg(unix)
The executor integration-test module hoisted job-test paths into
module-level imports, but their only consumer is a #[cfg(unix)] test —
on Windows the imports went unused and failed -D warnings.
2026-08-26 13:00:16 -06:00
Dark-Alex-17 1650196cae docs(config): document max_concurrent_jobs in agent example config 2026-08-26 12:53:17 -06:00
Dark-Alex-17 fa04e09373 feat(graph): support max_concurrent_jobs at the graph level
Graph agents could only inherit the app-wide job budget; the agent-level
header in graph.yaml now accepts max_concurrent_jobs alongside
model/temperature, flowing through AgentConfig::from_graph into the
run-wide supervisor. Deliberately graph-wide, not per-node: jobs outlive
the node that started them.
2026-08-26 12:53:17 -06:00
Dark-Alex-17 074083af31 feat(jobs): allow uncapped collect via full_result
job__collect's 50k-char tail cap is a safety default, but collect is
consume-once and the cap was mandatory — a model that genuinely needed
the complete output had no recourse. Add a full_result boolean that
skips the cap (tail_lines still honored; the session-wide
max_tool_result_chars limit still applies downstream), teach the
truncation banner to name the recourse, and point job__check's
output_bytes_captured at the collect decision.
2026-08-26 12:53:17 -06:00
Dark-Alex-17 c376737bbd refactor(function): rename agent-tool symbols out of supervisor vocabulary
Since the supervisor registry became kind-generic (agents AND jobs),
'supervisor' naming on the agent__* tool plumbing was misleading:
job__* handlers operate on the same supervisor. Rename
SUPERVISOR_FUNCTION_PREFIX -> AGENT_FUNCTION_PREFIX,
supervisor_function_declarations -> agent_function_declarations,
handle_supervisor_tool -> handle_agent_tool. No behavior change.
2026-08-26 12:53:17 -06:00
Dark-Alex-17 4e50b4ff4a refactor: Modified the naming of several generalized supervisor values 2026-08-26 12:43:51 -06:00
Dark-Alex-17 5a9f8c42b9 test: assert memory routing without depending on host memory files
memory_config() only reports enabled when a global memory index or a
workspace memory store exists on disk, so asserting the handler's
'name is required' detail was environment-dependent even with the
memory pref forced on. The routing test now accepts either
memory-handler-owned message: the 'Memory tool failed' prefix alone
proves the memory__ prefix reached the memory handler.
2026-08-25 20:52:31 -06:00
Dark-Alex-17 2eb63cfc0d test: pin memory config on in eval-routing test for environment-independent CI
The eval_routes_memory_prefix_to_memory_handler characterization test
inherited the host machine's memory configuration: on runners without a
memory setup, should_register_memory_tools() gates the handler off and the
error message differs. Force memory = Some(true) at ctx construction so
the test asserts the same handler path everywhere.
2026-08-25 20:47:34 -06:00
Dark-Alex-17 28018f33c9 test(jobs): add feature, hardening, and surface test matrix for background jobs
Covers the plan's T7 matrix: zero-diff invariants when jobs are off
(byte-identical tool lists and prompts, None-vs-Some select_functions),
validation hardening (shell/path-shaped/PATH-resolvable names, undeclared
MCP servers, non-whitelisted and context-filtered tools, mapping-tool
aliases, mid-batch tool-scope freshness), process lifecycle (grandchild
process-group kill, pgid clear after normal completion, panic skips the
completion notification), guardrail behavior (finished-job discard on
force-terminate, bounded inject-then-terminate iteration burn), surface
conformance (concrete_tool_names exclusion, toggle rejection, tools_info
listing, infra preservation under empty filters), supervisor swaps
(use_agent/exit_agent kill running jobs, child contexts cannot reach
parent job ids), and graph-node job lifecycle with deferred notification
drain.
2026-08-25 20:33:10 -06:00
Dark-Alex-17 6256b5fcfa docs: document background jobs across prompts, config example, and README
- Extend the injected Background Jobs prompt guidance: system_notifications
  push on completion, collect-only-when-idle wait protocol, and the graph
  LLM-node collect-before-final-turn rule
- Mention the system_notifications push in the agent spawning guidance and
  in the sisyphus/architect wait-protocol text (agent completions push
  notifications too)
- config.example.yaml: max_concurrent_jobs (default 5, 0 = disabled)
- README: features-list entry pointing at the Background-Jobs wiki page
2026-08-25 18:24:44 -06:00
Dark-Alex-17 24ed674952 feat(jobs): exempt polling tools from loop tracker and hint on unchanged checks 2026-08-25 18:17:15 -06:00
Dark-Alex-17 caabf41b65 feat(supervisor): push agent completion notifications to the spawning context
The spawned-agent task now pushes an agent_completed/agent_failed event
into the spawning context's notification queue before returning, so a
parent that keeps working learns mid-turn that a child finished instead
of discovering it only at the turn-end guardrail. Cancelled or
already-collected agents are suppressed by the existing drain-time
registration filter. This delivery applies regardless of whether
background jobs are enabled.
2026-08-25 17:52:19 -06:00
Dark-Alex-17 2d874f1d7c feat(jobs): push background-job completion notifications via per-context queue
- add NotificationQueue/SystemNotification: every context owns a fresh
  queue (children never inherit the parent's, avoiding first-drainer-wins
  races between transcripts)
- job tasks push job_completed/job_failed events on completion, failure,
  and timeout; a panic skips the push and is surfaced by the guardrail's
  finished-handle enumeration and collect's JoinError mapping instead
- events for jobs already collected or cancelled are dropped at drain time
  by filtering against live supervisor registration
- replace inject_escalation_notification with single-pass
  merge_system_channel: pending_escalations (root-only) ordered before
  system_notifications (any depth) on the last tool result of a batch;
  byte-identical output when notifications are empty, proven by the
  unmodified pre-merger characterization tests
2026-08-25 17:50:28 -06:00
Dark-Alex-17 6a694d10db test(jobs): assert kind-aware guardrail surfaces running jobs
Reconciles the T2 guardrail delta with the T3 jobs-only-supervisor test
at merge time, per plans/background-jobs-design.md §11 merge order.
2026-08-25 17:38:57 -06:00
Dark-Alex-17 177d61cf94 fix: harden job runner lifecycle and whitelist conformance
Reject fast built-in file tools (fs_* / ast_grep) in job__start per the
backgroundable-tools whitelist; clean up env-snapshot temp files on every
exit of run_process_job via a drop guard; bound the output-pump awaits and
abort them on the failure path; treat signal death (no exit code) as a
failure with a teaching message; bound job__collect's post-drain join with
a SIGKILL escalation so a TERM-ignoring process cannot hang collect after
a Ctrl-C teardown; document the unguarded SIGTERM pid-reuse window; give
the injected Background Jobs prompt section a fresh line on both sides;
extract the MCP server name with strip_prefix instead of replace.

Capacity-0 audit for jobs-disabled contexts: REPL displays have no
supervisor consumers (only Ctrl-C/exit cancel_recursive at
repl/mod.rs:460,473, kind-agnostic); session save/load does not persist
supervisor state (src/config/session.rs has no supervisor references) --
nothing to test for either.
2026-08-25 17:36:17 -06:00
Dark-Alex-17 4025b8dacd feat: inject background-jobs prompt guidance when jobs are enabled
Agents whose function pool includes job__* declarations get a Background
Jobs section teaching start/check/collect/cancel discipline and the
snapshot/no-persistence semantics. Presence of the declarations doubles
as the jobs_enabled predicate, so a context with function calling off or
max_concurrent_jobs 0 sees no job prompt text.
2026-08-25 17:36:17 -06:00
Dark-Alex-17 cb025b7fff feat: add background job runner, job__* handlers, and start gates
Detached tokio::process runner with a frozen JobEnvSnapshot (env-derived
bin dirs, vault-interpolated agent envs, COYOTE_TOOL_TIMEOUT resolved at
start), process_group(0) with pgid-guarded SIGTERM/SIGKILL escalation,
capture-only ring-buffer telemetry, and LLM_OUTPUT read after wait().
MCP jobs snapshot a single-entry McpRuntime holding only the validated
server and render through the same free fn as the foreground path.

job__start enforces its gates synchronously before any spawn:
jobs_enabled, the backgroundable whitelist with directionality teaching
errors, the per-request declared-names stash captured in
before_chat_completion, then capacity (lazy supervisor get-or-init in
plain sessions). job__check/list read the shared JobState cell without
consuming; job__collect blocks with the escalation early-out and applies
a tail-biased char-boundary cap plus optional tail_lines; job__cancel
kills the group with a 5s grace.

Job declarations are injected iff jobs are enabled at agent init, the
plain-session function-init sites, and the exit_agent rebuild; job__ is
carved out of enabled_tools filtering and excluded from
concrete_tool_names so REPL toggles cannot grant or revoke it.
2026-08-25 17:36:17 -06:00
Dark-Alex-17 fcc3756634 fix(function): floor tool-output truncation cut to a UTF-8 char boundary
When max_chars landed inside a multi-byte UTF-8 character of the
serialized output, s.get(..max_chars) returned None and the code fell
back to the FULL untruncated string while still prepending the
truncation marker — the "truncated" output actually grew. The cut is
now floored to the previous char boundary so the prefix is always a
valid, genuinely truncated slice.
2026-08-25 17:36:11 -06:00
Dark-Alex-17 257b06bbd4 fix(supervisor): make agent__check a pure status probe that never consumes the handle
agent__check on a finished agent delegated to agent__collect, which
returned the full (unbounded) result and consumed the handle. That
contradicted the tool's own docs and broke the check-then-collect
pattern: a second collect on the same id failed.

check now reports { status: finished } with a pointer to
agent__collect and leaves the handle registered; collect is the single
retrieval verb. The tool description and prompt table are updated to
stop promising that check returns the result.
2026-08-25 17:36:11 -06:00
Dark-Alex-17 7cf88c030f fix(supervisor): surface finished-but-uncollected tasks in turn-end guardrail
The turn-end guardrail only counted still-running agents, so an agent
that finished before the turn ended was invisible: its uncollected
result was silently dropped. Jobs were never counted at all.

The guardrail now enumerates every registered task (running and
finished, agents and jobs) via Supervisor::list_tasks and renders a
kind-aware prompt with two sections: still-running tasks to reclaim,
and completed-but-uncollected tasks with the exact collect command.

At the force-terminate cap, finished-but-uncollected handles are
explicitly discarded with a warning naming the lost ids, so the
guardrail cannot loop forever on handles nobody will collect.
2026-08-25 17:36:11 -06:00
Dark-Alex-17 a9df9a4dd5 docs(plan): reconcile whitelist row 1 with the grep-class carve-out row
T3 implementation followed the specific fs_*/ast_grep 'NO in v1' row;
row 1's 'ALL external command tools' over-claimed. No design change.
2026-08-25 17:35:47 -06:00
Dark-Alex-17 7f3f95d89d feat: generalize supervisor registry to TaskHandle enum with job scaffolding, kill discipline, and max_concurrent_jobs config
Implements T1 of plans/background-jobs-design.md (§6, R7/R8/R9):

- Supervisor.handles is now HashMap<String, TaskHandle> where
  TaskHandle = Agent(AgentHandle) | Job(JobHandle); agent-facing
  accessors (active_count, effective_active_count, is_finished, take,
  inbox, abort_signal_for, list_agents) match only Agent variants,
  preserving all existing external behavior byte-for-byte.
- New JobHandle/JobState/JobStatus/JobResult types with pgid-guarded
  process-group kill discipline: Drop and cancel_all/cancel_recursive
  kill the group only while state.pgid is still set (pid-reuse guard),
  via libc::killpg on unix and JoinHandle::abort elsewhere.
- Per-kind job capacity: Supervisor carries max_concurrent_jobs
  (builder-set, default 0); job registration rejects at capacity.
- Cross-kind teaching errors at the four agent-lookup miss sites
  (agent__check/collect/cancel/send_message) when the id is a
  registered job or job_-prefixed; genuinely-unknown ids keep their
  existing messages.
- Supervisor init condition is now can_spawn_agents || jobs_enabled in
  use_agent and both child-agent spawn paths, with agent capacity 0 in
  jobs-only contexts; use_agent cancels the old supervisor recursively
  before replacing it.
- max_concurrent_jobs config plumbing: global Config field, AgentConfig
  override + accessor, all four AppConfig touch points including the
  COYOTE_MAX_CONCURRENT_JOBS env override; shared
  effective_max_concurrent_jobs/jobs_enabled predicates
  (agent override -> global -> default 5; 0 disables).
- Stage dependency-free RingBuf (64 KiB default) in src/function/jobs.rs
  for the upcoming job output pump.
- New sanctioned dependency: libc 0.2 under cfg(unix).
2026-08-25 15:17:57 -06:00
Dark-Alex-17 bfc3b7bfea test: pin current tool-eval, guardrail, and truncation behavior ahead of background-jobs work
T0 characterization safety net per plans/background-jobs-design.md §9.3: 43 tests
pinning handle_collect/check/cancel/spawn, the pending-agents guardrail (incl.
ForceTerminate + counter resets), eval_tool_calls partition/re-sort/soft-fail/
loop-alert/truncation, truncate_if_needed's UTF-8 boundary edge, ToolCall::eval
prefix routing, merge_tool_results shape, and cancel_recursive recursion.

Known-buggy behaviors deliberately pinned for visible later diffs: handle_check
consumes finished handles, guardrail ignores finished-but-uncollected agents,
mid-char truncation returns the full string with marker prepended.

Not covered (findings): empty-after-dedup bail is unreachable from non-empty
input; run_child_agent needs a mock LLM client (none exists) — manual case;
over-threshold summarization pinned via deterministic unknown-model failure.
2026-08-25 13:55:15 -06:00
Dark-Alex-17 240eaa081a docs: add background jobs + push notifications design doc
Gatekeeper-SEALED + Oracle-APPROVED v1.8 (2026-08-24 gates; 2026-08-25
accuracy refresh against the MCP resources/prompts merge).
2026-08-25 13:27:49 -06:00
Dark-Alex-17andSisyphus c7384b7a9b feat: run an advisory observability pass after implementation in sisyphus
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-25 13:19:10 -06:00
Dark-Alex-17andSisyphus ed9778c07b feat: add an observability-review skill for post-implementation monitoring analysis
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-25 13:19:09 -06:00
Dark-Alex-17andSisyphus 5a32219178 feat: check under- and over-logging in the code review gate
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-25 13:19:09 -06:00
Dark-Alex-17andSisyphus 9e652f7801 feat: calibrate logging registers in the code-writing agents
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-25 13:19:09 -06:00
Dark-Alex-17andSisyphus fcff426ae5 feat: add a logging-discipline skill for calibrating log output to repo conventions
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-25 13:17:33 -06:00
Dark-Alex-17 cb0802c2d0 fix: fix grep "binary" errors when searching UTF-8 files with unicode characters like some of the Coyote source 2026-08-25 13:06:16 -06:00
Dark-Alex-17 d7524b8de7 Merge branch 'main' of github.com:Dark-Alex-17/coyote 2026-08-25 12:13:44 -06:00
Alex Clarke aa14e66c35 Merge pull request #18 from Dark-Alex-17/feat/mcp-resources-prompts
CI / All (ubuntu-latest) (push) Failing after 29s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
feat: MCP server resources and prompts support
2026-08-25 12:13:31 -06:00
Dark-Alex-17 3c9f443bce feat(cli)!: rename --prompt to --temp-role
Completes the .prompt/.temp-role split: --prompt set an ad-hoc system
role, which is what .temp-role now means everywhere. The --prompt name
is left unbound so a future one-shot MCP prompt flag can take it with
properly designed non-interactive semantics. use_prompt follows the
rename as use_temp_role.

BREAKING CHANGE: invocations using --prompt <text> must switch to
--temp-role <text>; clap rejects the old flag loudly.
2026-08-25 12:04:45 -06:00
Dark-Alex-17 e55120dac6 fix(bundles): harden the install pipeline for cross-platform correctness
Windows review findings on the bundle provenance code:

- clones now pin core.autocrlf=false and core.eol=lf so recorded sha256
  values reflect repository bytes, not the machine's git config (autocrlf
  on Windows previously made every text file a false conflict on update),
  plus core.longpaths=true for deep bundle trees
- is_safe_relative_path additionally rejects NTFS alternate data stream
  colons, reserved device names (con, nul, COM1..), and trailing dots or
  spaces; such names never come from a valid checkout and previously
  desynced or failed on Windows
- file ownership dedupe compares paths case-insensitively on Windows and
  macOS where case variants denote one physical file (uninstalling one
  bundle could previously delete another bundle's file)
- a failed git clone no longer leaks its partial tree in the temp dir,
  and temp cleanup failures are logged instead of swallowed
- recording a bundle file outside the config dir (asset dir override)
  now warns instead of silently producing an undeletable record
2026-08-25 11:37:41 -06:00
Dark-Alex-17 b67f1ef854 refactor: pulled out some imports to clean up the MCP render module a bit 2026-08-25 11:37:41 -06:00
Dark-Alex-17 b38562a961 fix(mcp): harden the spill path for cross-platform correctness
Windows review findings: reserved device names (con, nul, COM1..) and
trailing dots in server names break or desync directory creation, so
sanitize_server now escapes reserved stems, strips trailing dots, and
caps length at 64 chars. Spill writes go through a temp file + rename
so a visible file is always complete (closes a cross-process partial
read race), and eviction protection compares content-hashed file names
instead of full paths. Also drops a duplicated cfg attribute.
2026-08-25 11:37:41 -06:00
Dark-Alex-17 40846de37a test(bundles): skip uninstall ambiguity test when stdout is a TTY
The non-interactive bail under test only triggers without a TTY; from a
terminal the code correctly opens the interactive selector instead, so
the test hung or failed depending on input. Same guard as the three
sibling non-interactive tests.
2026-08-25 11:37:41 -06:00
Dark-Alex-17 a5a3eed6d8 fix(repl): offer prompts in .list tab completion and rename its listing helpers
MCP prompts are live, server-owned catalog entries, not managed assets;
list_prompt_assets/prompt_asset_rows implied otherwise and are now
list_mcp_prompts/mcp_prompt_rows. The .list completer was also missing
the prompts kind that the usage string and unknown-kind error advertise.
2026-08-25 11:37:41 -06:00
Dark-Alex-17 ad9ff3bea8 style: revised a few stylistic choices after I changed my mind 2026-08-25 11:37:41 -06:00
Dark-Alex-17 5177d95ee0 feat: complete --filter and --force on the first .install argument
The unified install parser accepts flags in any position, so the
first-argument completion list now offers all four flags instead of
only --git-host and --help.
2026-08-25 11:37:41 -06:00
Dark-Alex-17 9bc37e226b docs: removed design doc from commit 2026-08-25 11:37:41 -06:00
Dark-Alex-17 a4b55d9e42 fix(mcp): gate unix-only spill permission APIs for windows builds 2026-08-25 11:37:41 -06:00
Dark-Alex-17 c8b00b20bc docs: document MCP resources and prompts support
Update the README's MCP feature entry to cover the full capability trio
(tools, resources, prompts): the capability-gated mcp_read/mcp_prompt
meta-tools, bounded results and blob spilling, and the .prompt REPL
command with staged tab-completion and .list prompts.

Per plans/mcp-resources-prompts-design.md section 10.T9.
2026-08-25 11:37:41 -06:00
Dark-Alex-17 eb37f8bb46 feat(mcp): bound tool-result passthrough and surface resource audience annotations
Route CallToolResult content through the render.rs content policy per
plans/mcp-resources-prompts-design.md §6 (T8): oversized text sliced at
TEXT_MAX_BYTES_CLAMP with a self-explaining truncation note, image/audio/
embedded blob content spilled (or inlined when UTF-8-clean) instead of
shipping base64 into model context, and structuredContent subject to the
same ceiling. Clamp server-controlled uri/mime metadata strings to the new
METADATA_MAX_BYTES bound in both the read and tool-result paths, sanitize
the terminal rendering of MCP dispatch errors while keeping raw text in
the tool_call_error payload, and surface resource audience annotations in
both mcp_search results and mcp_read metadata via the catalog.
2026-08-25 11:37:41 -06:00
Dark-Alex-17 6fade71e8c feat(mcp): add mcp_prompt meta-tool and harden prompt display rendering
Emit an mcp_prompt_<server> declaration for servers advertising the
prompts capability, execute prompts via McpRuntime::prompt on both tool
dispatch chains, and return the flattened prompt text as the tool
result. Sanitize server-controlled prompt names, descriptions, and
argument names before terminal rendering, and attribute the .prompt
argument inquire label to its server and prompt.

Per plans/mcp-resources-prompts-design.md §5.2 (T7).
2026-08-25 11:37:41 -06:00
Dark-Alex-17 61a3cfb662 feat(repl): add .prompt command with live staged tab-completion
Implements plans/mcp-resources-prompts-design.md §5.1/§5.4 (T6):

- .prompt <server> <name> [key=value ...] fetches an MCP prompt and
  submits the result as chat input via Input::from_str + ask(), never
  through REPL line parsing; GetPromptResult messages are flattened
  into one user-role block with unconditional [user]/[assistant] labels
- missing required prompt arguments are collected interactively
- .list prompts renders server/name/description/args via the unified
  catalog (CatalogItem gains an arguments field), degrading per server
- staged live tab-completion: enabled+running+prompts-capable servers
  (no RPC), then live prompt names, then key= argument suggestions with
  (required) markers; 2s timeout per RPC, all errors degrade to silent
  empty suggestions, ctx read guard dropped before blocking
- the enabled-server alias expansion is factored into a shared helper
  used by both tool-scope rebuild and completion
- BREAKING: the former .prompt <text> temp-role builtin is renamed to
  .temp-role <text> (behavior preserved); .prompt now belongs to MCP
  prompts, and a user macro named prompt or temp-role is shadowed
2026-08-25 11:37:41 -06:00
Dark-Alex-17 67819784b7 feat(mcp): add mcp_read meta-tool for resource reads
Implements plans/mcp-resources-prompts-design.md section 4.3 (T4):
mcp_read_<server> declaration and handler wired to render.rs, RFC 6570
Level-1-only URI template expansion, defensive ResourceContents parsing,
per-item text paging with pattern filtering, blob spill metadata, an
overall 204800-byte multi-content ceiling, dispatch wiring on both
eval chains, and a render_text paging-stall guard.
2026-08-25 11:37:41 -06:00
Dark-Alex-17 437512fd6d feat(mcp): gate meta-function emission on advertised server capabilities
Per-server McpServerFeatures (tools fail-open, resources/prompts
fail-closed) now drive which meta-functions are declared, with
gated_meta_function_prefixes as the single gating seam; read/prompt
declarations land together with their handlers. The server-enablement
sentinel keys on the always-emitted search name so resources-only
servers survive role filtering.

Implements plans/mcp-resources-prompts-design.md §4.4/D7 (T5).
2026-08-25 11:37:41 -06:00
Dark-Alex-17 ef88b6a2c8 feat(mcp): add render.rs content policy (text paging, pattern filter, blob spill)
Single content-policy module for MCP resource and tool content, per
plans/mcp-resources-prompts-design.md §4.5 (T3):

- render_text: UTF-8-boundary-safe paging with clamped max_bytes and
  grep-style fancy-regex line filtering (2 lines of context, 1-based
  line-number prefixes, merged hunks); offsets walk the filtered stream.
- render_blob/render_blob_at: streaming base64 decode with a 50 MiB
  ceiling, UTF-8 sniff, sha256-named 0600 spill files under a sanitized
  server dir with a fixed mime->ext allowlist, and best-effort
  oldest-first eviction bounding the spill tree at 512 MiB.

Not yet wired to call sites; module carries #![allow(dead_code)] until
the read/prompt surfaces land.
2026-08-25 11:37:41 -06:00
Dark-Alex-17 d68f4ecaeb feat(mcp): extend the server catalog to resources, templates, and prompts
Implements the unified catalog from plans/mcp-resources-prompts-design.md §4.2 (T2): CatalogItem gains kind/uri/mime_type/size keyed as {kind}:{id}; catalog_items() lists per kind gated by advertised capabilities with warn-and-degrade; mcp_search results carry kind; mcp_describe gains an optional kind param (default tool); write-only registry ServerCatalog removed.
2026-08-25 11:37:41 -06:00
Dark-Alex-17 01ada1da18 refactor(mcp): centralize meta-function prefix predicates and fix list_tools pagination
Implements T1 of plans/mcp-resources-prompts-design.md (§4.1, §4.6):

- Replace list_tools(None) with cursor-following list_all_tools() at the
  three call sites (start_server catalog build, catalog_items, describe)
  so paginating servers no longer silently lose tools past page one.
- Add MCP_READ/MCP_PROMPT prefix constants (declared nowhere yet; wired
  in T4/T7) plus centralized helpers MCP_META_FUNCTION_PREFIXES,
  is_mcp_meta_function, and mcp_meta_function_names.
- Mechanically replace every hand-rolled 3-prefix starts_with triple
  (partition in eval_tool_calls, 3 exclusion triples in
  select_enabled_functions, 3 inclusion triples + per-server name
  construction in select_enabled_mcp_servers) with the helpers,
  preserving the existing lax starts_with matching semantics and the
  mcp_invoke_* enablement sentinel (sentinel moves to search in T5).
- Behavior-neutral: dispatch chains keep their 3 arms, emission stays
  at exactly 3 meta-functions per server, existing tests unmodified.
- Add unit tests: helper classification, prefix-soundness property,
  lax-matching pin, ordered candidate-name construction.
2026-08-25 11:37:41 -06:00
Dark-Alex-17 7caa24d090 docs(plans): add MCP resources & prompts design (v1.3, gate-approved)
Gatekeeper: SEALED. Oracle: APPROVE-WITH-CHANGES (B1-B3 folded in).
Phases: unified catalog + mcp_read w/ render.rs content policy;
.prompt REPL + staged live tab-completion + mcp_prompt meta-tool;
CallToolResult bounding; capability gating via McpRuntime::server_features.
2026-08-25 11:37:41 -06:00
Dark-Alex-17 0e941fb360 feat: complete --filter and --force on the first .install argument
The unified install parser accepts flags in any position, so the
first-argument completion list now offers all four flags instead of
only --git-host and --help.
2026-08-25 10:02:50 -06:00
Dark-Alex-17andSisyphus b972c12559 docs: extend the mattpocock/skills credit to the grilling adaptation
CI / All (ubuntu-latest) (push) Failing after 33s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-24 15:12:37 -06:00
Dark-Alex-17andSisyphus f8cab9b439 feat: run design interviews as grilling frontier rounds across the planning agents
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-24 15:12:37 -06:00
Dark-Alex-17andSisyphus 45333db5c2 feat: add a grilling skill for frontier-round design interviews
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-24 15:12:37 -06:00
Dark-Alex-17andSisyphus 7748b953f0 docs: extend the mattpocock/skills credit to the codebase-design adaptations
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-24 15:07:50 -06:00
Dark-Alex-17andSisyphus 720591d24a feat: add an on-demand architecture-reviewer agent for deepening scans
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-24 15:07:50 -06:00
Dark-Alex-17andSisyphus dbb0c51b7e feat: add a codebase-design skill with the deep-module design vocabulary
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-24 15:07:50 -06:00
Dark-Alex-17andSisyphus 71cd50fe4d docs: credit mattpocock/skills for the diagnosing-bugs and smell-baseline adaptations
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-24 15:02:12 -06:00
Dark-Alex-17andSisyphus b5863dded0 feat: add a feedback-loop-first diagnosing-bugs skill to the coding suite
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-24 15:02:12 -06:00
Dark-Alex-17andSisyphus 03116d2f42 feat: add a Fowler code-smell baseline to the code-review skill
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-24 15:02:12 -06:00
Dark-Alex-17andSisyphus 9b23247815 feat: flag duplicate helpers in code reviews with a repo-wide DRY check
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-24 14:46:38 -06:00
Dark-Alex-17andSisyphus 3493b01e9a feat: add a transactional-integrity review skill to the code review gate
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-24 14:46:38 -06:00
Dark-Alex-17andSisyphus fd989c44d0 feat: add an operational-history prior-art lane to the code-reviewer agent
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-24 14:46:13 -06:00
Dark-Alex-17 e7307da6a9 feat: support name=value macro arguments with variable tab completion
Macro invocations (.name and .macro name) accept leading name=value
assignments before positional args: assignments set declared variables
directly so earlier variables can keep their defaults, remaining
positionals fill unassigned variables in declaration order, and the
free text after -- is never scanned for assignments. Identifier-shaped
keys that match no declared variable error with the declared list to
catch typos; non-identifier tokens containing = stay positional.
MacroVariable gains an optional description field, and tab completion
after a macro name offers name= candidates showing each variable's
description and default until the assignment prefix ends.
2026-08-24 14:20:21 -06:00
Dark-Alex-17 b6721d6a15 feat: add --help guides to the .install and .uninstall REPL commands
.install --help and .uninstall --help print a usage guide covering the
owner/repo shorthand, --git-host, --filter, --force, ref pinning, and
the bundle lifecycle; both usage error lines now point at --help. Tab
completion offers --help for both commands and --git-host on the first
.install argument, and the unified install parser accepts flags in any
argument position so completed flags work wherever they are inserted.
The empty .list bundles message now shows the REPL install form
alongside the CLI one.
2026-08-24 13:58:09 -06:00
Alex Clarke 9e72a52b1c Merge pull request #17 from Dark-Alex-17/feat/bundle-provenance
feat: bundle manifest, provenance, and lifecycle for shared configurations
2026-08-24 11:23:25 -06:00
Dark-Alex-17 30c1637dff refactor: Refactored some bundle const locations 2026-08-24 11:15:13 -06:00
Dark-Alex-17 6b5535956d fix: harden the bundle lifecycle per code review
The path-escape guard that uninstall applies to recorded paths now also
covers update's obsolete-file deletion through a shared check, so a
tampered store cannot turn either delete site into an arbitrary file
removal. Updates gain a working non-interactive path: --yes now applies
to --update-bundle (locally modified files, obsolete files, and modified
mcp entries are all kept; everything else refreshes), owned mcp entries
whose recorded hash still matches the local entry take the remote side
without prompting, and the non-TTY conflict bails name the flag that
actually works per surface. An update records its new commit and version
only after files and mcp entries land, so an aborted update cannot claim
content it never wrote. The store gains a version field and rejects
stores from newer builds, the corrupt-store error no longer advises the
removal that would forfeit ownership tracking, and duplicate records
tracking one source abort a rename instead of overwriting a record.
Reinstalling from a source URL reclassifies owned unmodified files as
silent refreshes just like updates. git runs with GIT_TERMINAL_PROMPT=0
and a null stdin so private or mistyped URLs fail instead of hanging.
File comparison fills buffers fully before comparing, deleting an
obsolete file prunes emptied directories, mcp.json backfill uses the
fsynced atomic writer, --list-bundles no longer triggers builtin
backfill, bundle-name completion logs store errors instead of swallowing
them and offers --yes, and REPL .uninstall rejects unknown flags.
2026-08-24 11:15:13 -06:00
Dark-Alex-17 fdfe4ba023 refactor!: drop the .install remote migration hint
'remote' is no longer special-cased anywhere; the token falls through
to the unified .install dispatch like any other value.
2026-08-24 11:15:13 -06:00
Dark-Alex-17 8f02bf1c33 refactor!: drop the --install-from tombstone entirely
The flag no longer exists in any form; --install <GIT_URL|OWNER/REPO>
is the only spelling.
2026-08-24 11:15:13 -06:00
Dark-Alex-17 80b082423c fix: address code review findings on the bundle lifecycle
The user-origin marker on replaced mcp.json entries is now sticky:
re-records and cross-bundle transfers only upgrade replaced to
transferred when the prior record proves bundle origin, so updating a
bundle can no longer make uninstall delete a key the user had before the
bundle replaced it. Canonical source URLs lowercase only the host, since
self-hosted forges treat repository paths as case-sensitive and
collapsing distinct repos misdirects updates and uninstalls. git clone
invocations pass '--' before the URL so a crafted source cannot be
parsed as a git flag. Lifecycle flags (--install, --install-builtins,
--update-bundle, --uninstall) and their companions now conflict
explicitly instead of first-match dispatch silently dropping actions.
--install-from returns as a hidden tombstone that errors with the
replacement instead of feeding the flag to the LLM as prompt text.
--list-bundles dispatches before config load so a pure read no longer
boots MCP servers. write_file_atomic fsyncs before the rename so a crash
cannot persist a truncated store. REPL: .uninstall accepts --yes,
.install rejects trailing tokens after a category, and .install remote
gets a migration hint. Plus polish: host validation rejects '#' and '?',
renamed_to no longer serializes null, derived names get a debug assert
against the validator, completions share DEFAULT_GIT_HOST, README
mentions skills.
2026-08-24 11:15:13 -06:00
Dark-Alex-17 4324d551d6 fix: reserve category names, confirm fork-name collisions, report secrets on uninstall
Bundle names that collide with an asset category (agents, roles, skills,
macros, functions, mcp_config) are now owner-qualified at install time,
whether derived from the repo or declared by a manifest, so no bundle can
shadow a category by name. A manifest name that collides with a bundle
from a different source now prompts for confirmation interactively (a
fork or typo-squat is the likely cause); declining aborts before anything
is written, and non-interactive runs keep the deterministic
owner-qualification. Uninstall summaries now list the vault secrets the
bundle's MCP servers reference, noting they are installed by the bundle
but not removed. Also removes the dead ResolvedBundleName.migrated_from
field.
2026-08-24 11:15:13 -06:00
Dark-Alex-17 1136b1385b docs: removed extra fluff on bundles from the main README 2026-08-24 11:15:13 -06:00
Dark-Alex-17 84b90bfe26 style: remove em-dashes from comments and the uninstall selector 2026-08-24 11:15:13 -06:00
Dark-Alex-17 53ccbda97c feat: expand owner/repo shorthand for --install with a --git-host flag
--install someuser/repo expands to https://github.com/someuser/repo;
--git-host overrides the default host and forces source interpretation
even when the value matches an installed bundle name. Two or more path
segments are accepted so nested GitLab-style groups work, and #ref
pinning applies to shorthand values. --uninstall resolves owner/repo
against recorded sources: a single match uninstalls, multiple matches
prompt an interactive selection showing each bundle's source, and
non-interactive runs bail instead of guessing.
2026-08-24 11:15:13 -06:00
Dark-Alex-17 2d1bf372d8 style: strip narration comments from bundle provenance code
Function docs that restated behavior already evident from names,
signatures, and code are removed; only comments carrying invariants
the code cannot express remain.
2026-08-24 11:15:13 -06:00
Dark-Alex-17 4987d850f9 feat!: remove the deprecated --install-from flag and .install remote form
--install <git-url|name> is the single entry point for remote installs
and updates; the unified .install dispatch likewise replaces
.install remote. Flag completion for .install now applies to the
unified form.
2026-08-24 11:15:13 -06:00
Dark-Alex-17 9541a094d8 fix: make bundle provenance portable to Windows
Provenance records stored OS-native path separators, making
installed-bundles.yaml non-portable; slug derivation treated a Windows
drive letter as an scp host and swallowed the whole path into one
sanitized segment. Store paths are now always forward-slashed and
backslashes normalize before URL parsing. Test fixture repos commit a
'* -text' .gitattributes so clone-side autocrlf cannot rewrite content
assertions.
2026-08-24 11:15:13 -06:00
Dark-Alex-17 89df8ec1ca docs: document bundle lifecycle and manifest for sharing configurations 2026-08-24 11:15:13 -06:00
Dark-Alex-17 bca85a4017 feat: rename install flags and unify .install dispatch
--install now takes a git URL or an installed bundle name: categories
are redirected to the new --install-builtins, installed names become
implicit updates, and source-shaped values install remotely. The old
--install-from keeps its exact behavior as a hidden deprecated alias.
The REPL's .install gains the same unified dispatch while keeping
.install <category> and .install remote <url> back-compat.
2026-08-24 11:15:12 -06:00
Dark-Alex-17 0e5d85f2ff feat: add --uninstall and .uninstall for installed bundles 2026-08-24 11:15:12 -06:00
Dark-Alex-17 0a806da8d2 feat: add --update-bundle with provenance-aware conflict handling
Updates re-clone a bundle's recorded source (honoring a recorded commit
pin unless a #<ref> override moves it), silently refresh files the bundle
owns that the user never modified, and fall back to the normal conflict
prompts for modified or unowned files. Files the remote no longer ships
are offered for deletion (kept by default non-interactively, staying
owned). The record is refreshed with the new commit, version, and
metadata, and stamped with an updated_at timestamp on success.
2026-08-24 11:15:12 -06:00
Dark-Alex-17 2790a823b0 feat: add --list-bundles and .list bundles with drift detection 2026-08-24 11:15:12 -06:00
Dark-Alex-17 88acf2362f feat: record bundle provenance when installing from remote repos 2026-08-24 11:15:12 -06:00
Dark-Alex-17 bfcc762ec9 feat: add bundle provenance store 2026-08-24 11:15:12 -06:00
Dark-Alex-17 b21699b749 feat: parse bundle manifests and capture resolved SHAs for remote installs 2026-08-24 11:15:12 -06:00
Dark-Alex-17 5f23e2403f fix: user__ask should have been renamed to user__select in graph agent user interaction invocations 2026-08-21 14:49:27 -06:00
Dark-Alex-17 2af6fe64d4 Merge branch 'feat/comment-discipline' 2026-08-21 14:02:55 -06:00
Dark-Alex-17 da640f3dcd feat: created a new comment-discipline skill for the built in sisyphus suite 2026-08-21 14:02:39 -06:00
Dark-Alex-17 79ec2d87c7 fix: support tab completions for graph-based agents with variables as well as standard agents 2026-08-21 12:47:38 -06:00
Dark-Alex-17 faf9dd581f feat: created a dedicated git_command tool to tighten tool calling permissions in the git-master skill 2026-08-21 12:33:28 -06:00
Dark-Alex-17 873deef7c7 feat: Added a new security review step to the code writing quality gates 2026-08-21 12:30:53 -06:00
Alex Clarke f518c8a6fc Merge pull request #16 from Dark-Alex-17/feat/macros-as-commands
feat: macros as first-class custom commands
2026-08-21 12:29:24 -06:00
Dark-Alex-17 aea5f3d615 test: updated embedded macro tests to expect descriptions for all built-in macros 2026-08-21 12:22:48 -06:00
Dark-Alex-17 bda37d9f38 docs: Added a description to the built-in generate-commit-message macro 2026-08-21 12:15:31 -06:00
Dark-Alex-17 96e5390621 test: use collision-proof temp dirs in macro_policy tests
The with_macro_dirs fixture derived its temp-dir name from a wall-clock
nanosecond timestamp, so parallel tests starting in the same clock tick
shared a directory and saw each other's macro files (flaky on CI
runners with coarse tick granularity). A process id + atomic counter
makes the name unique by construction.
2026-08-21 12:03:11 -06:00
Dark-Alex-17 b43acac8ee fix: surface macro parse errors on top-level invocation
An invalid installed macro invoked as a top-level command fell through
to the generic unknown-command error, while .macro <name> reported the
parse/validation failure. Both paths now surface the reason.
2026-08-21 11:55:14 -06:00
Dark-Alex-17 03687c8981 feat: addressed review comments 2026-08-21 11:55:14 -06:00
Dark-Alex-17 95dd31e24b docs: cleaned up docs 2026-08-21 11:55:14 -06:00
Dark-Alex-17 e1b5562888 refactor: render .list agents and .list skills as comfy-tables
Long agent/skill descriptions wrapped badly in the bullet-list format.
Extract a shared asset_table helper (UTF8_FULL + dynamic arrangement,
same style as the markdown renderer and .list macros) and use it for
the agents, skills, and macros listings. The skills loaded marker
keeps its color; comfy-table's custom_styling feature accounts for
ANSI sequences in column widths.
2026-08-21 11:55:14 -06:00
Dark-Alex-17 fba040c668 fix: render .list macros as a comfy-table instead of fixed-width columns
Hand-rolled {:<24} padding broke alignment as soon as a macro name
exceeded the column width. Reuse the comfy-table UTF8_FULL preset with
dynamic content arrangement, matching the markdown renderer's tables.
2026-08-21 11:55:13 -06:00
Dark-Alex-17 f61a8f7afd style: updated styles across macro implementation 2026-08-21 11:55:13 -06:00
Dark-Alex-17 91328ca7e1 docs: document macros as first-class custom commands
Covers top-level .name invocation, the new description/isolated macro
fields (with the non-isolation persistence, fail-fast, nested-macro, and
.exit caveats), workspace .coyote/macros/ + --no-workspace-macros,
enabled_macros scoping at global/role/agent/session levels, and
.macro enable|disable across the README and every example config.
graph.example.yaml gains a note that enabled_macros is ignored in graph
configs.

CHANGELOG intentionally untouched: it is generated by commitizen at
release time from the conventional commit subjects.

Implements plans/custom-commands-design.md §8.
2026-08-21 11:55:13 -06:00
Dark-Alex-17 125360033d docs(plans): record implementation-verified correction from T6
no_workspace_mcp (and therefore no_workspace_macros, per the exact-mirror
ruling) is CLI-flag-only: AppConfig field exists but there is no
Config-struct key and no env arm, so a config.yaml entry is non-functional.
Pre-existing issue: config.example.yaml:140 documents the dead
no_workspace_mcp key — recorded as follow-up, out of scope.
2026-08-21 11:55:13 -06:00
Dark-Alex-17 4323d4823c feat: add --no-workspace-macros opt-out for workspace macro loading
Mirrors --no-workspace-mcp exactly: a CLI-only flag backed by an
AppConfig field (default false) that disables .coyote/macros in both
the resolved macro policy and Macro::load's workspace-then-global
preference, so the two always agree (custom-commands design §5).
2026-08-21 11:55:13 -06:00
Dark-Alex-17 5478c5a239 feat: execute non-isolated macros on the live REPL context
Make the macro isolated field live (design §3, §9 step 5):
isolated: false now runs the interpolated steps via run_repl_command
on the live RequestContext — session-recorded, conversation-visible,
with mutating steps persisting by design — while isolated: true keeps
the forked execution byte-for-byte unchanged.

- Add macro_non_isolated companion field beside macro_flag; both
  fork-propagation sites mirror it verbatim.
- RAII MacroModeGuard wraps the whole &mut RequestContext (DerefMut
  passthrough) and restores flag+mode on every exit path, including a
  failing step; steps remain fail-fast.
- Reject nested macro invocation when the current mode is non-isolated
  ("nested macros not allowed in non-isolated mode"); an isolated
  macro's step may still run a non-isolated macro inline on its fork.
- use_agent now suppresses the agent's default session only for
  isolated macros; a non-isolated .agent step engages it as if typed.
2026-08-21 11:55:13 -06:00
Dark-Alex-17 e94bd450cd feat: surface macros as first-class custom commands in the REPL
Implements the invocation and management surfaces from
plans/custom-commands-design.md §4 and §6:

- Top-level dispatch: an enabled macro <name> now runs as ".<name> [args]"
  from the command catch-all; runtime-disabled macros point at
  ".macro enable <name>", locked macros name the owning config, and
  unknown commands keep the existing error verbatim
- .macro enable|disable <name>: runtime toggles over the in-memory
  global-level enabled_macros list (disable with no list materializes
  all-active-minus-name); toggles error when a role/agent/session
  allowlist owns the field
- .set enabled_macros <csv|null> with workspace-then-global existence
  validation; .set key completion gains enabled_macros and the
  previously missing enabled_skills
- Dynamic completion: enabled macros (with descriptions) join built-ins
  on ".<TAB>" without touching the static command registry;
  ".macro <TAB>" lists invocable macros (incl. built-in-shadowed ones)
  plus the enable/disable subcommands; second-arg completion offers
  toggle-eligible names
- .list macros: enriched table (name, source, isolated, state,
  description) covering every resolver state incl. missing and
  shadowed rows; .help gains a custom-commands section
- Session info/render and sysinfo display enabled_macros; Macro::load
  resolves workspace-then-global; enable/disable rejected as macro
  names in the creator
2026-08-21 11:55:13 -06:00
Dark-Alex-17 e8ddb61518 feat: add lazy macro resolver with two-dir discovery and per-macro states
Adds src/config/macro_policy.rs: MacroPolicy::effective computes the
visible macro set on demand from the discovered definition files, the
four-level enabled_macros allowlists, and the built-in command names.

- Discovery scans workspace (.coyote/macros/) then global macros dirs on
  every resolution; workspace shadows global by name, and the shadowed
  global entry is retained and flagged so both stay listable (plan
  custom-commands-design.md §5). Workspace scanning is gated on a bool
  parameter so the future --no-workspace-macros flag wires in one line.
- Allowlist precedence is session > agent > role > global, first Some
  wins, no merging; None falls through, an empty list is an explicit
  zero, all-None enables everything (mirrors SkillPolicy).
- Per-macro states per plan §6: enabled, disabled (runtime, global-level
  exclusions only), locked (role/agent/session exclusions, recording the
  owning level), missing (unknown allowlist names warn instead of
  bailing — deliberate divergence from skills), shadowed (built-in name
  collisions), and invalid (parse failures and the reserved names
  enable/disable). Invalid beats allowlist exclusion beats shadowing.
- Adds enabled_macros() accessors on Role, Session, and Agent alongside
  their enabled_skills() counterparts, plus paths::workspace_macros_dir.
- 37 tests: state matrix, pairwise precedence, explicit-zero pinned at
  every level, workspace shadowing, reserved names, builtin collisions,
  missing rows, invalid YAML, and env-gated discovery (#[serial]).
2026-08-21 11:55:13 -06:00
Dark-Alex-17 f39381aa9d feat: add enabled_macros config field at global, role, agent, and session levels
Mirrors the enabled_skills plumbing per plans/custom-commands-design.md §5:
- global: Config + AppConfig structs, from_config copy, and the
  COYOTE_ENABLED_MACROS env-override arm (csv_to_vec parsing)
- role: frontmatter via parse_string_or_array (list or csv string),
  plus the export() mirror so Role::save round-trips the field
- agent (non-graph): plain serde on AgentConfig; graph.yaml silently
  ignores the key (pinned by test, no field on Graph by design)
- session: plain serde with csv-or-vec deserializer

Empty list/string deserializes to Some([]) (explicit zero), distinct
from absent/null (None) — pinned by tests at every level, including
the env arm (serial-fenced against the from_config tests, which read
the process env via load_envs).
2026-08-21 11:55:13 -06:00
Dark-Alex-17 e8b55bba15 feat: add description and isolated fields to Macro struct
description (optional, default None) will surface in listings and
completion; isolated (default true) preserves today's forked-context
execution behavior exactly. Both fields use plain serde defaults so
every existing macro YAML deserializes unchanged, and unknown fields
in newer files remain tolerated by older binaries.

Adds Serialize to Macro/MacroVariable (None description skipped) and
back-compat, round-trip, and embedded-asset deserialization tests.

Per plans/custom-commands-design.md §3 / §9 step 1.
2026-08-21 11:55:13 -06:00
Dark-Alex-17 9863c7a5f3 docs(plans): add macros-as-custom-commands design (gate-approved)
Design doc for macros as first-class custom commands: top-level .name
invocation, description/isolated fields, enabled_macros scoping,
workspace-local macros, completion integration.

Gatekeeper: sealed (3 findings fixed). Oracle plan-review: approved.
2026-08-21 11:55:13 -06:00
Dark-Alex-17 f44722df04 lint: Removed accidental session file
CI / All (ubuntu-latest) (push) Failing after 32s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
2026-08-19 12:59:50 -06:00
Dark-Alex-17 3eaae0e652 feat: Dynamically detect RAG embedding model dimension for any given model 2026-08-18 19:57:11 -06:00
Dark-Alex-17 b12829db39 fix: drain crossterm characters in zellij in kitty contexts to prevent DA1 responses from entering prompt 2026-08-18 15:30:07 -06:00
Dark-Alex-17 dffaf6b9db fix: drain tty input when displaying inquire prompts to prevent unintentional escapes 2026-08-18 15:10:30 -06:00
Dark-Alex-17 a9a4ccca88 fix: breaking iwe MCP server changes with newest version 2026-08-18 11:29:36 -06:00
Dark-Alex-17 7cb7d66575 fix: improved handling of non service-specific secrets using sbx custom-secrets
CI / All (ubuntu-latest) (push) Failing after 30s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
2026-08-17 18:01:21 -06:00
Dark-Alex-17 400b50fbd0 feat: Support auto confirmation for gatekeeper agents 2026-08-17 15:48:36 -06:00
Dark-Alex-17 7eeff2a226 fix: include tool output to LLM_OUTPUT in errors as well as stderr 2026-08-17 11:38:21 -06:00
Dark-Alex-17 374e8c0bf7 fix: prevent tmp-overwriting
CI / All (ubuntu-latest) (push) Failing after 29s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
2026-08-15 19:35:12 -06:00
Dark-Alex-17 ede87df960 fix: Corrected a rare edge case on how tool files are generated during parallel executions 2026-08-15 18:41:13 -06:00
Dark-Alex-17 95a8c3df44 fix: cosmetic fix after improved bash tool handling 2026-08-15 17:31:32 -06:00
Dark-Alex-17 f57bd21ee4 fix: Improved subagent escalation handling 2026-08-14 16:53:37 -06:00
Dark-Alex-17 e1b6e3f8c6 fix: latent parsing bugs in fs_patch and argc 2026-08-14 16:12:41 -06:00
Dark-Alex-17 644d899f78 fix: Prevent infinite hangs in coder agent and implement timeouts for LLM API calls and interactive tools 2026-08-14 15:22:25 -06:00
Dark-Alex-17 2596194417 feat: retry LLM API calls once after 401 by force-refreshing the OAuth token
The Client trait's default chat_completions, chat_completions_streaming,
and embeddings methods now classify failures via ApiStatusError: on a
401 with a cached OAuth token, the token is distrusted (identity-aware
marker) and the call retried exactly once — the retry's prepare step
sees the marker and force-refreshes. Streaming retries only while the
SSE handler has received no content, preventing duplicate rendering.
A second 401 propagates the original error; other retry errors
propagate as-is. API-key clients never retry. No backoff by design:
cost is bounded to one refresh + one retry per failing request.
2026-08-14 13:17:56 -06:00
Dark-Alex-17 684f19250a feat: identity-aware rejected-token marker for LLM OAuth cache
distrust_access_token compare-and-invalidates the in-memory entry only
when the cached token equals the rejected one, so a concurrent refresh
is never clobbered. is_valid_access_token and both expiry checks in
prepare_oauth_access_token treat marked tokens as expired, forcing a
refresh of provider-rejected tokens that are still locally unexpired.
The marker is cleared after every completed refresh, including ones
that return the same token.
2026-08-14 13:07:51 -06:00
Dark-Alex-17 f3d59ade11 feat: typed ApiStatusError carrying HTTP status through LLM client errors
catch_error and sse_stream now bail with ApiStatusError{status, message}
instead of bare anyhow strings, preserving every existing Display output
byte-for-byte. Enables structural status classification (e.g. 401
detection) via downcast through anyhow context chains.
2026-08-14 13:01:08 -06:00
Dark-Alex-17 e1604c58ea feat: per-request OAuth token injection with mid-session refresh for HTTP MCP servers
Replace the spawn-time static Authorization header for OAuth-managed HTTP
MCP servers with McpOAuthClient, a custom implementation of rmcp's
StreamableHttpClient trait that resolves the bearer token on every
request via load_or_refresh_mcp_token. Tokens that expire mid-session
now refresh transparently instead of failing tool calls until restart.

On a 401 for an injected token, the wrapper force-refreshes (identity-
aware: a still-unexpired copy of the rejected token is not trusted) and
retries exactly once, matching Claude Code / official SDK semantics.
Both *_with_max_sse_event_size trait methods are overridden to preserve
the inner client's SSE size enforcement, and the inner reqwest client
mirrors rmcp's default (pool_max_idle_per_host(0), no redirects).

SSE, stdio, and static-header HTTP paths are unchanged; startup
warning semantics (McpAuthRequired reasons) are preserved. Verified
live: mid-session backdated token refreshed transparently during an
active atlassian session.
2026-08-14 12:33:46 -06:00
Dark-Alex-17 dcacb3a962 chore: upgrade rmcp 1.8.0 -> 3.1.2
Compile-clean upgrade verified: zero source changes needed, full test
suite green, clippy clean. Coyote's rmcp API surface (14 items) dodges
all 2.0/3.0 breaking changes; the "Auth required" error string matched
by is_auth_required_error is intact in 3.1.2.
2026-08-14 11:10:45 -06:00
Dark-Alex-17 d791098e51 feat: reason-specific warnings for MCP servers that fail OAuth at startup
Distinguish why an OAuth MCP server was not started: never authenticated
(no stored credentials), stored token expired and refresh failed, or the
server rejected a token that looked valid. McpTokenStatus replaces the
Option<String> return of load_or_refresh_mcp_token, and McpAuthRequired
carries the reason across the error boundary via anyhow context.
2026-08-14 11:07:56 -06:00
Dark-Alex-17 d31110cd67 fix: allow nested italics inside bold spans in markdown renderer
CI / All (ubuntu-latest) (push) Failing after 29s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-13 16:18:50 -06:00
Dark-Alex-17 0f35e03a85 fix: properly handle OAuth refreshes
CI / All (ubuntu-latest) (push) Failing after 29s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-13 16:00:42 -06:00
Dark-Alex-17 c2b0c120d7 chore: Added grok4.6 to models.yaml
CI / All (ubuntu-latest) (push) Failing after 29s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-13 14:20:35 -06:00
Dark-Alex-17 65c9be36b2 fix: correct newline removal from fs_write and fs_patch 2026-08-13 14:18:45 -06:00
Dark-Alex-17 2658ca776e feat: Installed duckdb into the coyote image
CI / All (ubuntu-latest) (push) Failing after 29s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-12 19:29:26 -06:00
Dark-Alex-17 f8682102a0 docs: Added duckdb prerequisite
CI / All (ubuntu-latest) (push) Failing after 29s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-12 19:07:31 -06:00
Dark-Alex-17 3fa0f5c428 feat: Support managing MCP servers from the CLI directly
CI / All (ubuntu-latest) (push) Failing after 29s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-12 17:55:33 -06:00
Dark-Alex-17 68135b97d1 feat: append new built-in rag__query function to RAG contexts to allow further querying by LLMs
CI / All (ubuntu-latest) (push) Failing after 29s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-12 16:25:50 -06:00
Dark-Alex-17 b87a3460c4 fix: detect duplicate tool call IDs client-side before sending to Claude
CI / All (ubuntu-latest) (push) Failing after 29s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-12 13:33:24 -06:00
Dark-Alex-17 cb23da6490 feat: support static file bundling with sbx-mixins
CI / All (ubuntu-latest) (push) Failing after 29s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-12 13:18:38 -06:00
Alex Clarke 2a40a5a81d Merge pull request #14 from Dark-Alex-17/feat/rag-driver-abstraction-v3
CI / All (ubuntu-latest) (push) Failing after 31s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
feat(rag): pluggable RagProvider abstraction with DuckDB and Qdrant drivers
2026-08-12 12:45:12 -06:00
Dark-Alex-17 ebba976a27 fmt: applied formatting 2026-08-12 12:07:38 -06:00
Dark-Alex-17 c84f9522e9 feat(rag): offer the storage driver when an agent initializes its RAG
Agent startup and graph rag nodes both run an interactive wizard when their
knowledge base has not been built, but neither offered the driver choice that
interactive named-RAG creation has, so both silently produced a yaml store.

A plain agent was the worse of the two: AgentConfig carries only documents, so
there was no way to get a duckdb RAG for one, interactively or declaratively. A
graph node could at least declare driver: in the workflow.

Agent startup now passes prompt_for_driver, and a rag node whose wizard runs is
asked too. The prompt is skipped when the node already declares a driver, and
sits inside the not-fully-specified branch after the non-interactive bail, so
declarative workflows and headless runs are unchanged. Temp RAGs still pass
false: they are deleted on the next run, so a persistent store would only leave
a sidecar behind.

The prompt moves to select_rag_driver rather than being duplicated.
2026-08-12 12:02:15 -06:00
Dark-Alex-17 81ed769f8a fix(rag): warn when a duckdb store is empty but files are indexed
A duckdb RAG is two files. The .yaml deliberately carries no vectors, and
open() runs CREATE TABLE IF NOT EXISTS, so a .yaml copied or synced without its
.duckdb sidecar produces a fresh empty store, hydrates to nothing, and answers
every query with nothing while .info rag still lists every indexed file.

Neither existing guard catches it: the anti-wipe check in rebuild_indexes needs
existing > 0, and the mandatory ? on hydration needs a genuine error, while an
absent store is the same Ok(empty) as a RAG with nothing indexed yet.

Warn rather than bail, so a store deleted on purpose still loads and can be
rebuilt.
2026-08-12 11:26:16 -06:00
Dark-Alex-17 6f7defe25f style: removed redundant comment 2026-08-12 10:56:34 -06:00
Dark-Alex-17 b837f82d7e fix(rag): keep a local Qdrant off an ambient proxy
Reverts the global proxy rework in 54685be and narrows it to the provider.

That commit took over proxy detection for every client in order to exempt
loopback and private ranges. Too broad: reqwest's detection also reads macOS
System Settings and the Windows registry behind its system-proxy feature, which
sits in its default set. Coyote disables default features today, so hand-rolling
the environment lookup happened to match — but re-enabling defaults later would
silently restore that support for main and not for the hand-rolled version. It
also made an explicitly configured proxy skip local hosts, which nobody asked
for: a proxy named for a LAN endpoint should be used.

build_client and utils are byte-identical to main again. The bypass now lives in
QdrantProvider::make_client, which is the only place that knows the target host,
and applies solely when that host is loopback, link-local, private or .local. A
public or cloud-hosted store keeps whatever the environment configures.

Also drops apply_proxy: with build_client reverted there was one caller left, and
set_proxy already covers it.

Both #[ignore]d live tests still pass against a Qdrant on loopback while an
ambient proxy that rejects it is in force.
2026-08-12 10:52:31 -06:00
Dark-Alex-17 54685be9a2 fix: keep loopback and LAN traffic off an ambient proxy
Pre-existing on main, not introduced by the driver work, but it makes a local
RAG backend unusable so it belongs with this change.

build_client only called set_proxy when a client had configured one of its own.
With nothing configured, reqwest's own detection applied, which sends every
request through a *_PROXY variable including ones bound for 127.0.0.1 or a LAN
address. A proxy cannot usefully forward those, and anything that intercepts
proxied traffic answers on behalf of a service that is running perfectly well,
so the error names the proxy rather than the store and reads as a Coyote fault.

Concretely, an installed Socket Firewall exports HTTP_PROXY to the processes it
wraps and rejects hosts outside its allow list. That turned a healthy Ollama on
the LAN into 'error decoding response body: expected value at line 2 column 1' —
its HTML refusal page parsed as JSON — and a loopback Qdrant into an HTTP 405.

Proxy handling is now always applied and always exempts loopback and private
ranges, with NO_PROXY merged in since replacing reqwest's detection also
replaces its handling of that variable. HTTP_PROXY and HTTPS_PROXY are kept
separate because they are allowed to differ. An explicitly configured proxy
still wins, and '-' still means none.

This also supersedes the unconditional no_proxy() added to the Qdrant client in
af9622d: that made it the only client to ignore a proxy outright, on a
justification I got wrong. It now shares this path, so a remote store behind a
real proxy keeps working.
2026-08-12 10:33:03 -06:00
Dark-Alex-17 4dd6e794b2 docs: removed redundant comment 2026-08-11 22:19:14 -06:00
Dark-Alex-17 af9622d31c fix(rag): stop routing Qdrant requests through an ambient proxy
make_client used a bare reqwest builder, which honours whatever proxy the
environment advertises. That made it the only HTTP client in Coyote to do so:
utils::set_proxy discards ambient settings and applies only Coyote's configured
proxy, and every other client goes through it.

The symptom is that a perfectly healthy Qdrant is unreachable and the error
belongs to the interposing proxy, not the store, so it reads as a Coyote or
Qdrant fault. Locally an installed Socket Firewall answered `.rag attach`
against 127.0.0.1:6333 with an HTML 'Connection Required' page and HTTP 405.

Both #[ignore]d live tests now pass against a real Qdrant; they failed with that
same 405 before this change, which is the first time either has run green.

A remote store that genuinely needs Coyote's configured proxy is a follow-up:
that means threading the proxy config into the provider.
2026-08-11 22:15:04 -06:00
Dark-Alex-17 6f586bd535 style: cleanup 2026-08-11 22:07:35 -06:00
Dark-Alex-17 78740db170 chore: ignore the .coyote workspace directory
It holds generated workspace state and should never be committed.
2026-08-11 22:03:45 -06:00
Dark-Alex-17 64d594f4ee refactor(rag): discover driver_config secrets by grammar, not field name
Sandbox provisioning only ever looked at driver_config["api_key"], so a driver
whose credential is called anything else would have been silently unprovisioned
inside a sandbox. It now scans every driver_config value and treats any that is
a secret placeholder as a credential, which is the same rule resolve_driver_config
already used at point of use.

The first one binds to the RAG's own service id, which is what the generated
mixin declares; any others register under their own names, as MCP secrets do.
The mixin still carries a single credential entry, so a driver needing two bound
secrets remains a follow-up.

Also drops the placeholder parser added in 74bc613. crate::vault::SECRET_RE is
already the canonical definition and was already imported here, so that was a
third implementation of the same grammar. Requiring the whole value to match is
what keeps a literal key from being read as a secret name and printed.

The api_key check is gone from RagData::validate: a generic config validator
should not know a provider's field names.
2026-08-11 22:03:45 -06:00
Dark-Alex-17 1322d73c7b style: further cleanup 2026-08-11 21:50:46 -06:00
Dark-Alex-17 de91ffa517 fix(rag): treat a zero min_score as no floor on Qdrant searches
parse_search_hits filtered on score > min_score, and the only caller passes
0.0. Qdrant Euclid collections score by negative distance, so every hit was
dropped and an attached Euclid collection returned nothing at all, silently.

This is the same trap the surrounding code already documents: score_threshold
is deliberately not sent because it is metric-aware and a 0.0 floor filters
everything out on Euclid. The local filter then reproduced it exactly. Only a
positive floor is now treated as a floor.
2026-08-11 21:07:20 -06:00
Dark-Alex-17 74bc613d94 fix(rag): address Copilot review findings on the driver abstraction
Five review comments, all real:

- hybrid_search ran its vector and keyword legs sequentially after the
  provider refactor; main ran them under tokio::join!. Restores the
  concurrency while keeping the degrade-on-error keyword behaviour, so a
  remote provider no longer pays two serial round trips per query.

- inject_rag_secrets derived a vault secret name by trimming braces, which
  leaves a literal key untouched. A RAG holding a plaintext api_key therefore
  looked the secret up by its own value and printed it to stderr on failure.
  Parsing is now strict and a non-placeholder is skipped with a warning that
  names no credential.

- validate() now refuses a driver_config.api_key that is not a {{NAME}}
  placeholder, so a plaintext key cannot reach the RAG YAML at all.

- Rag::create's catch-all arm treated any unrecognised driver as yaml. A typo
  built a yaml store, paid to embed the corpus, persisted the bad driver and
  only failed on the next run. Unknown drivers now fail immediately.

- The qdrant arm's error was written for a developer; it now tells the user
  that only attached collections are readable and points at .rag attach.
2026-08-11 21:04:21 -06:00
Dark-Alex-17 6d0a5550fe style: Removed some redundant comments 2026-08-11 20:56:51 -06:00
Dark-Alex-17 7b1c0342b4 fix(rag): delete the DuckDB write-ahead log alongside the store
Deleting a RAG removed its .duckdb file but left the sibling .duckdb.wal
behind. DuckDB only removes that log on a clean close, so any RAG whose
process was killed left one on disk, and creating a new RAG under the same
name let it inherit a write-ahead log describing someone else's data.

The test helper already cleaned the log up after itself, which is why no
test noticed the production path did not.
2026-08-11 16:55:19 -06:00
Dark-Alex-17 d6c114fe58 feat: simplified the duckdb selection prompt 2026-08-11 16:50:50 -06:00
Dark-Alex-17 912e00a627 docs(rag): correct the duckdb concurrency note in the driver prompt
The driver picker still told users a duckdb RAG can only be open in one Coyote
process at a time. That stopped being true once the store began opening
read-only for queries: any number of processes can now query it concurrently.

The restriction that remains is narrower and only bites while writing, so the
prompt now states that instead — several processes can query at once, but an
ingest or rebuild locks the others out until it finishes.
2026-08-11 16:38:55 -06:00
Dark-Alex-17 e006e29ff1 feat(rag): let several Coyote processes query one duckdb RAG at once
The DuckDB store was always opened read-write, which takes an exclusive file
lock, so a second Coyote process could not even read the RAG. Querying does not
write, and DuckDB permits many concurrent readers as long as no writer is
attached, so the store is now opened read-only whenever it already carries a
complete schema.

Creating or initializing the store still writes, as does rebuilding, so those
paths take the exclusive handle. The rebuild path upgrades a read-only
connection in place, which every clone of the handle observes because the mode
lives behind the shared mutex rather than beside it. Extension loads and the
HNSW persistence setting are per-connection and are re-established on the
upgraded connection.

An upgrade that loses the race for the write lock reports that another process
holds the RAG and that nothing was written, then reopens read-only so the
session can keep querying. A read-only handle also refuses writes outright, so a
missed upgrade cannot silently discard an ingest.
2026-08-11 14:58:18 -06:00
Dark-Alex-17 c0067d387c refactor(rag): drop the hardcoded embedding model hint from attach
The attach wizard mapped a collection's vector dimension to a hardcoded list of
model names and printed them as likely candidates. The list was never checked
against the models the user actually has configured, so it could recommend a
model they cannot select, and one entry was a parenthetical note rather than a
model id and so could never match anything. Any list like this rots as models
are released.

The dimension itself comes from the server and is worth stating, so it is still
printed, as is the warning that a mismatched embedding model returns bad
results. Deriving real candidates would need a dimension recorded against each
configured model, which the model config does not carry today.
2026-08-11 14:06:57 -06:00
Dark-Alex-17 118c346345 feat(rag): support string and UUID Qdrant point IDs
Point ids were read with as_u64() inside a filter_map, so a string id was
silently dropped and a UUID-keyed collection returned zero hits with no error.
The attach wizard therefore refused such collections and told the user to
rebuild with integer ids, which defeats the purpose of attaching to a
collection someone else already built. LangChain, a common way to populate
Qdrant, uses UUIDs by default.

The integer id was never load-bearing for this driver. DocumentId is a packed
(file, chunk) pair used positionally by the local drivers, but an attached RAG
holds no local files or vectors and every positional consumer already returns
early on it, so the id only has to survive the round trip from search back to
the content fetch. Ids that cannot make that trip as a u64 are interned behind
a synthetic handle and restored when the fetch is issued, leaving collections
that already use integer ids on exactly the path they used before.
2026-08-11 14:05:38 -06:00
Dark-Alex-17 dc677a2529 fix(sandbox): discover agent-scoped RAG mixin sidecars
An agent-scoped RAG writes its config to <data>/agents/<agent>/<rag>.yaml, so
its sbx mixin sidecar lands beside it as <rag>.sbx-mixin.yaml. Discovery scanned
the agents directory only for a file named exactly sbx-mixin.yaml, and scanned
for suffixed sidecars only in the top-level rags directory, so a RAG attached
while an agent was active contributed no network allow rule and no credential to
the sandbox. The failure was silent: the sandbox launched and the RAG was simply
unreachable from inside it.

The two collectors differed only in the filename shape they matched, so they are
now one scan that takes the set of layouts to look for. The agents directory
asks for both its own sbx-mixin.yaml and the suffixed sidecars one level in,
which is the shape that was missing. Discovery order is unchanged, and it is
load-bearing: each mixin becomes a --kit in list order and later ones layer over
earlier ones, so the workspace mixin must stay last.
2026-08-11 14:05:31 -06:00
Dark-Alex-17 5e2b9c98ad fix(rag): stop the attach wizard from silently accepting an empty collection
`sample_point_id` returns `None` for a collection with no points, so the
UUID guard's `if let Some(..)` fell straight through and the wizard attached
happily. The result is a RAG that answers every query with zero hits and
never says why.

Sample once, then check for emptiness explicitly. This warns and asks rather
than hard-failing: an empty collection is not necessarily a mistake, since
another tool may be about to populate it, and none of the wizard's remaining
probes can distinguish that from a misconfiguration. The confirmation
defaults to "no" so it cannot be walked past by accident, and `attach`
already refuses to run non-interactively, so no unattended path reaches it.
2026-08-11 13:55:59 -06:00
Dark-Alex-17 c458ca93a9 feat(rag): create the API key secret inline in the attach wizard
The wizard hard-errored with "Secret 'X' not found in vault. Run
`coyote --add-secret X` first.", throwing away every answer the user had
already given it. Offer to create the secret in place instead, deferring to
`Vault::add_secret` for the masked prompt, the provider write and the
confirmation line, then read it back.

Only a genuine `SecretError::NotFound` triggers the offer. An auth failure,
a provider outage, or the vault being disabled inside a sandbox all
propagate with their own message, because prompting for a value that cannot
be stored would fail one step later and bury the real cause. Declining the
offer fails with both ways out spelled: add the secret up front, or answer
"no" to the API-key question.
2026-08-11 13:50:33 -06:00
Dark-Alex-17 860566bf50 feat: let workflow rag nodes select a RAG driver
`RagNode` gains an optional `driver`, forwarded into `RagInitConfig` so a
graph node can build its knowledge base on duckdb instead of yaml. Nodes
that name no driver forward `None`, which still resolves to yaml, so
existing workflows are unaffected.

An unknown driver is rejected up front rather than at construction time.
`Rag::create` dispatches unknown drivers to its yaml catch-all, so a typo
would otherwise embed every document and persist the bogus string, after
which every subsequent load fails validation and the agent cannot start.
The check asks `RagData::validate()` through a probe value instead of
restating the list of valid drivers, so the two cannot drift.
2026-08-11 13:46:59 -06:00
Dark-Alex-17 7f90710427 refactor(rag): interpolate every driver_config value, not just api_key
Only `driver_config["api_key"]` was interpolated, so any credential-bearing
driver field added later would have shipped its raw `{{PLACEHOLDER}}` to the
server. Resolve every value instead, via `resolve_driver_config`.

Resolution still happens into a function-local copy and never touches
`RagData`: `save()` serializes `self.data` and is called by `.set rag_top_k`
and friends, so a resolved credential parked there would be written to the
RAG's YAML in plaintext. The literal `{{NAME}}` also has to survive on disk
because sandbox credential provisioning parses it back out to learn which
vault secret to bind. Scope stays `driver_config` deliberately: the rest of
a RAG file is ingested document text, where `{{...}}` is ordinary content.

Covered by a test that saves after a load and asserts the placeholder, not
the secret, is what reaches the file.
2026-08-11 13:46:32 -06:00
Dark-Alex-17 93a934439b fix(rag): fail loudly when a RAG's vault secret is missing
`interpolate_secrets` does not error on a secret the vault cannot resolve:
it substitutes the empty string and returns the name in its second tuple
element. `load_async` discarded that vec, so a typo'd or deleted vault
secret produced `api_key = ""` and an unexplained 401 from Qdrant.

Bail instead, naming the RAG and the missing secrets, matching what global
config loading already does.
2026-08-11 13:44:00 -06:00
Dark-Alex-17 ecda258d3a style: Cleaned up some minor styling issues 2026-08-11 13:04:27 -06:00
Dark-Alex-17 3e598065f8 fix(rag): serialize DuckDB extension installs to stop a Windows race
`ensure_extension` fell back to `INSTALL` whenever `LOAD` failed. With a cold
extension cache every thread's `LOAD` fails at once, so every thread ran
`INSTALL` concurrently for the same extension. DuckDB installs by downloading
to a temp file and then MOVING it into `~/.duckdb/extensions/...`; POSIX allows
replacing a file other handles hold open, so Linux and macOS survived, but
Windows rejects that move with "Access is denied" and the losing threads failed.

Guard the install step with a process-global mutex and re-check `LOAD` after
acquiring it. The re-check is what bounds the work to a single install: without
it every thread queued behind the winner would still run a redundant `INSTALL`
and repeat the same move over a file that is now open.

`LOAD` is per-connection, so it still runs on every connection; only `INSTALL`
is serialized. An already-installed extension takes the pre-lock fast path and
costs neither a lock nor network. The lock is never held across the connection
mutex, so it cannot invert lock order.

Traced with strace on a cold cache under default test parallelism: before, 17
threads moved files into the store (13 racing on vss alone); after, exactly one
rename per extension.
2026-08-10 16:13:36 -06:00
Dark-Alex-17 3abc30d633 fix(rag): emit an sbx kit v2 mixin and declare RAG credentials to the proxy
The RAG attach sidecar was written against the sbx kit v1 spec and still emitted schemaVersion "1" with network.allowedDomains, network.serviceDomains, network.serviceAuth, credentials.sources.<n>.env and environment.proxyManaged. Every one of those keys was removed in kit v2. Coyote does not validate mixins, it copies them byte-for-byte into spec.yaml, so the invalid document surfaced only as an opaque sbx failure with no indication of which mixin caused it.

generate_rag_sbx_mixin now builds the document from the shared serializer structs instead of a format! string, which is how the envelope drifted unnoticed in the first place. render_mixin_yaml and the RAG sidecar both go through a new render_mixin_document, giving one definition of the envelope and one enforcement point for the rule that every inject domain must also appear in permissions.network.allow.

Fix an auth bug the port exposed: inject_rag_secrets bound the API key with sbx secret set, but nothing ever emitted a matching credentials entry, so the proxy held a value with no inject rule and never rewrote the auth header. An attached RAG credential silently did not work inside the sandbox. The sidecar now declares that credential; a RAG with no API key declares none while still receiving egress.

Fix the service id: the bind passed the raw file stem instead of routing it through secret_service_id, so a RAG named My_Docs produced an illegal id. The bind and the generated credentials service now share that derivation and cannot disagree.

Retire sbx_domain_forms in favour of allow_entry_for_url, now pub(crate). It emitted both a bare host and host:port because v1 serviceDomains needed a bare key; v2 has no such need, so the extra entry is simply wrong. It also defaulted a schemeless host to port 6333 while normalize_base_url resolves it to http and port 80, meaning the allow entry named a port the client never dialled.
2026-08-10 15:58:40 -06:00
Dark-Alex-17 f68937611e fix(rag): install DuckDB vss and fts extensions when they are missing
The DuckDB schema init loaded the vss and fts extensions but nothing ever
installed them, so any machine without them already present failed with
'IO Error: Extension "vss.duckdb_extension" not found'. This surfaced as 13
failing tests in CI while passing locally, because local runs had the
extensions installed already.

Loading is attempted first so an extension that is already present costs
nothing and never touches the network; INSTALL is reached only once, on a
machine seeing the extension for the first time, and reports an actionable
message if it cannot download.

CI cached the extension directory but nothing populated it, so the cache
saved an empty directory forever. The cache key now derives from Cargo.lock
rather than a hardcoded DuckDB version, and a step on cache miss installs the
extensions so the post-job save has something to store.
2026-08-10 15:47:15 -06:00
Dark-Alex-17 a12cf84eb6 Merge remote-tracking branch 'refs/remotes/origin/main' 2026-08-10 15:41:50 -06:00
Dark-Alex-17 2b45e3a9b8 feat: improved wording and heuristic detection for sisyphus suite of agents
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-10 15:37:53 -06:00
Dark-Alex-17 91dbaf5533 feat: upgraded to sbx kit v2 spec for improved integration
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-10 15:26:16 -06:00
Dark-Alex-17 7a732436aa feat(rag): add attach-only Qdrant provider, attach wizard and sandbox wiring
Adds QdrantProvider as a read-only driver for pre-existing remote Qdrant
collections, an interactive '.rag attach' wizard, and the sandbox credential
and domain-whitelisting wiring that lets an attached RAG work inside a sandbox.

Attach-only by design: rebuild_indexes bails for both the attached and the
unattached case rather than silently succeeding. Coyote never writes to Qdrant
in this change.

Vectors are never hydrated back from Qdrant. Cosine collections L2-normalize
stored vectors on write, so reading them back returns unit-length copies of the
originals; the YAML vector copy is authoritative and the serialization guard
stays scoped to the duckdb driver alone.

Collections keyed by string or UUID point IDs are rejected at attach time. The
read path parses point ids as u64 inside a filter_map, so such a collection
would otherwise yield zero results with no error.

The three response-shape parsers are pure functions over an already-parsed JSON
body, unit-tested against captured fixtures, with the async wrappers delegating
to them rather than duplicating the logic.

'.rag' now splits its first argument, so '.rag attach <name>' no longer tries to
load a RAG literally named 'attach <name>'.
2026-08-10 13:40:22 -06:00
Dark-Alex-17 98d3ba4a83 feat(rag): add DuckDB provider behind the RAG driver abstraction
Phase 3 of the RAG driver abstraction. Adds a `DuckDbProvider` that keeps
vectors and document content in a `.duckdb` sidecar next to the existing
YAML metadata, selected by the `driver: duckdb` field.

- `src/rag/providers/duckdb.rs` (new): vector search via the vss extension
  and keyword search via fts, an all-or-nothing hydration path (a partial
  read is an error, never a shorter map), and an anti-wipe guard that
  refuses the destructive `CREATE OR REPLACE TABLE` when `data.vectors` is
  empty while `data.files` is not and the store still holds rows.
- `src/rag/mod.rs`: `sync_documents` now refreshes `bm25`/`node_to_docs`
  BEFORE the fallible `provider.rebuild_indexes`. `self.data` is already
  mutated by that point, so propagating a provider error afterwards would
  leave the derived in-memory state describing the previous corpus while
  `data` describes the new one. Both rebuilds are pure functions of
  `self.data` and cannot fail, so running them first is always safe.
- `src/config/paths.rs`: sidecar path helpers.
- `src/rag/providers/mod.rs`, `src/config/agent.rs`: driver dispatch and
  RAG cache keying.

Also keeps `RequestContext::rag_key` in lockstep with `rag` at the two
sites that were still missing it, so that a cache insert and its matching
invalidate are structurally incapable of disagreeing:

- `use_agent` assigned `self.rag` from the agent but never set `rag_key`.
  This one was live. Agent RAGs are inserted under `RagKey::Agent(<name>)`,
  so with `rag_key == None` the invalidation guards in `rebuild_rag` and
  `edit_rag_docs` matched nothing and `.rebuild rag` left the stale cache
  entry in place. Worse, a preceding `.rag <name>` left a stale
  `Named(<name>)` key attached to the agent's RAG, pointing the
  invalidation at an unrelated RAG's cache entry. Now mirrors the insert
  key exactly, yielding `None` when the agent has no RAG.
- `exit_agent` cleared `self.rag` but left `rag_key` behind. Latent rather
  than live, since `rebuild_rag`/`edit_rag_docs` both bail on
  `rag.is_none()` before reaching the invalidate guards, but the guards
  that make it unobservable are not the kind of thing to depend on.

Covered by `use_agent_does_not_carry_stale_rag_key`, and by a new
assertion in `exit_agent_clears_all_agent_state`.
2026-08-10 12:51:56 -06:00
Dark-Alex-17 d734276927 build(rag): add duckdb dependency and pin comfy-table to 7.1.4
Adds the duckdb crate with the bundled feature ahead of any provider
code, so the dependency and build surface can be proven on every CI
target on its own.

duckdb constrains comfy-table to ~7.1, so comfy-table moves from 7.2.2
to 7.1.4 while keeping custom_styling. That feature swaps
measure_text_width for an ANSI-stripping implementation that
render_table's pre-styled cells depend on; dropping it still compiles
and still passes every other test, and only corrupts column widths. A
regression test now renders a styled table at a fixed wrap width and
asserts every line has an equal ANSI-stripped display width.

comfy-table 7.1.4 pins crossterm 0.28 while coyote pins 0.29, so both
now build side by side. comfy_table::Color, Attribute and Cell are
consequently crossterm 0.28 types and must not be used; table styling
stays ANSI-string based.

Caches the DuckDB extension directory in CI, keyed on the runner OS and
the DuckDB version, so vss and fts survive an upstream outage.

Also switches merge_vector_results to f32::total_cmp. The previous
partial_cmp().unwrap_or(Equal) comparator is not total under NaN, which
sort_by is permitted to answer with a panic in release builds.
2026-08-10 11:59:57 -06:00
Dark-Alex-17 5049143fcc refactor(rag): extract RagProvider trait and add YamlProvider
Introduce a narrow `RagProvider` trait covering vector search and content
retrieval, and make `Rag` delegate to a boxed provider instead of owning an
HNSW index directly. `YamlProvider` is the sole implementation for now.

The trait deliberately stays narrow: embeddings, chunking, BM25 keyword
search, graph RAG, entity extraction, RRF merging and persistence all remain
on `Rag`/`RagData`, so a new storage backend does not have to reimplement
Coyote's indexing logic.

Notable points:

- `fetch_content`'s ordering contract is part of the trait, not an accident.
  Implementations must return results in input-`ids` order; `hybrid_search`
  passes an RRF-ranked list straight to the prompt builder, so a provider
  returning storage order would silently discard the ranking.
- The content store is keyed on `data.files`, never `data.vectors`. Both the
  content map and BM25 now route through the new `RagData::iter_documents()`
  so the two key spaces match by construction. `RagData::add` zips document
  ids with embeddings and truncates silently, so ids in `files \ vectors` are
  genuinely reachable.
- A provider keyword-search failure degrades to an empty ranker with a
  warning rather than failing the whole query; it is one of three RRF inputs.
  It deliberately does not fall back to the local BM25, which would be a
  silent ranking-algorithm swap once a provider with native FTS exists.
- The rerank path builds its text and id vectors from a single `fetch_content`
  result in one pass, so the reranker's positional indices cannot desync.

This is not a bit-for-bit no-op. `vector_search` now dedups by best score and
sorts globally instead of concatenating per-chunk hit lists. Single-chunk
queries (the common case) are unaffected. Multi-chunk queries get corrected
rank assignment and no longer let a document that matched several query
chunks accumulate multiple RRF contributions. There is no overall cap on the
merged pool — truncation remains `reciprocal_rank_fusion`'s job.

`RagData::get()` is removed: its only two callers were the content lookups
replaced here, and an unused private-module method fails the build under
`--deny warnings`. Its three tests were rewritten against `iter_documents()`,
one of which now guards the files-vs-vectors keying directly.

Implements Phase 2 of the RAG driver abstraction design (§6).
2026-08-10 11:41:37 -06:00
Dark-Alex-17 a968c3228d feat(rag): add driver/attached fields, validation floors and force-reingest
Phase 1 of the RAG driver abstraction (design doc sections 5.1-5.5a).

Data model:
- Add `driver: String` (serde default "yaml" via RagData::default_driver) and
  `attached: bool` as the first two fields of RagData, so driver metadata sits
  at the top of each RAG YAML. Old files without them load unchanged.
- Add `#[serde(default)]` to the non-Option fields so a minimal attached-RAG
  YAML deserializes, and add `skip_serializing_if` to `vectors` so an empty
  map renders no `vectors:` key.
- Add a hand-written `impl Default for RagData` delegating to `RagData::new()`.
  It is deliberately not derived: a derived impl yields `driver: ""`, which is
  not a valid driver string.

Validation (the price of the new serde defaults):
- Add `RagData::validate()`, called from `Rag::load()` after deserialization.
  It enforces the (driver, attached) matrix and, critically, numeric floors
  that the new defaults would otherwise mask: `top_k >= 1` unconditionally
  (a 0 makes every query return nothing, silently), and `chunk_size >= 1` plus
  `chunk_overlap < chunk_size` when not attached (a 0 chunk_size is a real
  divide-by-zero panic while sizing embedding batches).
- Reject `.set rag_top_k 0` at the setter, before the set/update fork. Without
  this, the new load-time floor turns one keystroke into an unloadable RAG:
  the setter saves immediately and no dot-command can reach the file again.

Rebuild actually re-embeds now:
- `.rebuild rag` and `--rebuild-rag` previously re-scanned paths and re-embedded
  nothing, because the content-hash skip fired regardless of the refresh flag.
  Extract that decision into a module-level `find_hash_skip()` free function and
  thread a `force_reingest` flag through `sync_documents()` and
  `refresh_document_paths()`, set true only from `rebuild_rag()`. `.edit rag-docs`
  stays incremental. Re-embedding costs time and API spend, so `rebuild_rag()`
  now prints a one-line file-count warning first (no prompt: the path is
  reachable from a non-interactive CLI flag).

Attached-RAG guards:
- Block `.rebuild rag` / `--rebuild-rag` and `.edit rag-docs` on attached RAGs,
  which Coyote did not index and whose source documents it does not own.
- Add `Rag::driver()`, `Rag::is_attached()` and `Rag::file_count()`, and surface
  driver/attached through `Rag::export()` so `.info rag` shows them.

Adds 12 unit tests (1299 -> 1311), including the two gate tests pinning that a
forced re-ingest does not hash-skip while an ordinary refresh still does.
2026-08-10 11:04:51 -06:00
Dark-Alex-17 f404acdbca Merge branch 'main' 2026-08-06 16:39:02 -06:00
Dark-Alex-17 efa570267d feat(mcp): send RFC 8707 resource indicator in OAuth flows
CI / All (ubuntu-latest) (push) Failing after 27s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-06 16:13:06 -06:00
Dark-Alex-17 9aeb9e6e2e Merge branch 'main' 2026-08-06 13:13:03 -06:00
Dark-Alex-17 3607a180d9 fix: don't output thinking blocks for claude-based models
CI / All (ubuntu-latest) (push) Failing after 23s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-06 13:12:36 -06:00
Dark-Alex-17 d429def0f6 fix: additional edge case fix for duplicate tool call IDs in anthropic API calls
CI / All (ubuntu-latest) (push) Failing after 27s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-06 10:30:33 -06:00
Dark-Alex-17 e606eb7c49 fix: removed temperature modifier in librarian agent to mitigate invisible errors
CI / All (ubuntu-latest) (push) Failing after 27s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-05 16:10:19 -06:00
Dark-Alex-17 a8fb32b6bd fix: strip reasoning blocks for structured LLM output in graph agents 2026-08-05 16:10:03 -06:00
Dark-Alex-17 1f7b8417fa fix: prevent rare duplicate tool call IDs in long running claude prompts 2026-08-05 16:01:40 -06:00
Dark-Alex-17 9540345ec7 feat: Added loaded indicators to .list tools/mcp-servers/skills
CI / All (ubuntu-latest) (push) Failing after 27s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-05 13:11:24 -06:00
Dark-Alex-17 70b6d51b55 test: Implemented unit tests to prevent regression on agent reasoning effort inheritance
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-04 13:12:37 -06:00
Dark-Alex-17 9e5e8a60f2 fix: agents inherit global reasoning effort if unset 2026-08-04 13:09:13 -06:00
Dark-Alex-17 d6447603bc feat: hide .recover from tab completions when no session is active
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-04 12:05:23 -06:00
Dark-Alex-17 e6dc24beb5 feat: add a new .recover command for sessions to recover from errors
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-04 12:02:31 -06:00
Dark-Alex-17 e76c3efe4e feat: integrated the git-ssh-sign kit into the coyote sandbox kit
CI / All (ubuntu-latest) (push) Failing after 26s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-04 10:56:50 -06:00
github-actions[bot] fac577589b chore: bump Cargo.toml and sandbox image to 0.8.3
CI / All (ubuntu-latest) (push) Failing after 26s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-03 21:28:37 +00:00
github-actions[bot] 737fc42ec1 bump: version 0.8.2 → 0.8.3 [skip ci] 2026-08-03 21:28:33 +00:00
Dark-Alex-17 888529f381 fix: infinite loop bug when attempting to interrupt a prompt exchange right before a session compression
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-31 16:00:26 -06:00
Dark-Alex-17 35e75e5b4f fix: ctrl-c inside of an auto-continue loop created an infinite loop
CI / All (ubuntu-latest) (push) Failing after 26s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-31 11:44:16 -06:00
github-actions[bot] dd7d75fd9f chore: bump Cargo.toml and sandbox image to 0.8.2
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-31 16:08:58 +00:00
github-actions[bot] 78f5fc8fb9 bump: version 0.8.1 → 0.8.2 [skip ci] 2026-07-31 16:08:53 +00:00
Dark-Alex-17 4f38214681 fix: sbx update doesn't allow undefined fields in sbx spec
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-31 10:07:29 -06:00
github-actions[bot] 019bd6f1c7 chore: bump Cargo.toml and sandbox image to 0.8.1
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-30 22:37:17 +00:00
github-actions[bot] 875b6749c2 bump: version 0.8.0 → 0.8.1 [skip ci] 2026-07-30 22:37:15 +00:00
Dark-Alex-17 69e1b98c44 fix: ctrl-c interruption doesn't discard session messages when throbber is showing
CI / All (ubuntu-latest) (push) Failing after 27s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-30 13:38:37 -06:00
Dark-Alex-17 0324436114 feat: ctrl-c interrupts ongoing prompt in a session, but lets the user inject more instructions mid-stream
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-30 10:21:19 -06:00
Dark-Alex-17 233c212d2a feat: improved function calling performance by allowing parallel tool calling
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-29 12:40:52 -06:00
Dark-Alex-17 e288b41365 fix: improper handling of fd-style globbing for directories in fs_glob 2026-07-29 12:40:33 -06:00
Dark-Alex-17 fbf6a6bdf4 fix: properly templated architect design doc path in starter commands
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-29 11:59:09 -06:00
Dark-Alex-17 57b72702b2 feat: created the architect and gatekeeper agents for dramatically improved coding performance
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-29 11:23:52 -06:00
Dark-Alex-17 38ba303c3c feat: Improved readability of session message exchange replays
CI / All (ubuntu-latest) (push) Failing after 26s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-29 10:15:35 -06:00
Dark-Alex-17 fc7bc0ff8f fix: .copy works when sessions are resumed 2026-07-29 09:50:40 -06:00
Dark-Alex-17 7f7ea758a7 fix: ACP session/prompt now drives the full tool-execution loop
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
run_prompt_turn previously performed a single completion and returned,
leaving tool calls unexecuted. It now mirrors start_directive's loop:
call -> execute tools -> merge results -> continue until no tool results,
then check the pending-agents guardrail before returning.

The pending-agents guardrail injects a reminder prompt when sub-agents
are pending, matching the same loop termination semantics as --headless.
The session is NOT exited between turns (unlike start_directive) so
multi-turn ACP conversations retain history across session/prompt calls.

Manual gate (tool-probe agent, fs_write + fs_read tools):
  id 3 result: {"output":"DONE:probe.txt","stopReason":"end_turn"}
  probe.txt exists: YES, content: hello
2026-07-28 15:42:51 -06:00
Dark-Alex-17 06b2c384e3 fix: ACP spec conformance — ContentBlock prompt params and protocolVersion type
CI / All (ubuntu-latest) (push) Failing after 26s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
DEFECT 1: session/prompt now accepts the spec-shaped params as primary:
  {"prompt": [{"type": "text", "text": "..."}]}
Text blocks are joined with newlines. Non-text block types are silently
ignored. The legacy params.text alias is preserved as a fallback. -32602
is returned only when neither a non-empty prompt array with text blocks
nor a non-empty text field is present.

DEFECT 2: initialize result now emits protocolVersion as integer 1
instead of the string "1", matching the ACP spec's InitializeResponse.

Tests: 4 new unit tests pin the spec-shaped prompt path, the non-text
block ignore behavior, the missing-both -32602 path, and the numeric
protocolVersion type.
2026-07-28 14:41:22 -06:00
Dark-Alex-17 d50de7c06a lint: fixed test ordering
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-27 19:52:51 -06:00
Dark-Alex-17 0956f08791 refactor: move ACP server dispatch into run() for shared flag setup
All CLI flag processing (--agent, --role, --rag, --model, --no-memory,
--no-stream, --no-workspace-instructions, etc.) now runs through run()
before the REPL/cmd split. The ACP server is dispatched right before
match is_repl, after apply_prelude and skills loading, so it benefits
from the complete context setup with no duplication or drift risk.
2026-07-27 19:44:11 -06:00
Dark-Alex-17 087d0c320c feat: apply --agent/--role/--rag/--model flags in --acp-server mode
These CLI flags were previously ignored because the ACP branch returned
before run() could apply them. Now the context is configured with the
requested agent, role, RAG index, and model before the server starts.
Session management remains protocol-driven via session/new and session/load.
2026-07-27 19:31:45 -06:00
Dark-Alex-17 2128390f99 fmt: applied formatting
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-27 19:26:29 -06:00
Dark-Alex-17 d008de1848 fix: restore stdout output for standalone --headless mode
RenderMode::Silent was incorrectly applied to --headless in addition to
--acp-server. Standalone headless should still display the LLM response;
only ACP mode requires stdout purity for JSON-RPC. The acp server's
run_prompt_turn already sets Silent before each prompt call.
2026-07-27 19:22:51 -06:00
Dark-Alex-17 d79787bf96 fix: suppress tool-call display in headless mode; initialize session on session/new 2026-07-27 19:14:28 -06:00
Dark-Alex-17 577c51b62f fix: skip stdin drain and set silent render mode when --acp-server is active 2026-07-27 18:59:09 -06:00
Dark-Alex-17 d462b09f80 feat: add headless profile to sbx-kit spec 2026-07-27 18:03:35 -06:00
Dark-Alex-17 b711e4983b feat: implement ACP user-interaction to request_permission bridge 2026-07-27 18:00:07 -06:00
Dark-Alex-17 6ae3efb06c feat: implement ACP session/load and session/cancel 2026-07-27 17:52:49 -06:00
Dark-Alex-17 02dd14394b feat: implement ACP session/prompt 2026-07-27 17:48:31 -06:00
Dark-Alex-17 f11d4ca760 feat: add ACP server skeleton with stdout-purity test 2026-07-27 17:31:47 -06:00
Dark-Alex-17 f1415067f2 feat: add --headless flag for unattended operation 2026-07-27 17:17:56 -06:00
Dark-Alex-17 af5c34fde5 Merge branch 'main' of github.com:Dark-Alex-17/coyote 2026-07-27 15:05:25 -06:00
Dark-Alex-17 2ffa278f2d test: testing potential nerdbox regression fix for coyote sandbox mode 2026-07-27 15:04:41 -06:00
Dark-Alex-17 f4cbee9611 docs: remove comment in spec.yaml about copying in coyote password file 2026-07-27 10:50:40 -06:00
Dark-Alex-17 e95fd1e06e ci: fix typo in coyote image tag; needs leading 'v'
CI / All (ubuntu-latest) (push) Failing after 23s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-27 09:51:23 -06:00
Dark-Alex-17 d79ea55e09 fix: include graph-agent descriptions in .agent <TAB> completions
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-27 09:34:36 -06:00
github-actions[bot] b898e4dd78 chore: bump Cargo.toml and sandbox image to 0.8.0
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-25 01:08:02 +00:00
github-actions[bot] 30e00f8332 bump: version 0.7.4 → 0.8.0 [skip ci] 2026-07-25 01:07:59 +00:00
Dark-Alex-17 58d9d4c64e docs: updated help message for --fresh flag
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-24 18:30:45 -06:00
Dark-Alex-17 63a768aa46 fix: fresh wizard openai-compatible support
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-24 18:29:24 -06:00
Dark-Alex-17 2c3f671efa fix: config existence check for --fresh sandboxes
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-24 18:09:06 -06:00
Dark-Alex-17 8b0a536f4e feat: Dynamically detect if a selected client in the sandbox first run wizard supports oauth
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-24 18:07:30 -06:00
Dark-Alex-17 5a1bb569b4 feat: Add support for the --fresh flag again with host environment configuration injection
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-24 18:00:41 -06:00
Dark-Alex-17 0c12580836 fix: Improve coyote sandbox startup time 2026-07-24 17:23:08 -06:00
Dark-Alex-17 df909325a7 fmt: applied formatting
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-24 16:50:27 -06:00
Dark-Alex-17 d379fcddf8 fix: bypass forgotten sandbox mode check for MCP secret interpolation
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-24 16:50:04 -06:00
Dark-Alex-17 b405acd8f0 feat: force overwrite global sbx secrets 2026-07-24 16:45:51 -06:00
Dark-Alex-17 805ae7112a feat: add sbx secrets globally
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-24 16:41:00 -06:00
Dark-Alex-17 6ddbf37523 feat: only create secrets local to a sandbox
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-24 16:36:16 -06:00
Dark-Alex-17 f510bb649b feat: Improved credentials management for docker sandboxes 2026-07-24 16:26:35 -06:00
Dark-Alex-17 c1b14bdfdf docs: Created a mermaid diagram for Sisyphus
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-24 13:55:31 -06:00
Dark-Alex-17 46f2a9eae2 docs: added in forgotten connection to the librarian agent diagram
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-24 13:44:07 -06:00
Dark-Alex-17 cf3a12141b docs: updated the graph agent diagrams to use mermaid diagrams for more easily readable diagramming in their READMEs
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-24 13:39:59 -06:00
Dark-Alex-17 8f13810f0f chore: updated models.yaml 2026-07-24 13:27:20 -06:00
Dark-Alex-17 eba8c86e21 fix: properly wrap sub-style changes in markdown rendering
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-24 12:13:33 -06:00
Dark-Alex-17 d75fb47de1 docs: Updated project license
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-23 21:07:51 -06:00
Dark-Alex-17 49a281bf93 feat: renamed --contents arg for fs_write/patch to --content since most models attempt that first and error otherwise
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-23 19:13:36 -06:00
Dark-Alex-17 b9474ce6ef feat: Used improved theme selections for tool call highlighting colors
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-23 18:51:57 -06:00
Dark-Alex-17 d328310880 feat: Improved theme-derived syntax highlighting for LLM tool call logging 2026-07-23 18:45:23 -06:00
Dark-Alex-17 5eb77ab467 feat: Improved coloring of LLM tool invocation outputs to make LLM output more readable and cohesive 2026-07-23 18:27:54 -06:00
Dark-Alex-17 a2c4f05c8c feat: renamed the user__ask to user__select and improved descriptions to improve model usage 2026-07-23 18:22:18 -06:00
Dark-Alex-17 89fadcca15 feat: Improved coloring/highlighting of tool calls to make LLM invocation logs easier to read 2026-07-23 18:12:18 -06:00
Dark-Alex-17 ab3a818507 docs: Added the new compression control fields to the config example file
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-23 12:40:38 -06:00
Dark-Alex-17 63f73f22c3 style: applied formatting to new long run features
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-23 12:30:30 -06:00
Dark-Alex-17 54c5079cb7 feat: long-running session improvements
- Hang fix: 120s timeout on compression LLM call + raw_stream
  channel-close handling (None => break instead of spinning)
- Supervisor effective active count: stop counting finished
  JoinHandles as occupying capacity slots
- Universal tool result size cap: truncate_if_needed() on
  ToolResult, applied in eval_tool_calls() after escalation block;
  configurable via max_tool_result_chars in AppConfig/AgentConfig
- Windowed compression: compression_keep_last config param keeps
  the N most recent messages visible after compression
- Fix pre-existing flaky test: add #[serial] to
  handle_list_available_unrestricted_when_no_whitelist so it does
  not race with TestConfigDirGuard-based tests that temporarily
  populate the agents data dir
2026-07-23 12:25:54 -06:00
Dark-Alex-17 d51bdd3086 feat: Made sisyphus suite of agents all auto-approved for tool usage
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-22 22:28:20 -06:00
Dark-Alex-17 56ec58a748 fix: removed accidental duplicate ast_grep tool in explore agent
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-22 17:42:14 -06:00
Dark-Alex-17 93f9c5425e fix: npm and npx need the /usr/local/share/npm-global/lib directory to exist to run properly so I've added it to the dockerfile
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-22 17:11:57 -06:00
Dark-Alex-17 77dfd08103 fix: added executable bit to adversary agent tools script 2026-07-22 16:38:42 -06:00
Dark-Alex-17 46dcef0dec Merge branch 'main' of github.com:Dark-Alex-17/coyote 2026-07-22 16:37:34 -06:00
Dark-Alex-17 72c6bb74c2 feat: created the adversay agent and adversarial-review skill 2026-07-22 16:35:00 -06:00
Dark-Alex-17 6bf80dcce9 feat: added ast_grep tool to Sisyphus suite agents
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-22 15:41:09 -06:00
Dark-Alex-17 df948c69bf feat: new spawnable_agents field in agents to let users restrict what agents can be spawned by a parent agent
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-22 14:29:55 -06:00
Dark-Alex-17 e5d0fcc764 test: fixed linter issues on markdown tests
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-22 13:32:37 -06:00
Dark-Alex-17 c60d3e7cda style: updated some stylistic things across the new markdown rendering implementation
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-22 13:20:56 -06:00
Dark-Alex-17 c21fd47b42 feat: replay pre-compressed messages as well when resuming sessions for users to see 2026-07-22 13:05:06 -06:00
Dark-Alex-17 ab0b89dd67 fix: fetch descriptions from graph agent configs as well when listing agents 2026-07-22 12:59:30 -06:00
Dark-Alex-17 420db4bb88 feat: created a new builtin function for agents who can spawn other agents to list available agents via agent__list_available 2026-07-22 12:50:41 -06:00
Dark-Alex-17 dfacf31f6a fix(render): suppress blank lines above rendered table 2026-07-22 12:37:30 -06:00
Dark-Alex-17 8cfd5ee2c4 docs(render): update phase 2.7 SHA after amend 2026-07-22 12:30:34 -06:00
Dark-Alex-17 e82e5ab8e4 test(render): comprehensive table rendering coverage 2026-07-22 12:30:10 -06:00
Dark-Alex-17 d790782ace feat(render): hanging-indent line wrapping for lists and blockquotes 2026-07-22 12:27:13 -06:00
Dark-Alex-17 bf06d5e8f3 feat(render): wire table state machine and finalize hook 2026-07-22 12:23:44 -06:00
Dark-Alex-17 cdfaa0f111 feat(render): render markdown tables with comfy-table 2026-07-22 12:20:02 -06:00
Dark-Alex-17 c062f34852 feat(render): parse table cells and column alignments 2026-07-22 12:16:05 -06:00
Dark-Alex-17 7671d28d6e feat(render): detect markdown table rows and separators 2026-07-22 12:14:45 -06:00
Dark-Alex-17 fcc4a1d2b5 feat(render): add comfy-table dependency and table border style 2026-07-22 12:11:04 -06:00
Dark-Alex-17 b0eeba110d test(render): comprehensive coverage for rich markdown renderer 2026-07-22 11:48:43 -06:00
Dark-Alex-17 d65d63ee50 feat(render): activate rich markdown renderer as default 2026-07-22 11:47:39 -06:00
Dark-Alex-17 9890cf0ddc feat(render): rich block-level markdown rendering (headings, quotes, lists, hr) 2026-07-22 11:45:03 -06:00
Dark-Alex-17 89db5b3887 feat(render): rich inline markdown rendering (bold, italic, code, links) 2026-07-22 11:41:35 -06:00
Dark-Alex-17 f40ba4ccbe feat(render): detect markdown block-level line types 2026-07-22 11:37:11 -06:00
Dark-Alex-17 d2940a8d32 feat(render): precompute markdown scope styles for rich rendering 2026-07-22 11:31:23 -06:00
Dark-Alex-17 ed7ad36475 feat: Created the raw_markdown configuration flag 2026-07-22 11:03:27 -06:00
Dark-Alex-17 393ed16963 fix: chown the full sandbox cache dir, not just the coyote subdir
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-22 10:08:00 -06:00
Dark-Alex-17 3f94d2003a fix: chown the whole coyote cache dir not just the oauth dir in the sandbox
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-22 09:48:11 -06:00
Dark-Alex-17 50ff9008fe chore: updated deepseek models
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-22 09:31:17 -06:00
Dark-Alex-17 9606d7f8aa feat: added a .fork command to fork a new session from a running conversation
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-21 14:54:55 -06:00
Dark-Alex-17 d8ae9e25d9 docs: reworded explore agent instructions to empower agent to spawn as many sub agents as it deems necessary
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-21 14:14:00 -06:00
Dark-Alex-17 006f64bfa0 docs: Improved sisyphus wording to empower agent to spawn as many subagents as necessary 2026-07-21 14:12:51 -06:00
Dark-Alex-17 000559bc9d style: Applied formatting
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-21 11:24:18 -06:00
Dark-Alex-17 3aede58a11 feat(oauth): enable browser-paste PKCE flow for OpenAI-compatible providers
Two coordinated changes that make openai-compatible OAuth providers usable
with a non-localhost redirect_uri (browser shows the callback URL, user
copies it back into the terminal — the same UX Claude uses).

Fix: OpenAICompatibleOAuthProvider::fixed_redirect_uri() previously returned
Some(uri) for any redirect_uri including public HTTPS URLs, which trapped
run_pkce_flow into trying to bind a TCP listener on a public URL. It now
returns Some only for loopback URIs (127.0.0.1, localhost, ::1). Non-loopback
URIs return None, routing run_pkce_flow to the paste branch.

New tri-format paste parser (parse_paste_input):
- Full callback URL (starts with http:// or https://): parse code + state from
  the query string. This is what most modern OAuth providers redirect to and
  what a naive user copies from the browser bar.
- Anthropic-style code#state fragment: preserved for Claude compatibility.
- Bare code: accepted with a warning that CSRF state validation is skipped.
  For providers whose callback page shows only the code with no state.

State validation moved from mandatory to conditional — if a paste didn't
carry state (bare-code path), we warn and skip the check instead of hard-
failing. The listener path (localhost + LAN redirects) still requires state
because the server sends it in the query.

Adds 9 unit tests covering both changes.
2026-07-21 11:14:55 -06:00
Dark-Alex-17 cab1e72b97 fix: fix typo in Gemini's generation_config property to use camelCase exclusively 2026-07-21 11:11:00 -06:00
Dark-Alex-17 cd4bf245e9 chore: updated models.yaml 2026-07-21 11:09:51 -06:00
Dark-Alex-17 82bf6176f8 fix(oauth): treat missing expires_in as non-expiring device_code token
GitHub OAuth Apps issue tokens that never expire and omit expires_in from
the response (they only send access_token, token_type, scope). RFC 6749 §5.1
allows this — expires_in is only REQUIRED for tokens that actually expire.

When expires_in is missing, save the token with expires_at = i64::MAX so
prepare_oauth_access_token never tries to refresh. If the token is ever
revoked server-side, the eventual 401 on the API call is the user's cue
to re-authenticate.

No effect on providers that include expires_in (Moonshot etc. — unchanged).
2026-07-21 10:32:47 -06:00
Dark-Alex-17 d407eb5a6a fix(oauth): send Accept: application/json in device flow requests
GitHub's device flow endpoints (and likely other RFC 8628 servers) default
to responding in application/x-www-form-urlencoded unless the client asks
for JSON via the Accept header. Our device auth and polling paths both call
.json() on the response and were failing to decode form-urlencoded bodies
with 'expected value at line 1 column 1'.

Adds Accept: application/json to:
- The device authorization POST in run_device_code_flow
- The device_code polling POST (on the RequestBuilder returned by build_token_request)

RFC 6749 §5.1 already specifies JSON as the token response format, so this
is spec-compliant across providers. Servers that already default to JSON
(Moonshot, etc.) ignore the redundant header.
2026-07-21 10:30:36 -06:00
Dark-Alex-17 6f2594712f refactor: Standardized paths module function names to not use 'path' in the name and to just always be either 'dir' or 'file'
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-21 10:15:42 -06:00
Dark-Alex-17 79d43c8791 docs: config.example.yaml example for Device Authorization Grant
Adds a commented example under the openai-compatible client section showing
how to configure flow: device_code for RFC 8628 device flow. Uses Moonshot's
kimi-code endpoints as the illustrative reference (users supply their own
client_id — no bundled defaults per §5.8 of the design plan).
2026-07-20 15:27:20 -06:00
Dark-Alex-17 5a5da90734 test: unit tests for OAuthFlow::DeviceCode and merge behavior
Adds 9 unit tests covering:
- yaml deserialization of flow: device_code
- merge preserves base device_authorization_url when user omits
- merge lets user device_authorization_url win
- merge lets user use_pkce_in_device_flow win
- OpenAICompatibleOAuthProvider exposes / defaults both new trait methods
- Full serde roundtrip of a realistic device_code yaml block

No network or polling — pure config/serde logic tests. Brings the test
count from 1134 to 1143.
2026-07-20 15:25:24 -06:00
Dark-Alex-17 f2a0e7453e feat: copy host OAuth tokens into sandbox at launch
Projects ~/.cache/coyote/oauth/ from the host into /home/agent/.cache/coyote/oauth/
inside the sandbox so agents can call OAuth-authenticated providers without
re-authenticating. Same trust model as the existing config-dir and vault-password
copies. One-way copy (not bind-mount) — matches Docker's universal support
surface. Refreshed tokens die with the sandbox instance; run coyote --authenticate
inside if a fresh token is needed (Device Flow works via the QR code render).
2026-07-20 15:23:52 -06:00
Dark-Alex-17 0fe430102a feat: implement OAuth 2.0 Device Authorization Grant (RFC 8628)
Adds a third OAuthFlow variant (device_code) alongside the existing pkce and
client_credentials flows. Device flow enables OAuth for headless environments
where a browser-based callback listener isn't available — the user visits a
verification URL on any device and enters a short user_code.

- OAuthFlow::DeviceCode variant + serde 'device_code' string
- OAuthConfig fields: device_authorization_url, use_pkce_in_device_flow
- OAuthProvider trait: device_authorization_url() / use_pkce_in_device_flow()
- OpenAICompatibleOAuthProvider passes both through from config
- run_device_code_flow() polls the token endpoint per RFC 8628 §3.4–§3.5:
  handles authorization_pending, slow_down (+5s backoff), expired_token,
  access_denied, and unknown errors distinctly
- Sandbox-gated QR code display (via qrcode crate) — scanning with a phone
  is dramatically faster than copy-pasting the URL from a container
- Optional PKCE per draft-ietf-oauth-device-flow §5.4 (default off)
- run_oauth_flow and prepare_oauth_access_token dispatchers wire DeviceCode
  in; refresh path shared with PKCE since both flows produce refresh_tokens
2026-07-20 15:21:32 -06:00
Dark-Alex-17 d13bd32fdf chore: add qrcode dependency 2026-07-20 15:13:40 -06:00
Dark-Alex-17 1f1729ba00 chore: added new kimi models
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-20 14:35:26 -06:00
Dark-Alex-17 107419966d style: Cleaned up some comments and imports 2026-07-20 13:46:41 -06:00
Dark-Alex-17 344ef7526f feat: hint that browser 'paste code' pages can be ignored during callback capture 2026-07-20 13:29:37 -06:00
Dark-Alex-17 13d31f850c fix: OAuth callback listener skips speculative/malformed browser connections 2026-07-20 13:21:44 -06:00
Dark-Alex-17 f6bd02dc73 feat: openai-compatible wizard offers OAuth when provider has bundled oauth defaults 2026-07-20 13:17:26 -06:00
Dark-Alex-17 31df1a720d test: unit tests for OAuthConfig merge + get_oauth_provider_for_client 2026-07-20 13:12:38 -06:00
Dark-Alex-17 4e0e65fc8a docs: config.example.yaml OAuth examples for openai-compatible 2026-07-20 13:09:50 -06:00
Dark-Alex-17 ab85a4f534 feat: validate unique client names at config load 2026-07-20 13:07:41 -06:00
Dark-Alex-17 420447275c refactor: main.rs resolve_oauth_client uses new dispatcher 2026-07-20 13:05:57 -06:00
Dark-Alex-17 cdc40f7302 feat: bundle xAI OAuth defaults in models.yaml 2026-07-20 13:03:15 -06:00
Dark-Alex-17 c611685033 feat: OAuth branch in openai_compatible prepare_* fns 2026-07-20 13:01:28 -06:00
Dark-Alex-17 cac2a3eba0 feat: get_oauth_provider_for_client dispatcher + client_config_info update 2026-07-20 12:57:10 -06:00
Dark-Alex-17 66bbb34d7f feat: OpenAICompatibleOAuthProvider (config-driven OAuthProvider impl) 2026-07-20 12:55:30 -06:00
Dark-Alex-17 68177fdb6a feat: add auth + oauth fields to OpenAICompatibleConfig 2026-07-20 12:51:09 -06:00
Dark-Alex-17 1acaad223f feat: add oauth field to ProviderModels 2026-07-20 12:46:18 -06:00
Dark-Alex-17 aa0270602d feat: add client_credentials support to prepare_oauth_access_token 2026-07-20 12:44:53 -06:00
Dark-Alex-17 4669958bdd refactor: split run_oauth_flow into pkce + client_credentials dispatchers 2026-07-20 12:43:53 -06:00
Dark-Alex-17 559107073d feat: add OAuthConfig + OAuthFlow types to oauth.rs 2026-07-20 12:41:24 -06:00
Dark-Alex-17 d0a38747e0 chore: updated models.yaml
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-20 11:55:00 -06:00
Dark-Alex-17 677bd71b93 docs: added brew trust command to install example for iwe
CI / All (ubuntu-latest) (push) Failing after 26s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-19 15:24:13 -06:00
Dark-Alex-17 ad6d0a2e0e fix: resolve reasoning effort for the prompt for global defaults as well
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-17 17:52:38 -06:00
Dark-Alex-17 50911b99ef fix: Account for default model reasoning_effort when supplying that value for the REPL prompts
CI / All (ubuntu-latest) (push) Failing after 26s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-17 17:42:10 -06:00
Dark-Alex-17 44783c5573 feat: Added reasoning effort to the right prompt
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-17 17:34:10 -06:00
Dark-Alex-17 8629c1ca15 feat: Improved support for Anthropic's extended thinking 2026-07-17 17:26:41 -06:00
Dark-Alex-17 078e6e3744 fix: model narration included in history and between tool calls to prevent repetition
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-17 16:57:51 -06:00
Dark-Alex-17 b908fc20ba docs: Added a docker pulls tracker badge
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-17 16:24:07 -06:00
Dark-Alex-17 058810137c fix: Don't terminate agent loops early for null tool output
CI / All (ubuntu-latest) (push) Failing after 26s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-17 16:17:29 -06:00
Dark-Alex-17 c979041161 fix: reduce code duplication by reusing the new concrete_tool_names function in .list tools
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-17 15:51:01 -06:00
Dark-Alex-17 a606ea552d fix: Agent tools can only be modified via .tool enable/disable using tools in the allowed whitelist in the agent 2026-07-17 15:42:57 -06:00
Dark-Alex-17 39a654a79e fix: re-render agent sessions when entering agents with either pre-configured agent_session or when entering an agent directly into a session 2026-07-17 15:08:19 -06:00
Dark-Alex-17 17d1decce6 feat: Also support GEMINI.md workspace instructions 2026-07-17 15:05:16 -06:00
Dark-Alex-17 a45e66c634 feat: Improved workspace instructions support 2026-07-17 14:52:35 -06:00
Dark-Alex-17 8c885d9a77 feat: also detect .mcp.json configurations at workspace roots 2026-07-17 14:03:08 -06:00
Dark-Alex-17 6dd1e59815 fix: Per RFC 9728, enable dynamic discovery of OAuth endpoints in MCP using path-aware discovery
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-17 13:28:55 -06:00
Dark-Alex-17 f5085a773a fix: hot-attach to MCP servers that require auth after running .mcp auth <name> 2026-07-17 13:16:25 -06:00
Dark-Alex-17 09afdeaf7c feat: Created new .tool enable/disable and .mcp enable/disable aliases to make REPL usage more egonomic 2026-07-17 12:53:59 -06:00
Dark-Alex-17 0216d84eee feat: Created a new .list <kind> REPL command to make discoveribility easier in the REPL 2026-07-17 11:49:49 -06:00
Dark-Alex-17 320dbf2479 fix: Correctly inherit graph-global model for extractor model if none is defined 2026-07-17 11:42:28 -06:00
Dark-Alex-17 6958e9cba8 feat: Support claude-style hidden workspace MCP configuration files via .mcp.json
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-17 10:46:26 -06:00
Dark-Alex-17 825f9f6bf5 feat: Allow users to customize the workspace-specific configuration directory name so they can use Coyote with other CLI clients like .claude 2026-07-17 10:38:35 -06:00
Dark-Alex-17 863740f916 fix: no cursor timeout when user scrolls away from ongoing streaming output
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-16 16:01:19 -06:00
Dark-Alex-17 304088bf5c tests: Added tests for graph-based RAG
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-16 14:33:52 -06:00
Dark-Alex-17 b7599b8acf build: Added just recipe for building the multi-platform image
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-16 13:33:47 -06:00
Dark-Alex-17 9b3ae761f3 feat: Add reasoning_effort validation for the main configuration file
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-16 13:18:28 -06:00
Dark-Alex-17 0f7877aafc feat: Add validation for reasoning_effort settings to prevent users from specifying erroneous values 2026-07-16 13:12:11 -06:00
Dark-Alex-17 5843a9ac15 docs: Fixed broken links in the code-review and file-reviewer agent READMEs 2026-07-16 13:05:39 -06:00
Dark-Alex-17 4bfaabcb99 docs: Added the reasoning_effort field to example configuration files 2026-07-16 13:05:23 -06:00
Dark-Alex-17 f16f858074 Merge branch 'main' of github.com:Dark-Alex-17/coyote
# Conflicts:
#	src/repl/mod.rs
2026-07-16 12:30:21 -06:00
Dark-Alex-17 e9a8c01dc4 feat: Added support for modifying the reasoning effort of reasoning models 2026-07-16 12:28:04 -06:00
Dark-Alex-17 5bbf1b2d71 test: updated repl tests for undo command 2026-07-15 17:00:01 -06:00
Dark-Alex-17 e9c52566b8 feat: Explicitly Prevent .undo usage in graph agents
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-15 16:27:57 -06:00
Dark-Alex-17 4c7de650c0 feat: Added an .undo command to the REPL to let users have more control over the conversation 2026-07-15 16:23:59 -06:00
Dark-Alex-17 6127d964ee chore: update models.yaml 2026-07-15 16:14:33 -06:00
Dark-Alex-17 8bbbd71fec fix: default to the nano or notepad when a configured editor is not found
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-15 15:32:07 -06:00
Dark-Alex-17 7f89a80f7e fix: When EDITOR, VISUAL, or config.editor is defined, don't verify via which
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-15 15:16:41 -06:00
Dark-Alex-17 19cca06db6 ci: bump the coyote image tag version in the sandbox kit spec
CI / All (ubuntu-latest) (push) Failing after 27s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-15 13:24:51 -06:00
Dark-Alex-17 e8df9f119c feat: Improve sandbox startup time by using the prebuilt Coyote image 2026-07-15 13:24:37 -06:00
Dark-Alex-17 8abe297bfe feat: Make coyote available as a docker image 2026-07-15 13:24:21 -06:00
Dark-Alex-17 4ec6daff30 fix: Added a loop exit condition for the diagnostics skill
CI / All (ubuntu-latest) (push) Failing after 26s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-15 11:51:06 -06:00
Dark-Alex-17 9c1067e544 fix: Added directness clause to the diganose role to improve prompt 2026-07-15 11:14:15 -06:00
Dark-Alex-17 2fe6704fbc fix: fs tools now output better error handling to guide the model more effectively
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-14 12:47:43 -06:00
Dark-Alex-17 dd40892ad5 fix: Make fs_read more tolerant of various arg invocation formats. 2026-07-14 12:31:24 -06:00
Dark-Alex-17 ed86b7bfc3 Merge branch 'main' of github.com:Dark-Alex-17/coyote 2026-07-14 11:31:21 -06:00
Dark-Alex-17 f32d72a3f2 feat: Made fs_patch more flexible for different model preferences of patch formats 2026-07-14 11:31:11 -06:00
Dark-Alex-17 7b00638476 style: removed redundant '&' from functions module 2026-07-13 18:11:03 -06:00
Dark-Alex-17 6733b3600f test: Fixed flaky python AST parser test for macOS
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-13 17:46:34 -06:00
Dark-Alex-17 de6010d525 docs: Organized coyote --help output to be more readable
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-13 17:31:20 -06:00
Dark-Alex-17 9b0e26bade feat: Installed nano into the sandbox so that users can edit config files in the sandbox directly 2026-07-13 17:29:10 -06:00
Dark-Alex-17 ac40043c00 style: Removed outdated implementation plan 2026-07-13 17:25:20 -06:00
Dark-Alex-17 d8eec1d427 docs: Documented the new no_workspace_mcp configuration property that disables workspace-local MCP configurations
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-13 17:14:06 -06:00
Dark-Alex-17 382916c3ee style: Removed redundant '&' from paths module function calls 2026-07-13 17:12:58 -06:00
Dark-Alex-17 bc3cc10a7b feat: Support workspace-local skill definitions and MCP configurations 2026-07-13 17:12:34 -06:00
Dark-Alex-17 b91f738209 docs: updated the configuratino examples for graph-based RAG
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-13 16:55:18 -06:00
Dark-Alex-17 4f0dae9b49 feat: fully functional graph-based RAG
CI / All (ubuntu-latest) (push) Failing after 26s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-13 16:50:07 -06:00
Dark-Alex-17 deb673ebc9 fmt: applied some formatting changes 2026-07-13 16:07:19 -06:00
Dark-Alex-17 7fc06ad9bc feat: Implemented graph-based RAG 2026-07-08 21:20:50 -06:00
Dark-Alex-17 e814b9f62d fix: todo functions are injected properly to roles when roles have auto_continue: true and the REPL is started directly into the role
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-08 15:45:50 -06:00
Dark-Alex-17 209fbc9e41 feat: Added a --dangerously-skip-permissions flag to skip permission prompts for tool invocations
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-08 15:17:21 -06:00
Dark-Alex-17 5eb1daf18d feat: Remove the temperature hyperparameter from the diagnose role
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-08 14:59:45 -06:00
Dark-Alex-17 b8990fdfc2 feat: Added a new oauth.redirectHost field to make it possible to further extend MCP support
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-07 15:35:04 -06:00
Dark-Alex-17 7673799d83 feat: Updated the REPL mcp auth path to use the prettified error messaging
CI / All (ubuntu-latest) (push) Failing after 23s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-07 15:18:56 -06:00
Dark-Alex-17 ce212fe660 feat: Improved error messaging for failed MCP starts because of auth issues
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-07 15:09:50 -06:00
Dark-Alex-17 f855a45493 fix: updated the redirect URI for OAuth MCP to use localhost since that's what is whitelisted, not 127.0.0.1
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-07 14:44:35 -06:00
Dark-Alex-17 5be4f45671 feat: Added support for specifying the oauth port and client ID in MCP server configs
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-07 14:31:51 -06:00
Dark-Alex-17 84ce094677 fix: allow MCP OAuth refresh_token to be absent from initial token exchanges
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-07 14:15:39 -06:00
Dark-Alex-17 fa7eadd08a feat: Implemented OAuth support for OpenAI models via Codex endpoints
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-06 11:20:32 -06:00
Dark-Alex-17 af91b89cff feat: merge MCP config when installing bundled mcp config 2026-07-06 09:57:17 -06:00
Dark-Alex-17 d4a6a2fb34 build: Installed ast_grep in the sandbox kit definition
CI / All (ubuntu-latest) (push) Failing after 26s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-04 13:14:22 -06:00
Dark-Alex-17 8f667886c8 docs: Updated the README to mention the installation of ast-grep 2026-07-04 13:14:08 -06:00
Dark-Alex-17 898bac3c69 docs: added the ast_grep tool to the config.example.yaml 2026-07-04 13:13:16 -06:00
Dark-Alex-17 fc0b2ada7e feat: Implemented durable state for sisyphus 2026-07-04 13:02:50 -06:00
Dark-Alex-17 09cdb40420 feat: Installed ast-grep for the explore agent to use for better code exploration 2026-07-04 12:59:05 -06:00
Dark-Alex-17 9d2e936e7f feat: Created the step-runner graph agent for more deterministic coding workflows to produce even more reliable and higher-quality results 2026-07-04 12:50:37 -06:00
Dark-Alex-17 159afbbc06 feat: Improved oracle and sisyphus agents with skill integrations for the new skills 2026-07-04 12:34:09 -06:00
Dark-Alex-17 428d544277 feat: Created new sisyphus family skills to improve performance 2026-07-04 12:28:42 -06:00
Dark-Alex-17 531bdfab7f feat: Created new diagnostic role and skill for use in other contexts 2026-07-04 12:28:24 -06:00
Dark-Alex-17 08f6ea5e6c feat: Added new memory functions for deleting and renaming memory files, as well as new lints for memory expiration dates and staleness of memories to improve the memory system
CI / All (ubuntu-latest) (push) Failing after 28s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-03 22:30:08 -06:00
Dark-Alex-17 ede0f75a89 feat: Created a new iwe skill and installed the iwe MCP server for utilizing large knowledgebases 2026-07-03 22:04:16 -06:00
Dark-Alex-17 2ec2aec4c0 style: updated the previous conversation marker a tad
CI / All (ubuntu-latest) (push) Failing after 26s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-02 16:49:38 -06:00
Dark-Alex-17 c2cb4ac433 feat: Session-specific, file-backed history in the REPL
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-02 16:44:55 -06:00
Dark-Alex-17 605a9170b0 feat: Replay session output when a user re-enters a session so all output can be seen again 2026-07-02 16:35:10 -06:00
Dark-Alex-17 385bd3eda2 fix: Overrode the default JSON content-type for MCP OAuth so its properly application/x-www-form-urlencoded
CI / All (ubuntu-latest) (push) Failing after 26s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-02 15:53:29 -06:00
Dark-Alex-17 6c3d96ac83 feat: Added confirmation message after MCP Oauth succeeds when invoked from --auth-mcp
CI / All (ubuntu-latest) (push) Failing after 26s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-02 15:22:22 -06:00
Dark-Alex-17 aa1fe7f7aa fmt: applied formatting 2026-07-02 15:22:00 -06:00
Dark-Alex-17 5e50828108 fix: typo in mcp file name 2026-07-02 15:20:57 -06:00
Dark-Alex-17 693e2d9672 feat: Created the --auth-mcp CLI flag to let users auth with remote MCP servers without needing to be in the REPL
CI / All (ubuntu-latest) (push) Failing after 26s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-02 14:51:52 -06:00
Dark-Alex-17 16f324cefc feat: add OAuth authentication support for remote MCP servers 2026-07-02 14:43:24 -06:00
Dark-Alex-17 cc50d39ab4 fix: Added uvx wrapper for macos-based sandboxes
CI / All (ubuntu-latest) (push) Failing after 28s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-02 12:57:12 -06:00
Dark-Alex-17 fc23b532d9 feat: Added mixin for sisyphus so the ddg MCP server can search arbitrary domains 2026-07-02 12:56:18 -06:00
Dark-Alex-17 c2d4240138 perf: updated the memory injection warning so it only logs once, rather than after each keystroke
CI / All (ubuntu-latest) (push) Failing after 27s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-02 12:47:57 -06:00
Dark-Alex-17 cd1b043b1e feat: added improved error messaging on MCP server initialization
CI / All (ubuntu-latest) (push) Failing after 26s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-02 11:42:12 -06:00
Dark-Alex-17 81b4f6a76e feat: prefer musl versions for linux when running --update/.update
CI / All (ubuntu-latest) (push) Failing after 27s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-02 11:31:34 -06:00
github-actions[bot] d48b11dcfa chore: bump Cargo.toml to 0.7.4
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-02 01:06:00 +00:00
github-actions[bot] 86dd922d2c bump: version 0.7.3 → 0.7.4 [skip ci] 2026-07-02 01:05:42 +00:00
Dark-Alex-17 9ec20d74a4 chore: updated models.yaml
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-01 19:04:55 -06:00
Dark-Alex-17 c78cdef5ae fix: Added back in --kit specification for the running of the sbx
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-01 18:52:34 -06:00
Dark-Alex-17 3df590f276 fix: sbx isn't copying base files in their respective directories
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-01 18:44:07 -06:00
Dark-Alex-17 91300c16fe fix: Update deprecated sbx kit config 2026-07-01 17:52:04 -06:00
Dark-Alex-17 52356ead6c fix: Properly chown the coyote config recursively and password file in the sbx
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-01 17:12:42 -06:00
Dark-Alex-17 ad9fc524d4 feat: Pin specific usql version to sbx kit 2026-07-01 17:11:27 -06:00
Dark-Alex-17 af50909a89 feat: recursively take ownership over the copied in coyote config for the sbx 2026-07-01 16:54:57 -06:00
Dark-Alex-17 318d9ba1cd feat: explicitly specify the COYOTE_CONFIG_DIR in the sbx kit 2026-07-01 16:54:42 -06:00
Dark-Alex-17 45d709f28e Merge branch 'main' of github.com:Dark-Alex-17/coyote
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-06-30 14:55:08 -06:00
Dark-Alex-17 9cd074cb9b build: fixed coyote install scripts 2026-06-30 14:54:59 -06:00
Dark-Alex-17 93eec45473 docs: fixed coyote install script URLs 2026-06-30 14:43:00 -06:00
Dark-Alex-17 e585e0b049 feat: --tail-logs can track log rollovers and incoporates a sleep timer to minimize idle CPU cycles
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-06-25 14:01:38 -06:00
Dark-Alex-17 13bfaf9aca feat: Added support for log rolling so log files don't just blow up over time 2026-06-25 13:57:15 -06:00
github-actions[bot] 040dad05d2 chore: bump Cargo.toml to 0.7.3
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-06-24 18:20:20 +00:00
github-actions[bot] 1ba38860f2 bump: version 0.7.2 → 0.7.3 [skip ci] 2026-06-24 18:20:16 +00:00
Dark-Alex-17 84ec5fe7b8 fix: apply bootstrapping of functions at startup to fix edge case
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-06-24 12:13:55 -06:00
github-actions[bot] 1684788fe6 bump: version 0.7.1 → 0.7.2 [skip ci] 2026-06-19 18:51:49 +00:00
Dark-Alex-17 4b7e242998 fix: usql version upgrade
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-06-19 12:45:41 -06:00
github-actions[bot] f69aba2dd8 chore: bump Cargo.toml to 0.7.1
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-06-19 18:04:58 +00:00
github-actions[bot] c3487ecd0e bump: version 0.7.0 → 0.7.1 [skip ci] 2026-06-19 18:04:56 +00:00
Dark-Alex-17 db75391fb6 Merge branch 'main' of github.com:Dark-Alex-17/coyote
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-06-19 11:44:28 -06:00
Dark-Alex-17 e3815af69b fix: sbx mixins must be passed in directories, not as files and the files must be named spec.yaml per new sbx version
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-06-19 11:44:16 -06:00
github-actions[bot] 66a485f924 chore: bump Cargo.toml to 0.7.0 2026-06-18 22:40:24 +00:00
github-actions[bot] 49d7204f89 bump: version 0.6.0 → 0.7.0 [skip ci] 2026-06-18 22:40:19 +00:00
Dark-Alex-17 bbcae1fc2b feat: added configurable cache path via the COYOTE_CACHE_PATH environment variable
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-06-18 16:30:24 -06:00
Dark-Alex-17 3ff27a7935 feat: added a memory option to .set tab completions
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-06-18 15:50:23 -06:00
Dark-Alex-17 373d80121a lint: Fixed linter complaints in paths module
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-06-18 14:32:07 -06:00
Dark-Alex-17 3299a4699e refactor: Migrated the .skills command completion to use StateFlags and updated the help messages 2026-06-18 14:30:55 -06:00
Dark-Alex-17 d4dbda1e89 fix: rebuild the tool scope after dynamically updating the skills_enabled value in the REPL 2026-06-18 13:01:38 -06:00
Dark-Alex-17 e77fa6ef42 feat: Added a diagnostic .info tools subcommand to make it easier to see what tools are enabled in all contexts 2026-06-18 13:01:11 -06:00
Dark-Alex-17 241dda24f0 feat: Added additional info outputs for enabled skills and sbx directories 2026-06-18 11:58:29 -06:00
Dark-Alex-17 e5668e4495 docs: Added sandboxes to the README 2026-06-18 11:57:58 -06:00
Dark-Alex-17 4a01e9a66c fmt: applied formatting 2026-06-18 11:29:03 -06:00
Dark-Alex-17 530000bc2f fix: properly resolve Windows-based local vault password file locations and bootstrap them into the sandbox when possible 2026-06-18 11:28:54 -06:00
Dark-Alex-17 f2e8f3ab59 fix: auto-translation of user-prefixed Mac and Linux paths for the vault password file when running inside a sandbox 2026-06-18 10:53:38 -06:00
Dark-Alex-17 2f33b6631e feat: directly execute shell commands from within the REPL 2026-06-18 08:19:01 -06:00
Dark-Alex-17 8c288195a0 feat: created mixin kit for built-in functions and MCP servers 2026-06-17 15:10:40 -06:00
Dark-Alex-17 e6a5e67a8e feat: Added sbx mixins for the secrets providers so users can also bootstrap those as well. 2026-06-17 14:57:35 -06:00
Dark-Alex-17 6ae474c79e feat: added support for loading sbx mixins that are dynamically discovered in the users workspace and config directory 2026-06-17 14:39:32 -06:00
Dark-Alex-17 8e0b07c9fb docs: Updated the --fresh command help message 2026-06-17 14:20:38 -06:00
Dark-Alex-17 69589bd5e5 feat: Added a --fresh flag to let users create a truly bare bones sandbox without bootstrapping their config 2026-06-17 14:20:17 -06:00
Dark-Alex-17 587df087ed feat: initial built-in sandboxing support powered by Docker sbx 2026-06-17 14:11:04 -06:00
Dark-Alex-17 ee100eef96 fix: don't attempt to auto complete .vault list in the REPL; that's the end of the command 2026-06-17 12:50:04 -06:00
Dark-Alex-17 14969e35fa fix: buffer tool stdout as well as stderr so that any tools that error to stdout are captured and included in the response to the model, enabling the model to see what went wrong and to reason about how to fix it.
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-06-16 15:07:55 -06:00
Dark-Alex-17 b927e2a200 fix: auto-bootstrapped memory was accidentally putting the MEMORY.md directly in the repo root rather than .coyote/memory/MEMORY.md
CI / All (ubuntu-latest) (push) Failing after 23s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-06-15 15:05:51 -06:00
Dark-Alex-17 6ce69ee989 fix: improved the fs_patch script description and added improved error handling to it. 2026-06-15 15:05:18 -06:00
Dark-Alex-17 dc6d736df3 build: pinned aws-smithy-types and time to fix yesterday's release of aws-smithy-types issues 2026-06-12 17:33:49 -06:00
Dark-Alex-17 2a79616f8b feat: Added the ability to auto-bootstrap workspace memory when in git repos 2026-06-11 16:03:00 -06:00
Dark-Alex-17 eb6a02f947 test: fixed broken memory test after updating index format 2026-06-11 20:28:33 -06:00
Dark-Alex-17 00939e4634 feat: Added explicit guardrail handling for pending agents 2026-06-11 20:20:14 -06:00
Dark-Alex-17 6ebd32d47c fix: added in forgotten require_max_tokens to the fable model 2026-06-11 16:11:22 -06:00
Dark-Alex-17 73c4449e7f feat: auto-append memory to memory index and don't necessarily require the LLM to remember to do it after a write 2026-06-10 21:31:37 -06:00
Dark-Alex-17 7143b50d98 fix: append memory functions to non-graph based agents on init 2026-06-10 21:07:56 -06:00
Dark-Alex-17 de38e663a0 docs: Added some docs for the memory system and the --init-memory flag 2026-06-10 20:26:31 -06:00
Dark-Alex-17 10de6025b5 feat: Added an --init-memory [global|workspace] flag to easily and quickly enable memory 2026-06-10 20:26:17 -06:00
Dark-Alex-17 0d2292bff6 feat: added memory global configuration settings to the output of --info and .info 2026-06-10 20:10:45 -06:00
Dark-Alex-17 eb38ca0bbb docs: updated example configurations to include settings for the new memory system 2026-06-10 20:06:17 -06:00
Dark-Alex-17 1931331163 fix: when auto_continue is disabled via the .set auto_continue false command, it should strip the todo functions from the list of functions 2026-06-10 19:31:19 -06:00
Dark-Alex-17 218750cc1e feat: added .set memory REPL commands to control memory injection and applied formatting 2026-06-10 19:24:08 -06:00
Dark-Alex-17 a10b23dbc1 test: added more unit tests for the memory system 2026-06-10 18:44:32 -06:00
Dark-Alex-17 19d2340489 feat: Create the built-in memory management tools 2026-06-10 18:35:59 -06:00
Dark-Alex-17 4ece3d3df1 feat: Append the memory system prompts (readonly or r/w) to the system prompt when applicable 2026-06-10 18:19:37 -06:00
Dark-Alex-17 6d5cbfa56d feat: Created the --no-memory CLI flag to disable memory for this invocation 2026-06-10 17:53:40 -06:00
Dark-Alex-17 7e097e0465 feat: Added the memory configuration properties and storage to the main app config, roles, sessions, and agents. 2026-06-10 17:50:28 -06:00
Dark-Alex-17 b2d70a3fd3 feat: initial scaffolding of a memory system 2026-06-10 17:24:45 -06:00
Dark-Alex-17 3183fedca9 chore: updated models.yaml to include claude Fable 5 2026-06-10 17:18:58 -06:00
Dark-Alex-17 33c6f2c4e3 fix: use rawPredict for non-streaming Claude requests
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-06-09 23:05:31 -06:00
Dark-Alex-17 bca25404ab docs: migrated location of skill_instructions examples in example configs
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-06-05 15:34:25 -06:00
github-actions[bot] 161fa2d983 chore: bump Cargo.toml to 0.6.0 2026-06-05 21:30:01 +00:00
github-actions[bot] 0e93775491 bump: version 0.5.0 → 0.6.0 [skip ci] 2026-06-05 21:29:49 +00:00
Dark-Alex-17 c00c4ff84a test: added a few additional tests to the request_context surrounding the skills system 2026-06-05 15:24:51 -06:00
Dark-Alex-17 46685cb641 fix: disable skills for specific built-in roles 2026-06-05 15:11:22 -06:00
Dark-Alex-17 165d0d113d feat: added skill hint prompt injection and configuration 2026-06-05 14:48:54 -06:00
Dark-Alex-17 70dc7c9680 fix: redirect stderr into user's /dev/tty for guards 2026-06-05 12:46:52 -06:00
Dark-Alex-17 4eac536327 docs: updated the graph.example.yaml 2026-06-05 11:53:19 -06:00
Alex Clarke 8e0fa79ff3 Merge pull request #13 from Dark-Alex-17/skills
Implement support for skills and refactored secrets providers
2026-06-05 11:43:15 -06:00
Dark-Alex-17 68a912ec38 fix: azure doesn't support underscores in key vault 2026-06-05 11:29:14 -06:00
Dark-Alex-17 f405ec5e16 chore: updated models.yaml 2026-06-05 11:28:55 -06:00
Dark-Alex-17 b997e9493c fix: accidental regression on enabled_skills being empty = all 2026-06-04 16:12:32 -06:00
Dark-Alex-17 8d6e9bef32 fix: greedy secrets regex caused multiple secrets on one line to fail 2026-06-04 15:41:56 -06:00
Dark-Alex-17 e54a2e42c9 test: improve vault password file errors by propagating up 2026-06-04 15:32:31 -06:00
Dark-Alex-17 b1696c3425 refactor: removed redundant skill name validation from has_skill function 2026-06-04 14:58:33 -06:00
Dark-Alex-17 feef3f67b5 style: miscellaneous cleanup 2026-06-04 14:30:56 -06:00
Dark-Alex-17 dc066bee0d fmt: applied formatting 2026-06-04 14:21:06 -06:00
Dark-Alex-17 6c4e042dad fix: add agent context check to skill visibility validation 2026-06-04 14:19:38 -06:00
Dark-Alex-17 30f3b01358 feat: Fallthrough on missing secrets during mcp.json merging 2026-06-04 14:19:24 -06:00
Dark-Alex-17 ebf3b5f776 test: improved skill validation test in graph validator 2026-06-04 14:02:34 -06:00
Dark-Alex-17 84dcb3078b feat: validate visible_skills field at config load time 2026-06-04 13:43:40 -06:00
Dark-Alex-17 7b320e08c4 fix: enforced global visible_skills in llm node validation and improved skill loading error handling across the project 2026-06-04 13:09:43 -06:00
Dark-Alex-17 7078280b3d fix: restore agent skill policy on error during effective policy calculation 2026-06-04 12:16:42 -06:00
Dark-Alex-17 43607dbe8d fix: apply the same validation for skill filenames on list_skills as happens everywhere else 2026-06-04 12:10:00 -06:00
Dark-Alex-17 8f7a57f8e6 fix: the vault's init_bare should try to load the provisioned secret_provider from the config file without also interpolating any of the rest of the configuration file. It should only fail if the user has not yet created a configuration file; i.e. done a first-time run. 2026-06-04 12:02:43 -06:00
Dark-Alex-17 40fdf3aaa7 fix: the vault roundtrip test used characters that are unsupported by some major secrets providers 2026-06-04 11:20:46 -06:00
Dark-Alex-17 46d4b78ccc fix: fixed tool filtering logic for skills and user functions in agents 2026-06-04 11:03:44 -06:00
Dark-Alex-17 b0a3b0a9a5 feat: implemented reflexion (sorta) in sisyphus for significant code changes to delegate to the code-reviewer agent 2026-06-04 10:40:14 -06:00
Dark-Alex-17 53b3ce9ab1 feat: improved explore agent 2026-06-04 10:39:46 -06:00
Dark-Alex-17 44f533018e fix: privilege leak when unloading skills and leaving tool scope untouched 2026-06-04 10:17:01 -06:00
Dark-Alex-17 bbb23f4884 fix: When bootstrapping an app config to interpolate secrets, clone the secrets provider configuration as well so config secrets stored in remote vaults can be used properly 2026-06-04 10:07:46 -06:00
Dark-Alex-17 8de0eef4f9 fix: forgot to move back up the vault probe value error to be before the delete 2026-06-04 09:32:25 -06:00
Dark-Alex-17 73a4499c68 fix: don't silently fail on skill role composition extraction in llm nodes 2026-06-04 09:09:55 -06:00
Dark-Alex-17 97100bee29 fix: set -euo pipefail for the temp script in execute_command.sh tool 2026-06-03 15:26:23 -06:00
Dark-Alex-17 9a25438643 fix: added forgotten skill name validation to has_skill to prevent side-channel attacks 2026-06-03 15:21:16 -06:00
Dark-Alex-17 f6da937c5d fmt: applied formatting 2026-06-03 15:05:58 -06:00
Dark-Alex-17 eeaeb42c9a fix: use unique values for the secrets round trip verification 2026-06-03 15:04:50 -06:00
Dark-Alex-17 1dde7f4442 fix: stop interpolating a line if any errors occur 2026-06-03 15:02:22 -06:00
Dark-Alex-17 9879980304 fix: added path validation for skill names 2026-06-03 14:59:57 -06:00
Dark-Alex-17 7ec81ae607 fix: effective_policy unconditionally overwrote skill values for role-like structs 2026-06-03 14:54:42 -06:00
Dark-Alex-17 dac2a16677 feat: removed conditional fallback of LLM_*_RAW_JSON from built-ins 2026-06-03 14:40:42 -06:00
Dark-Alex-17 260bf4e5bc fmt: applied formatting to refactored mcp_servers and tools lists 2026-06-03 14:02:06 -06:00
Dark-Alex-17 ece66448e0 refactor: support both CSV and list formats for enabled_tools 2026-06-03 13:58:24 -06:00
Dark-Alex-17 a254d60876 refactor: Support both CSV and list formats for enabled_mcp_servers 2026-06-03 13:23:13 -06:00
Dark-Alex-17 c36c4f4699 feat: updated enabled_skills handling to support both list and comma-separated strings 2026-06-03 12:24:10 -06:00
Dark-Alex-17 4a14d80d97 feat: added new REPL set commands for toggling skills and changing what skills are enabled 2026-06-03 12:23:53 -06:00
Dark-Alex-17 c6a9268856 docs: improved fs_patch and fs_write descriptions and examples 2026-06-03 12:06:39 -06:00
Dark-Alex-17 2914a1070b feat: upgraded to the latest version of mcp-remote 2026-06-03 11:46:52 -06:00
Dark-Alex-17 5ebf8649a6 fmt: applied uniform formatting across refactored vault code 2026-06-03 11:15:11 -06:00
Dark-Alex-17 0272412334 feat: fs_grep now works with both files and directories 2026-06-03 10:48:18 -06:00
Dark-Alex-17 7a7824be6a feat: improved code reviewer agents with skills 2026-06-03 10:40:34 -06:00
Dark-Alex-17 aa2d4f3265 fix: updated execute_command to not mangle heredocs and also added explicit instructions to the coder and sisyphus agents to use fs_write and fs_patch over execute_command when writing files 2026-06-03 10:20:39 -06:00
Dark-Alex-17 28a283283f docs: Updated configuration example to include new secret provider support 2026-06-03 08:36:03 -06:00
Dark-Alex-17 652ab0b180 feat: added round trip validation for vault providers to ensure permissions and authentication 2026-06-03 08:30:47 -06:00
Dark-Alex-17 8ad764527d feat: created new first-time run wizard for secrets provider 2026-06-03 08:08:06 -06:00
Dark-Alex-17 bba094086d feat: vault_password_file or nothing at all is shorthand for just using the local gman provider for secret management 2026-06-02 14:52:36 -06:00
Dark-Alex-17 658ca7fec3 feat: refactored gman usage to be generic and work with various vault providers and use the SupportedProvider enum directly for configurations 2026-06-02 14:16:45 -06:00
Dark-Alex-17 156de15a33 feat: created initial parity gman generalization for vault provider 2026-06-02 13:59:32 -06:00
Dark-Alex-17 695a684b8d build: upgraded to gman 0.5.0 2026-06-02 13:59:10 -06:00
Dark-Alex-17 307e2cfc50 docs: documented the llm node skills policy in the graph.example.yaml 2026-06-02 13:58:59 -06:00
Dark-Alex-17 ed59f793fc docs: documented the llm node skills policy in the graph.example.yaml 2026-06-02 13:14:41 -06:00
Dark-Alex-17 c17db05f39 feat: Refactored the sisyhpus agent system to utilize the new skills system to improve performance and reliability 2026-06-02 13:14:25 -06:00
Dark-Alex-17 b1782b614f fix: llm nodes accidentally skipped skill_registry::effective_role because I was passing an inline role instead 2026-06-02 12:58:14 -06:00
Dark-Alex-17 2acff31213 feat: llm graph nodes support skills 2026-06-02 12:39:43 -06:00
Dark-Alex-17 a564085449 feat: updated sisyphus and coder tools 2026-06-02 11:13:30 -06:00
Dark-Alex-17 2d5cdb96d2 fix: updated temperature values for all agents and roles 2026-06-02 10:41:20 -06:00
Dark-Alex-17 5a47a6637f fix: added back in require_max_tokens for new Claude models 2026-06-02 10:30:40 -06:00
Dark-Alex-17 625a251931 docs: Updated skill docs to mention that function calling support must be enabled for skills to work at all 2026-06-02 09:55:08 -06:00
Dark-Alex-17 d0ebe7408f fix: skill support also requires function calling to be enabled 2026-06-02 09:42:36 -06:00
Dark-Alex-17 976ba7066d fix: non_tty tests break on some TTY terminals 2026-06-01 16:51:04 -06:00
Dark-Alex-17 ff3789f869 style: removed now deprecated SkillRegistry::new and skillRegistry::load methods 2026-06-01 16:45:34 -06:00
Dark-Alex-17 744dd213f5 fix: skill loading on agents 2026-06-01 16:37:17 -06:00
Dark-Alex-17 f6b4bf05b6 fix: forgot to bootstrap skills on REPL startup 2026-06-01 16:11:23 -06:00
Dark-Alex-17 94e3c3535c feat: removed potentially confusing tab completions for .skill 2026-06-01 16:04:22 -06:00
Dark-Alex-17 31b44fbeb7 fix: remove now deprecated .skill edit command 2026-06-01 15:58:06 -06:00
Dark-Alex-17 07f4b134b6 docs: Added example skills configurations 2026-06-01 15:50:20 -06:00
Dark-Alex-17 5c374bb5bf feat: .edit skill <name> support from within the REPL 2026-06-01 15:48:19 -06:00
Dark-Alex-17 0f90dd5f53 feat: Added skills_dir to the info output of Coyote 2026-06-01 15:30:22 -06:00
Dark-Alex-17 d07caf2a4b fmt: Applied uniform formatting to skills implementation 2026-06-01 15:21:00 -06:00
Dark-Alex-17 81a2bd1d00 feat: Created a few auto built-in skills 2026-06-01 15:20:12 -06:00
Dark-Alex-17 5fa6ffb81d feat: Added support for auto_unload skills during chat 2026-06-01 15:19:59 -06:00
Dark-Alex-17 1faab15377 feat: cleaned up skill implementation 2026-06-01 15:13:50 -06:00
Dark-Alex-17 a4ddc3d65d feat: support multiple skill flags to load multiple skills at CLI startup 2026-06-01 14:27:40 -06:00
Dark-Alex-17 588c69ea6c feat: Modified --skill CLI to allow users to specify skills to start the REPL or CLI with. 2026-06-01 14:20:45 -06:00
Dark-Alex-17 bf8dad2a4f feat: added CLI --skill flag for modifying skills easily 2026-06-01 14:05:16 -06:00
Dark-Alex-17 2e06c0e7d2 feat: REPL integration with skills 2026-06-01 13:43:43 -06:00
Dark-Alex-17 de42cae87f feat: dynamic loading/unloading of skill tools and MCP servers whenever load_skill/unload_skill are invoked 2026-06-01 13:22:44 -06:00
Dark-Alex-17 cdc4bd154a feat: created built-in functions for listing, loading, and unloading skills 2026-06-01 12:58:42 -06:00
Dark-Alex-17 aa2e627a5f feat: implemented the skills policy to track available skills per context 2026-06-01 12:26:30 -06:00
Dark-Alex-17 3359c62429 feat: added remote install and install support for skills 2026-06-01 11:58:35 -06:00
Dark-Alex-17 75a6a5e145 feat: created the skill registry 2026-06-01 11:41:04 -06:00
Dark-Alex-17 a9cad501ff tests: update skill tests 2026-06-01 11:19:02 -06:00
Dark-Alex-17 26584c7500 feat: decided to make skills persist to disk like agents and not in-memory like built-in roles 2026-06-01 11:17:55 -06:00
Dark-Alex-17 62fdf4a2b5 feat: scaffold skill module 2026-06-01 10:22:46 -06:00
Dark-Alex-17 296aa6f50f docs: fix typo in config.example.yaml 2026-05-29 10:47:15 -06:00
Dark-Alex-17 93cc498731 chore: updated models.yaml 2026-05-28 16:23:08 -06:00
github-actions[bot] b1cd8351fa chore: bump Cargo.toml to 0.5.0 2026-05-27 21:27:54 +00:00
github-actions[bot] ccf5e73341 bump: version 0.4.0 → 0.5.0 [skip ci] 2026-05-27 21:27:49 +00:00
Dark-Alex-17 be5d280c32 fix: bash-based user interactions in agents accidentally regressed in graph implementation 2026-05-27 15:20:19 -06:00
Dark-Alex-17 6633a8c0bf fix: Claude function calling in agent contexts 2026-05-27 14:47:27 -06:00
Dark-Alex-17 097d8936e3 fix: Claude code rate limit error per new Claude changes 2026-05-27 14:06:17 -06:00
Dark-Alex-17 8a53b7934b fmt: apply uniform formatting with name change 2026-05-27 12:57:05 -06:00
Dark-Alex-17 0facb15e32 feat: rename Loki to Coyote 2026-05-27 12:47:32 -06:00
Dark-Alex-17 c172736362 docs: clarified OAuth more 2026-05-22 19:56:00 -06:00
github-actions[bot] 4a2b9fa42a bump: version 0.3.0 → 0.4.0 [skip ci] 2026-05-23 01:53:47 +00:00
Dark-Alex-17 98db37866c docs: Fixed a typo in the README 2026-05-22 19:49:40 -06:00
Dark-Alex-17 ad31fbd169 test: fixed broken cross tests that required home directory access 2026-05-22 19:49:01 -06:00
Dark-Alex-17 d69e28fd39 docs: fixed broken sharing configurations link 2026-05-22 19:48:44 -06:00
Alex Clarke 279eaa5300 Merge pull request #12 from Dark-Alex-17/develop
Release v0.4.0: Graph-based agents, remote asset installation, self-update and god-config refactor
2026-05-22 19:18:13 -06:00
Dark-Alex-17 e687d78931 build: Removed unnecessary Language import for Windows systems 2026-05-22 19:04:46 -06:00
Dark-Alex-17 0c2e4df647 feat: LLM node failures propgate up 2026-05-22 18:27:03 -06:00
Dark-Alex-17 6221875f64 build: upgraded to rust v1.95.0 2026-05-22 18:11:01 -06:00
Dark-Alex-17 895b9c27db chore: removed the deprecated haiku 3.5 Claude model 2026-05-22 17:53:49 -06:00
Dark-Alex-17 e661ca2eda docs: Added sharing configurations links in the main README 2026-05-22 17:47:58 -06:00
Dark-Alex-17 7066edd904 feat: Added .install remote tab completions to the REPL 2026-05-22 17:44:16 -06:00
Dark-Alex-17 61bdf29bea feat: feature complete install remote with category selection 2026-05-22 17:00:11 -06:00
Dark-Alex-17 ef39c7d9ff feat: Support to interactively add secrets to Loki that are missing from MCP configs when merging 2026-05-22 16:47:25 -06:00
Dark-Alex-17 e9e46158e7 feat: Added MCP config merging support for remote asset installations 2026-05-22 16:30:45 -06:00
Dark-Alex-17 34dc4b0dce fix: Generified the functions usage of script detection for an executable bit on unix systems 2026-05-22 16:01:28 -06:00
Dark-Alex-17 cd226577e7 feat: install remote now writes files to disk 2026-05-22 15:55:37 -06:00
Dark-Alex-17 b5fc633454 feat: Created basic install_remote functions 2026-05-22 15:33:37 -06:00
Dark-Alex-17 484b18ef16 feat: Created a more comprehensive and immediately useful default config for first runs 2026-05-22 14:16:03 -06:00
Dark-Alex-17 7333046cfe fix: merge required claude code system prompt into instructions 2026-05-22 13:51:45 -06:00
Dark-Alex-17 815f0e5c39 feat: Created an example graph-based agent called deep-research 2026-05-22 12:57:56 -06:00
Dark-Alex-17 dacccbfcf7 feat: Improved coder agent that is now a graph-based agent 2026-05-22 12:57:12 -06:00
Dark-Alex-17 5370637274 docs: Removed slightly-confusing wording in the README 2026-05-22 12:56:49 -06:00
Dark-Alex-17 e6da252a5a feat: Removed indicatif spinners. The UX just won't stop clobbering for parallel graph nodes 2026-05-22 12:56:04 -06:00
Dark-Alex-17 4aaff21f45 fix: updated argc argument passing in run-tool and run-agent scripts 2026-05-21 17:06:20 -06:00
Dark-Alex-17 2678afe02b docs: updated the graph.example.yaml to document the agent environment variables. 2026-05-21 13:29:38 -06:00
Dark-Alex-17 558b764db8 feat: Added agent variables support for graph agents and improved script executor to use the same environment variables as normal agent tool calling for further flexibility 2026-05-21 13:27:33 -06:00
Dark-Alex-17 0bb312a85c feat: Improved UX with colored spinners for parallel graph agents and no clobbering outputs for sub-agents 2026-05-21 13:00:44 -06:00
Dark-Alex-17 d81d233527 feat: created new graph-based deep-research agent 2026-05-21 11:27:55 -06:00
Dark-Alex-17 597f823bdf fmt: cleaned up graph implementation 2026-05-21 11:27:29 -06:00
Dark-Alex-17 81c037515e feat: improved UX for parallel graph execution 2026-05-20 18:54:20 -06:00
Dark-Alex-17 3c7d19da07 fix: Added additional graph validation for parallel reads and writes with dependencies between nodes states 2026-05-20 17:35:33 -06:00
Dark-Alex-17 4536d00067 docs: created an example graph agent configuration 2026-05-20 16:54:34 -06:00
Dark-Alex-17 98d16d9a56 fix: bug in next_single method and improved outcome handling for LLM node execution 2026-05-20 16:27:25 -06:00
Dark-Alex-17 26de81e84e test: implemented integration tests for the parallel frontier-based graph scheduling 2026-05-20 16:09:07 -06:00
Dark-Alex-17 20c28b55d5 feat: added branch progress tracker for better visualization of parallel graph super-steps 2026-05-20 15:50:38 -06:00
Dark-Alex-17 7d6f1dda26 feat: Removed the jira-helper agent and replaced it with the atlassian role 2026-05-20 15:38:51 -06:00
Dark-Alex-17 9a061944ae feat: created the RenderMode enum to suppress stdout streaming during parallel graph super-steps 2026-05-20 15:32:03 -06:00
Dark-Alex-17 1f50af0974 feat: Full support for map node types 2026-05-20 15:15:58 -06:00
Dark-Alex-17 bdacf9fc78 feat: implemented the frontier-based scheduling for the graph executor with simplified state management (gotta love .clone) 2026-05-20 13:48:55 -06:00
Dark-Alex-17 a9f2a5edc2 feat: validation support for parallel graph execution; restricted map nodes to only run for nodes without next targets and not supporting chained map nodes 2026-05-20 12:50:29 -06:00
Dark-Alex-17 2df8b1a541 fix: inline RAG bug when globbing files by extension without subdirectory globbing 2026-05-20 12:22:21 -06:00
Dark-Alex-17 de055bf8a4 feat: created the staging area for state merges per super-step and created the built-in reducers (and their application) for the state merge phase of a super step 2026-05-20 12:16:14 -06:00
Dark-Alex-17 8fb0eece4b feat: scaffolding work for fan-out nodes for parallel branch execution support and stubbed out Map node types 2026-05-20 11:37:23 -06:00
Dark-Alex-17 ba03c3037d style: applied formatting to the new update feature 2026-05-19 14:44:15 -06:00
Dark-Alex-17 afa0e4af67 feat: Loki can now update itself via .update and --update commands 2026-05-19 14:29:44 -06:00
Dark-Alex-17 5a9a00bc6f build: updated dependencies to the latest versions and removed unused dependencies 2026-05-19 13:03:31 -06:00
Dark-Alex-17 e7bb668ac7 fix: update the estimate_token_length function to use the standard word count method 2026-05-19 12:25:53 -06:00
Dark-Alex-17 04498b96ec fix: removed unnecessary regenerate logic for sessions and use the same logic for all contexts; prevents a panic on empty message list 2026-05-19 11:46:37 -06:00
Dark-Alex-17 eb2843d38a build: upgraded to the most recent version of reqwest 2026-05-19 11:05:40 -06:00
Dark-Alex-17 696ce03ee4 feat: added a .edit command for editing the MCP configuration file 2026-05-18 15:14:22 -06:00
Dark-Alex-17 a3d67bfbf7 feat: Created a new .install command to install bundled assets on-demand 2026-05-18 14:59:02 -06:00
Dark-Alex-17 5bd0766a60 style: Cleaned up all graph agent code 2026-05-18 13:46:52 -06:00
Dark-Alex-17 35e1b14843 fix: error when users try to start a session on a graph agent 2026-05-18 12:55:17 -06:00
Dark-Alex-17 503c9b4699 feat: migrated llm node validation to graph loading time instead of graph runtime 2026-05-18 11:51:47 -06:00
Dark-Alex-17 7a8b09542d feat: ripped out user input timeout scaffolding for approval and input node types; implementation can't be done cleanly 2026-05-18 11:32:34 -06:00
Dark-Alex-17 da5cd21c1c test: added additional test coverage to graph components 2026-05-18 10:08:36 -06:00
Dark-Alex-17 27fcb1fc15 docs: Updated README and created graph.example.yaml spec 2026-05-15 17:37:54 -06:00
Dark-Alex-17 e292c414c5 feat: added additional support for all RAG-configuration fields in RAG nodes 2026-05-15 16:38:52 -06:00
Dark-Alex-17 8a2f18204f feat: initial support for RAG nodes in the graph execution system 2026-05-15 14:11:23 -06:00
Dark-Alex-17 c70ac98223 feat: implemented structured logging for graph execution 2026-05-15 13:17:42 -06:00
Dark-Alex-17 249d1fc881 feat: merged normal agent config and graph agent configs into one file (either/or) 2026-05-15 12:57:08 -06:00
Dark-Alex-17 3f4fd91b3f fix: added on_other field for approval nodes so users can specify an alternative free-text target when none of the options match what they want 2026-05-14 16:35:08 -06:00
Dark-Alex-17 48c52b5829 feat: added structured-output extraction for llm and agent nodes 2026-05-14 15:36:10 -06:00
Dark-Alex-17 f58f751c59 fix: accidentally added back in full agent tools on LLM nodes 2026-05-14 14:39:08 -06:00
Dark-Alex-17 fc7fdc98b4 feat: created full llm node runtime implementation 2026-05-14 14:00:24 -06:00
Dark-Alex-17 f4d7d0fb73 refactor: migrated llm nodes to use Roles to simplify instructions handling and to function like inline roles 2026-05-14 13:24:34 -06:00
Dark-Alex-17 4b38f53488 refactor: migrated the next_node and apply_state_updates logic for LLM nodes into the LlmExecutor 2026-05-14 12:08:55 -06:00
Dark-Alex-17 186422ff58 feat: scaffolded together the initial llm node type and its executor 2026-05-14 11:57:18 -06:00
Dark-Alex-17 9bc4f8b621 feat: wired together graph execution and agent graph dispatch 2026-05-14 11:10:45 -06:00
Dark-Alex-17 84497d3d65 feat: implemented support for the graph executor 2026-05-13 14:29:45 -06:00
Dark-Alex-17 3ea9116a23 feat: created the approval node executor and the input node executor for user interaction 2026-05-13 14:08:44 -06:00
Dark-Alex-17 bfcd73c32a feat: Added initial support for native Loki agent nodes in the graph-based agent system 2026-05-13 13:21:45 -06:00
Dark-Alex-17 3cd3ba55ff feat: Added direct script invocation support for graph-based agents 2026-05-13 12:35:10 -06:00
Dark-Alex-17 3535edba79 feat: Added graph validation 2026-05-13 10:18:51 -06:00
Dark-Alex-17 bf0343e245 feat: Implemented state management for agent graphs 2026-05-13 09:18:38 -06:00
Dark-Alex-17 b001ae4c18 feat: initial agent graph scaffolding 2026-05-12 14:13:03 -06:00
Dark-Alex-17 9ce088a530 fix: Improve the coder agent's usage of tools 2026-05-11 15:03:15 -06:00
Dark-Alex-17 16f3f71188 fix: make the agent__collect escalation-aware so it doesn't freeze on sub-agent escalations 2026-05-11 13:57:02 -06:00
Dark-Alex-17 0af5fa02f9 fmt: Applied uniform formatting across all files 2026-05-08 15:52:12 -06:00
Dark-Alex-17 d6a0676264 docs: Updated example configurations to link to the new Wiki-based documentation 2026-05-08 15:51:11 -06:00
Dark-Alex-17 b582bab17c fix: check for an existing session before starting up MCP servers when switching to a role 2026-05-08 12:28:24 -06:00
Dark-Alex-17 a8732c63d6 fix: do not switch to agent if a session is active. 2026-05-08 12:15:01 -06:00
Dark-Alex-17 389d0b768f fix: Do not append todo instructions when function calling is disabled 2026-05-08 12:06:07 -06:00
Dark-Alex-17 70a251a7e2 feat: add auto-continue support to all contexts 2026-05-08 12:02:10 -06:00
Dark-Alex-17 462f136596 feat: dynamic tab completions now show the sessions for a given agent instead of only listing global sessions 2026-05-07 15:23:50 -06:00
Dark-Alex-17 bf9d7d750e fix: a bug in the dynamic completions because the crate name is loki-ai but the binary is named loki 2026-05-07 14:08:54 -06:00
Alex Clarke 540ec648c9 Merge pull request #11 from Dark-Alex-17/config-refactor
Decompose God-Config struct into focused state architecture with MCP SSE support and comprehensive tests
2026-05-07 13:50:49 -06:00
Dark-Alex-17 e69352ee2d fmt: reapplied formatting for the sse_transport module 2026-05-07 13:47:30 -06:00
Dark-Alex-17 ee4e3bc13f fix: bug found by copilot that would create a lock on the PollSender for sse-based MCP servers 2026-05-07 13:45:19 -06:00
Dark-Alex-17 a576961bd6 test: removed forgotten mem::forget from supervisor tests 2026-05-07 13:03:44 -06:00
Dark-Alex-17 59c7fc1276 style: Addressed style comments left by copilot reviewer 2026-05-07 13:01:26 -06:00
Dark-Alex-17 bcf512fcfc test: Fixed forgotten Windows-specific tests for functions 2026-05-07 12:20:30 -06:00
Dark-Alex-17 195401c496 style: Added import for Arc in macros 2026-05-07 11:45:26 -06:00
Dark-Alex-17 34d8d20ec6 chore: updated models.yaml 2026-05-07 08:35:52 -06:00
Dark-Alex-17 08ba6f0446 docs: Fixed typo in README agent example path 2026-05-06 08:04:54 -06:00
Dark-Alex-17 26984892af docs: Deprecated in-repo docs and migrated them to a Wiki 2026-05-05 15:03:18 -06:00
Dark-Alex-17 526a426073 docs: removed now unnecessary implementation wiki for configuration migration 2026-05-01 14:46:03 -06:00
Dark-Alex-17 c53e0546d4 test: added integration tests for inter-feature interactions like RAG + Agents, function calling/MCP servers, etc. 2026-05-01 14:06:41 -06:00
Dark-Alex-17 349b3748bd test: Added unit tests for the rag, completions and prompt, macros, vault, and functions/tool usage 2026-05-01 13:24:58 -06:00
Dark-Alex-17 e23e5f9f7b test: Added integration tests for the sub-agent spawning system and inter-agent communication mechanisms 2026-05-01 12:53:26 -06:00
Dark-Alex-17 8d02782de6 test: unit tests for the sub agent spawning system 2026-05-01 12:20:00 -06:00
Dark-Alex-17 27ceefdb40 test: REPL command tests and CLI flag tests 2026-05-01 11:57:17 -06:00
Dark-Alex-17 5168eb6781 test: request_context tests 2026-05-01 11:12:30 -06:00
Dark-Alex-17 ddb73a9a33 test: added tests for input 2026-05-01 11:06:35 -06:00
Dark-Alex-17 53eff10d75 test: implemented tests for tool call dispatch and tracking 2026-05-01 10:52:56 -06:00
Dark-Alex-17 1df6114ff3 test: Implemented tests for the MCP server lifecycle 2026-05-01 10:27:49 -06:00
Dark-Alex-17 975484cc2b fix: Accidental shadow of temp_file function for Windows function calling 2026-04-28 08:53:57 -06:00
Dark-Alex-17 0421c9b643 style: Addressed style issues 2026-04-28 08:08:23 -06:00
Dark-Alex-17 fb69c21252 build: updated crossterm version for MacOS 2026-04-23 08:49:26 -06:00
Dark-Alex-17 0cb9122d16 feat: legacy SSE support for MCP server configurations 2026-04-20 14:10:26 -06:00
Dark-Alex-17 c164ad3cbb fix: upgraded to newer rmcp version to get native-tls support 2026-04-20 13:50:34 -06:00
Dark-Alex-17 9b4171a468 feat: support http/sse transport types for MCP server configurations so it fully supports claude desktop-style MCP configs 2026-04-20 13:08:20 -06:00
Dark-Alex-17 5cae4e44fb Merge remote-tracking branch 'gitea/restful-api' into restful-api
# Conflicts:
#	docs/PHASE-1-IMPLEMENTATION-PLAN.md
#	src/cli/completer.rs
#	src/client/common.rs
#	src/config/agent.rs
#	src/config/input.rs
#	src/config/macros.rs
#	src/config/mod.rs
#	src/config/session.rs
#	src/function/mod.rs
#	src/function/supervisor.rs
#	src/function/todo.rs
#	src/function/user_interaction.rs
#	src/main.rs
#	src/mcp/mod.rs
#	src/rag/mod.rs
#	src/repl/mod.rs
2026-04-20 09:02:30 -06:00
Dark-Alex-17 a145a42b2b refactor: fully complete state re-architecting 2026-04-19 19:21:24 -06:00
Dark-Alex-17 715807645a refactor: Fully ripped out the god Config struct 2026-04-19 19:14:25 -06:00
Dark-Alex-17 1259c6865f refactor: Deprecated old Config struct initialization logic 2026-04-19 18:27:33 -06:00
Dark-Alex-17 ff42460cb4 refactor: migrate functions and MCP servers to AppConfig 2026-04-19 18:14:16 -06:00
Dark-Alex-17 39a16f8d56 refactor: Migrate the vault/bare_init logic 2026-04-19 18:00:14 -06:00
Dark-Alex-17 83de60f59c refactor: created a single install_builtins free function to remove from Config::init 2026-04-19 17:54:50 -06:00
Dark-Alex-17 cf60e090a5 refactor: partial migration to init in AppConfig 2026-04-19 17:46:20 -06:00
Dark-Alex-17 0fb37c33ab fix: RagCache was not being used for agent and sub-agent instantiation 2026-04-19 17:39:49 -06:00
Dark-Alex-17 d81508c22a feat: 99% complete migration to new state structs to get away from God-Config struct; i.e. AppConfig, AppState, and RequestContext 2026-04-19 17:05:27 -06:00
Dark-Alex-17 883ac659b2 testing 2026-04-16 10:17:03 -06:00
Dark-Alex-17 c6c10b5e24 Merge branch 'tree-sitter-tools' into 'develop' 2026-04-09 14:48:22 -06:00
Dark-Alex-17 a4e5bef1b7 feat: Automatic runtime customization using shebangs 2026-04-09 14:16:02 -06:00
Dark-Alex-17 f72c7b03f9 test: Updated client stream tests to use the thread_rng from rand 2026-04-09 13:53:52 -06:00
Dark-Alex-17 bd6f709374 build: Pulled additional features for rand dependency 2026-04-09 13:45:08 -06:00
Dark-Alex-17 00f2201157 fix: TypeScript function args were being passed as objects rather than direct parameters 2026-04-09 13:32:16 -06:00
Dark-Alex-17 b3f0d66071 build: upgraded dependencies to latest 2026-04-09 13:28:19 -06:00
Dark-Alex-17 8730d413bc docs: Updated docs to talk about the new TypeScript-based tool support 2026-04-09 13:19:15 -06:00
Dark-Alex-17 79140fda3c feat: Created a demo TypeScript tool and a get_current_weather function in TypeScript 2026-04-09 13:18:41 -06:00
Dark-Alex-17 67e749ea3a feat: Updated the Python demo tool to show all possible parameter types and variations 2026-04-09 13:18:18 -06:00
Dark-Alex-17 7bcfc133ae fix: Added in forgotten wrapper scripts for TypeScript tools 2026-04-09 13:17:53 -06:00
Dark-Alex-17 e3e246607e feat: Added TypeScript tool support using the refactored common ScriptedLanguage trait 2026-04-09 13:17:28 -06:00
Dark-Alex-17 16104cb2c5 refactor: Extracted common Python parser logic into a common.rs module 2026-04-09 13:16:35 -06:00
Dark-Alex-17 224e51c386 refactor: python tools now use tree-sitter queries instead of AST 2026-04-09 10:20:49 -06:00
Dark-Alex-17 b022ca089c fix: don't shadow variables in binary path handling for Windows 2026-04-09 07:53:18 -06:00
Dark-Alex-17 0ebb761c09 build: Upgraded crossterm and reedline dependencies 2026-04-08 14:54:53 -06:00
Dark-Alex-17 c8067828d5 fix: Tool call improvements for Windows systems 2026-04-08 12:49:43 -06:00
github-actions[bot] 30eedd9b8c chore: bump Cargo.toml to 0.3.0 2026-04-02 20:17:47 +00:00
github-actions[bot] d701b45057 bump: version 0.2.0 → 0.3.0 [skip ci] 2026-04-02 20:17:45 +00:00
Dark-Alex-17 722c9c101e feat: Added todo__clear function to the todo system and updated REPL commands to have a .clear todo as well for significant changes in agent direction 2026-04-02 13:13:44 -06:00
Dark-Alex-17 86aa45f0c4 fix: Clarified user text input interaction 2026-03-30 16:27:22 -06:00
Dark-Alex-17 cf45dc4820 fix: recursion bug with similarly named Bash search functions in the explore agent 2026-03-30 13:32:13 -06:00
Dark-Alex-17 db77034431 feat: Added available tools to prompts for sisyphus and code-reviewer agent families 2026-03-30 13:13:30 -06:00
Dark-Alex-17 abdaec11b0 feat: Added available tools to coder prompt 2026-03-30 11:11:43 -06:00
Dark-Alex-17 95fb349656 Merge branch 'main' of github.com:Dark-Alex-17/loki 2026-03-30 10:15:51 -06:00
Dark-Alex-17 d0b6b6c324 fix: updated the error for unauthenticated oauth to include the REPL .authenticated command 2026-03-28 11:57:01 -06:00
Dark-Alex-17 d74c23ccf5 feat: Improved token efficiency when delegating from sisyphus -> coder 2026-03-18 15:07:29 -06:00
Dark-Alex-17 ea1cfda0d6 build: Removed deprecated agent functions from the .shared/utils.sh script 2026-03-18 15:04:14 -06:00
Dark-Alex-17 5623f47f9a fix: Corrected a bug in the coder agent that wasn't outputting a summary of the changes made, so the parent Sisyphus agent has no idea if the agent worked or not 2026-03-17 14:57:07 -06:00
Dark-Alex-17 e4df9ec193 feat: modified sisyphus agents to use the new ddg-search MCP server for web searches instead of built-in model searches 2026-03-17 14:55:33 -06:00
Dark-Alex-17 a6306d6b76 fix: Claude code system prompt injected into claude requests to make them valid once again 2026-03-17 10:44:50 -06:00
Dark-Alex-17 64529ba5cc fix: Do not inject tools when models don't support them; detect this conflict before API calls happen 2026-03-17 09:35:51 -06:00
Dark-Alex-17 cc7f963b89 style: Applied formatting across new inquire files 2026-03-16 12:39:20 -06:00
Dark-Alex-17 0ce86af116 feat: Added support for specifying a custom response to multiple-choice prompts when nothing suits the user's needs 2026-03-16 12:37:47 -06:00
Dark-Alex-17 2cb0ed3f64 feat: Supported theming in the inquire prompts in the REPL 2026-03-16 12:36:20 -06:00
Dark-Alex-17 fb61854f11 build: upgraded to the most recent version of the inquire crate 2026-03-16 12:31:28 -06:00
Dark-Alex-17 53ba3344b1 docs: Fixed a spacing issue in the example agent configuration 2026-03-13 14:19:39 -06:00
Dark-Alex-17 e20c8be8bb docs: Added the file-reviewer agent to the AGENTS docs 2026-03-13 14:07:13 -06:00
Dark-Alex-17 894dcb1d3c docs: Updated the MCP-SERVERS docs to mention the ddg-search MCP server 2026-03-13 13:32:58 -06:00
Dark-Alex-17 9a9e890f8a feat: Added the duckduckgo-search MCP server for searching the web (in addition to the built-in tools for web searches) 2026-03-13 13:29:56 -06:00
Dark-Alex-17 818ea634f0 Merge branch 'main' of github.com:Dark-Alex-17/loki 2026-03-12 15:17:54 -06:00
Dark-Alex-17 780460f8d8 fix: Implemented the path normalization fix for the oracle and explore agents 2026-03-12 13:38:15 -06:00
Dark-Alex-17 e19483a920 chore: Added GPT-5.2 to models.yaml 2026-03-12 13:30:23 -06:00
Dark-Alex-17 aca93f1cae docs: Updated the docs to now explicitly mention Gemini OAuth support 2026-03-12 13:30:10 -06:00
Dark-Alex-17 1371a4aad2 feat: Support for Gemini OAuth 2026-03-12 13:29:47 -06:00
Dark-Alex-17 db4a45c0f6 refactor: Made the oauth module more generic so it can support loopback OAuth (not just manual) 2026-03-12 13:28:09 -06:00
Dark-Alex-17 e95b1e5f82 fix: Updated the atlassian MCP server endpoint to account for future deprecation 2026-03-12 12:49:26 -06:00
Dark-Alex-17 15f4008f4b fix: Fixed a bug in the coder agent that was causing the agent to create absolute paths from the current directory 2026-03-12 12:39:49 -06:00
Dark-Alex-17 f45f81fb45 fix: The REPL .authenticate command works from within sessions, agents, and roles with pre-configured models 2026-03-12 09:08:17 -06:00
Dark-Alex-17 2220fd2542 feat: Support authenticating or refreshing OAuth for supported clients from within the REPL 2026-03-11 13:07:27 -06:00
Dark-Alex-17 564480e165 fix: the updated regex for secrets injection broke MCP server secrets interpolation because the regex greedily matched on new lines, replacing too much content. This fix just ignores commented out lines in YAML files by skipping commented out lines. 2026-03-11 12:55:28 -06:00
Dark-Alex-17 297c63d91a feat: Allow first-runs to select OAuth for supported providers 2026-03-11 12:01:17 -06:00
Dark-Alex-17 26e2cd3f65 fix: Don't try to inject secrets into commented-out lines in the config 2026-03-11 11:11:09 -06:00
Dark-Alex-17 9f899466d4 feat: Support OAuth authentication flows for Claude 2026-03-11 11:10:48 -06:00
Dark-Alex-17 38393ea4cf chore: Added support for Claude 4.6 gen models 2026-03-10 14:55:30 -06:00
Dark-Alex-17 a4f25826e3 fix: Removed top_p parameter from some agents so they can work across model providers 2026-03-10 10:18:38 -06:00
Dark-Alex-17 93484fb33f Merge branch 'main' of github.com:Dark-Alex-17/loki 2026-03-09 14:58:23 -06:00
Dark-Alex-17 c90f003f92 chore: Added the new gemini-3.1-pro-preview model to gemini and vertex models 2026-03-09 14:57:39 -06:00
Dark-Alex-17 24793b9b8d docs: created an authorship policy and PR template that requires disclosure of AI assistance in contributions 2026-02-24 17:46:07 -07:00
Dark-Alex-17 78e772f455 style: Applied formatting to MCP module 2026-02-20 15:28:21 -07:00
Dark-Alex-17 1e0d269aad docs: Updated sisyphus README to always include the execute_command.sh tool 2026-02-20 15:06:57 -07:00
Dark-Alex-17 f6b1d408fc docs: Updated the sisyphus system docs to have a pro-tip of configuring an IDE MCP server to improve performance 2026-02-20 15:01:08 -07:00
Dark-Alex-17 442b318b6c docs: Created README docs for the CodeRabbit-style Code reviewer agents 2026-02-20 15:00:32 -07:00
Dark-Alex-17 a7c97aedb7 feat: Improved MCP server spinup and spindown when switching contexts or settings in the REPL: Modify existing config rather than stopping all servers always and re-initializing if unnecessary 2026-02-20 14:36:34 -07:00
Dark-Alex-17 746f9e7b24 fix: Improved sub-agent stdout and stderr output for users to follow 2026-02-20 13:47:28 -07:00
Dark-Alex-17 0d6c61af5c Update models.yaml with latest OpenRouter data 2026-02-20 12:08:00 -07:00
Dark-Alex-17 673f31c059 Add script to update models.yaml from OpenRouter 2026-02-20 12:07:59 -07:00
Dark-Alex-17 369a4f0a89 fix: Inject agent variables into environment variables for global tool calls when invoked from agents to modify global tool behavior 2026-02-20 11:38:24 -07:00
Dark-Alex-17 8d54eae4d0 feat: Allow the explore agent to run search queries for understanding docs or API specs 2026-02-19 14:29:02 -07:00
Dark-Alex-17 a805d5beab feat: Allow the oracle to perform web searches for deeper research 2026-02-19 14:26:07 -07:00
Dark-Alex-17 dbb2aec8b6 fix: Removed the unnecessary execute_commands tool from the oracle agent 2026-02-19 14:18:16 -07:00
Dark-Alex-17 1a98b76a1f fix: Added auto_confirm to the coder agent so sub-agent spawning doesn't freeze 2026-02-19 14:15:42 -07:00
Dark-Alex-17 51d10ab2b5 feat: Added web search support to the main sisyphus agent to answer user queries 2026-02-19 12:29:07 -07:00
Dark-Alex-17 1aad750395 refactor: Changed the default session name for Sisyphus to temp (to require users to explicitly name sessions they wish to save) 2026-02-19 10:26:52 -07:00
Dark-Alex-17 e0aab6bd02 fix: Fixed a bug in the new supervisor and todo built-ins that was causing errors with OpenAI models 2026-02-18 14:52:57 -07:00
Dark-Alex-17 6cb93132b7 fix: Added condition to sisyphus to always output a summary to clearly indicate completion 2026-02-18 13:57:51 -07:00
Dark-Alex-17 04126b99d6 fix: Updated the sisyphus prompt to explicitly tell it to delegate to the coder agent when it wants to write any code at all except for trivial changes 2026-02-18 13:51:43 -07:00
Dark-Alex-17 0794eb960d fix: Added back in the auto_confirm variable into sisyphus 2026-02-18 13:42:39 -07:00
Dark-Alex-17 d619ad1d48 fix: Removed the now unnecessary is_stale_response that was breaking auto-continuing with parallel agents 2026-02-18 13:36:25 -07:00
Dark-Alex-17 5b147e07b3 style: Applied formatting to the function module 2026-02-18 13:20:18 -07:00
Dark-Alex-17 944ce441d8 build: Upgraded to the most recent version of rmcp 2026-02-18 12:28:52 -07:00
Dark-Alex-17 a7dcb8519b refactor: Updated the sisyphus agent to use the built-in user interaction tools instead of custom bash-based tools 2026-02-18 12:17:35 -07:00
Dark-Alex-17 d912d44fb3 feat: Created a CodeRabbit-style code-reviewer agent 2026-02-18 12:16:59 -07:00
Dark-Alex-17 4f7254a634 docs: Updated the docs to include details on the new agent spawning system and built-in user interaction tools 2026-02-18 12:16:29 -07:00
Dark-Alex-17 bf923cb296 fix: Bypassed enabled_tools for user interaction tools so if function calling is enabled at all, the LLM has access to the user interaction tools when in REPL mode 2026-02-18 11:25:25 -07:00
Dark-Alex-17 d9f737e1bf feat: Added configuration option in agents to indicate the timeout for user input before proceeding (defaults to 5 minutes) 2026-02-18 11:24:47 -07:00
Dark-Alex-17 59690d045e feat: Added support for sub-agents to escalate user interaction requests from any depth to the parent agents for user interactions 2026-02-18 11:06:15 -07:00
Dark-Alex-17 5d95acba53 feat: built-in user interaction tools to remove the need for the list/confirm/etc prompts in prompt tools and to enhance user interactions in Loki 2026-02-18 11:05:43 -07:00
Dark-Alex-17 d46225d2a9 fix: When parallel agents run, only write to stdout from the parent and only display the parent's throbber 2026-02-18 09:59:24 -07:00
Dark-Alex-17 3af30a0e62 refactor: Cleaned up some left-over implementation stubs 2026-02-18 09:13:39 -07:00
Dark-Alex-17 69eca4d96d fix: Forgot to implement support for failing a task and keep all dependents blocked 2026-02-18 09:13:11 -07:00
Dark-Alex-17 7b2e4a83c9 fix: Clean up orphaned sub-agents when the parent agent 2026-02-18 09:12:32 -07:00
Dark-Alex-17 344b80872a fix: Fixed the bash prompt utils so that they correctly show output when being run by a tool invocation 2026-02-17 17:19:42 -07:00
Dark-Alex-17 ddf828ff5f feat: Experimental update to sisyphus to use the new parallel agent spawning system 2026-02-17 16:33:08 -07:00
Dark-Alex-17 4e170b069b fix: Forgot to automatically add the bidirectional communication back up to parent agents from sub-agents (i.e. need to be able to check inbox and send messages) 2026-02-17 16:11:35 -07:00
Dark-Alex-17 22c75fb578 feat: Added an agent configuration property that allows auto-injecting sub-agent spawning instructions (when using the built-in sub-agent spawning system) 2026-02-17 15:49:40 -07:00
Dark-Alex-17 11ab9eb6b8 feat: Auto-dispatch support of sub-agents and support for the teammate pattern between subagents 2026-02-17 15:18:27 -07:00
Dark-Alex-17 29b232f407 docs: Initial documentation cleanup of parallel agent MVP 2026-02-17 14:30:28 -07:00
Dark-Alex-17 53e8c920e5 fix: Agent delegation tools were not being passed into the {{__tools__}} placeholder so agents weren't delegating to subagents 2026-02-17 14:19:22 -07:00
Dark-Alex-17 78d19bed4d feat: Full passive task queue integration for parallelization of subagents 2026-02-17 13:42:53 -07:00
Dark-Alex-17 10f4160635 feat: Implemented initial scaffolding for built-in sub-agent spawning tool call operations 2026-02-17 11:48:31 -07:00
Dark-Alex-17 7622836e8b feat: Initial models for agent parallelization 2026-02-17 11:27:55 -07:00
Dark-Alex-17 4d4713a9fa docs: Fixed typos in the Sisyphus documentation 2026-02-16 14:05:51 -07:00
Dark-Alex-17 25008599f9 feat: Added interactive prompting between the LLM and the user in Sisyphus using the built-in Bash utils scripts 2026-02-16 13:57:04 -07:00
github-actions[bot] c00ab074f8 chore: bump Cargo.toml to 0.2.0 2026-02-14 01:41:41 +00:00
github-actions[bot] aed1f1957f bump: version 0.1.3 → 0.2.0 [skip ci] 2026-02-14 01:41:29 +00:00
Dark-Alex-17 c6a959e2e1 feat: Simplified sisyphus prompt to improve functionality 2026-02-13 18:36:10 -07:00
Dark-Alex-17 02b7ed37f6 feat: Supported the injection of RAG sources into the prompt, not just via the .sources rag command in the REPL so models can directly reference the documents that supported their responses 2026-02-13 17:45:56 -07:00
Dark-Alex-17 0d84aaabb9 docs: updated the tools documentation to mention the new fs_read, fs_grep, and fs_glob tools 2026-02-13 16:53:00 -07:00
Dark-Alex-17 6efdcf9610 docs: updated the default configuration example to have the new fs_read, fs_glob, fs_grep global functions 2026-02-13 16:23:49 -07:00
Dark-Alex-17 4266d317d8 docs: Updated the docs to mention the new agents 2026-02-13 15:42:28 -07:00
Dark-Alex-17 4ce7aafcbd feat: Created the Sisyphus agent to make Loki function like Claude Code, Gemini, Codex, etc. 2026-02-13 15:42:10 -07:00
Dark-Alex-17 35d8b69f92 feat: Created the Oracle agent to handle high-level architectural decisions and design questions about a given codebase 2026-02-13 15:41:44 -07:00
Dark-Alex-17 562057e608 feat: Updated the coder agent to be much more task-focused and to be delegated to by Sisyphus 2026-02-13 15:41:11 -07:00
Dark-Alex-17 b7024e5340 feat: Created the explore agent for exploring codebases to help answer questions 2026-02-13 15:40:46 -07:00
Dark-Alex-17 088588231b docs: Updated todo-system docs 2026-02-13 15:13:37 -07:00
Dark-Alex-17 eff117d3d9 feat: Use the official atlassian MCP server for the jira-helper agent 2026-02-13 14:56:42 -07:00
Dark-Alex-17 968c535709 feat: Created fs_glob to enable more targeted file exploration utilities 2026-02-13 13:31:50 -07:00
Dark-Alex-17 c8b6fa7b11 feat: Created a new tool 'fs_grep' to search a given file's contents for relevant lines to reduce token usage for smaller models 2026-02-13 13:31:20 -07:00
Dark-Alex-17 0aa334b54e feat: Created the new fs_read tool to enable controlled reading of a file 2026-02-13 13:30:53 -07:00
Dark-Alex-17 78a49f841d feat: Let agent level variables be defined to bypass guard protections for tool invocations 2026-02-09 16:45:11 -07:00
Dark-Alex-17 43b2bd937e fix: Improved continuation prompt to not make broad todo-items 2026-02-09 15:36:57 -07:00
Dark-Alex-17 a4326875ba fix: Allow auto-continuation to work in agents after a session is compressed and if there's still unfinish items in the to-do list 2026-02-09 15:21:39 -07:00
Dark-Alex-17 eb31a58346 fix: fs_ls and fs_cat outputs should always redirect to "$LLM_OUTPUT" including on errors. 2026-02-09 14:56:55 -07:00
Dark-Alex-17 a6b0acc35d feat: Implemented a built-in task management system to help smaller LLMs complete larger multistep tasks and minimize context drift 2026-02-09 12:49:06 -07:00
Dark-Alex-17 cc7fcd0b5b feat: Improved tool and MCP invocation error handling by returning stderr to the model when it is available 2026-02-04 12:00:21 -07:00
Dark-Alex-17 02fe59b913 feat: Added variable interpolation for conversation starters in agents 2026-02-04 10:51:59 -07:00
Dark-Alex-17 6fd5f47089 build: Upgraded to the most recent version of gman to fix vault vulnerabilities 2026-02-03 09:24:53 -07:00
Dark-Alex-17 2a2922760e feat: Implemented retry logic for failed tool invocations so the LLM can learn from the result and try again; Also implemented chain loop detection to prevent loops 2026-02-01 17:06:16 -07:00
Dark-Alex-17 a3793460fd fix: Claude tool calls work incorrectly when tool doesn't require any arguments or flags; would provide an empty JSON object or error on no args 2026-02-01 17:05:36 -07:00
Dark-Alex-17 e0927a04d9 feat: Added gemini-3-pro to the supported vertexai models 2026-01-30 19:03:41 -07:00
Dark-Alex-17 8665604bab Fixed some typos in tool call error messages 2026-01-30 12:25:57 -07:00
Dark-Alex-17 d4c3c135b3 build: Created justfile to make life easier 2026-01-27 13:49:36 -07:00
Dark-Alex-17 60bd5e493c docs: Created a CREDITS file to document the history and origins of Loki from the original AIChat project 2026-01-27 13:15:20 -07:00
Dark-Alex-17 0753b2d841 build: Support Claude Opus 4.5 2026-01-26 12:40:06 -07:00
Dark-Alex-17 17e6fbd692 feat: Added an environment variable that lets users bypass guard operations in bash scripts. This is useful for agent routing 2026-01-23 14:18:52 -07:00
Dark-Alex-17 0710441650 fix: Fixed a bug where --agent-variable values were not being passed to the agents 2026-01-23 14:15:59 -07:00
Dark-Alex-17 20a76cee3e feat: Added support for thought-signatures for Gemini 3+ models 2026-01-21 15:11:55 -07:00
Dark-Alex-17 cb64785867 style: Cleaned up an anyhow error 2025-12-16 14:51:35 -07:00
github-actions[bot] e6e26103c4 bump: version 0.1.2 → 0.1.3 [skip ci] 2025-12-13 20:57:37 +00:00
Dark-Alex-17 15529a14f1 ci: Prep for 0.1.3 release 2025-12-13 13:38:09 -07:00
Dark-Alex-17 86839188e0 style: Improved error message for un-fully configured MCP configuration 2025-12-13 13:37:01 -07:00
github-actions[bot] 39701b378b chore: bump Cargo.toml to 0.1.3 2025-12-13 20:28:10 +00:00
github-actions[bot] 45ff6da737 bump: version 0.1.2 → 0.1.3 [skip ci] 2025-12-13 20:27:58 +00:00
Dark-Alex-17 a260dd1503 chore: Updated the models 2025-12-11 09:05:41 -07:00
Dark-Alex-17 57859301df docs: Removed the warning about MCP token usage since that has been fixed 2025-12-05 12:38:15 -07:00
Dark-Alex-17 8c968d3f53 docs: Fixed an unclosed backtick typo in the Environment Variables docs 2025-12-05 12:37:59 -07:00
Dark-Alex-17 0034bfbe46 docs: Fixed typo in vault readme 2025-12-05 11:05:14 -07:00
Dark-Alex-17 a733b9247a style: Applied formatting 2025-12-03 15:06:50 -07:00
Dark-Alex-17 e0afa349b9 Merge branch 'main' of github.com:Dark-Alex-17/loki 2025-12-03 14:57:03 -07:00
Dark-Alex-17 7d0ce94907 feat: Improved MCP implementation to minimize the tokens needed to utilize it so it doesn't quickly overwhelm the token space for a given model 2025-12-03 12:12:51 -07:00
Alex Clarke 9045763c35 ci: Updated the README to be a bit more clear in some sections 2025-11-26 15:53:54 -07:00
github-actions[bot] 29898552d7 bump: version 0.1.1 → 0.1.2 [skip ci] 2025-11-08 23:13:34 +00:00
Dark-Alex-17 9d7c2f5c2f refactor: Gave the GitHub MCP server a default placeholder value that doesn't require the vault 2025-11-08 16:09:32 -07:00
github-actions[bot] 5c0fa42351 bump: version 0.1.1 → 0.1.2 [skip ci] 2025-11-08 23:02:40 +00:00
Dark-Alex-17 ab045b0ef3 bug: Removed the github MCP server and slack MCP server from mcp.json so users can just use Loki without any other setup and add more later 2025-11-08 15:59:05 -07:00
Alex Clarke 41e6843db1 build: Removed the remaining IDE metadata directories 2025-11-07 18:21:58 -07:00
Dark-Alex-17 911ec3c9b9 build: Added forgotten IDE configuration directories into my .gitignore 2025-11-07 18:18:32 -07:00
377 changed files with 104008 additions and 14863 deletions
+1
View File
@@ -0,0 +1 @@
assets/config-template.yaml text eol=lf
+13 -13
View File
@@ -21,25 +21,25 @@ body:
value: |
I tried this:
1. `loki`
1. `coyote`
I expected this to happen:
Instead, this happened:
- type: textarea
id: loki-log
id: coyote-log
attributes:
label: Loki log
description: Include the Loki log file to help diagnose the issue. (`loki --info` to see the log_path)
label: Coyote log
description: Include the Coyote log file to help diagnose the issue. (`coyote --info` to see the log_path)
value: |
| OS | Log file location |
| ------- | ----------------------------------------------------- |
| Linux | `~/.cache/loki/loki.log` |
| Mac | `~/Library/Logs/loki/loki.log` |
| Windows | `C:\Users\<User>\AppData\Local\loki\loki.log` |
| Linux | `~/.cache/coyote/coyote.log` |
| Mac | `~/Library/Logs/coyote/coyote.log` |
| Windows | `C:\Users\<User>\AppData\Local\coyote\coyote.log` |
```
please provide a copy of your loki log file here if possible; you may need to redact some of the lines
please provide a copy of your coyote log file here if possible; you may need to redact some of the lines
```
- type: input
@@ -57,13 +57,13 @@ body:
validations:
required: true
- type: input
id: loki-version
id: coyote-version
attributes:
label: Loki Version
label: Coyote Version
description: >
Loki version (`loki --version` if using a release, `git describe` if building
Coyote version (`coyote --version` if using a release, `git describe` if building
from main).
**Make sure that you are using the [latest loki release](https://github.com/Dark-Alex-17/loki/releases) or a newer main build**
placeholder: "loki 0.1.0"
**Make sure that you are using the [latest coyote release](https://github.com/Dark-Alex-17/coyote/releases) or a newer main build**
placeholder: "coyote 0.1.0"
validations:
required: true
@@ -0,0 +1,11 @@
### AI assistance (if any):
- List tools here and files touched by them
### Authorship & Understanding
- [ ] I wrote or heavily modified this code myself
- [ ] I understand how it works end-to-end
- [ ] I can maintain this code in the future
- [ ] No undisclosed AI-generated code was used
- [ ] If AI assistance was used, it is documented below
+11
View File
@@ -36,6 +36,17 @@ jobs:
- uses: Swatinem/rust-cache@v2
- name: Cache DuckDB Extensions
id: duckdb-extensions
uses: actions/cache@v4
with:
path: ~/.duckdb/extensions
key: duckdb-ext-${{ matrix.os }}-${{ hashFiles('Cargo.lock') }}
- name: Install DuckDB Extensions
if: steps.duckdb-extensions.outputs.cache-hit != 'true'
run: cargo test --all duckdb
- name: Test
run: cargo test --all
+95 -30
View File
@@ -8,9 +8,9 @@ on:
workflow_dispatch:
inputs:
bump_type:
description: "Specify the type of version bump"
description: 'Specify the type of version bump'
required: true
default: "patch"
default: 'patch'
type: choice
options:
- patch
@@ -46,7 +46,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.10"
python-version: '3.10'
- name: Install Commitizen
run: |
@@ -98,9 +98,9 @@ jobs:
# Ignore Act's local artifact dir noise
echo artifacts/ >> .git/info/exclude || true
# Edit the version line right after name="loki"
# Edit the version line right after name="coyote"
sed -E -i '
/^[[:space:]]*name[[:space:]]*=[[:space:]]*"loki"[[:space:]]*$/ {
/^[[:space:]]*name[[:space:]]*=[[:space:]]*"coyote"[[:space:]]*$/ {
n
s|^[[:space:]]*version[[:space:]]*=[[:space:]]*"[^"]*"|version = "'"$VERSION"'"|
}
@@ -108,17 +108,19 @@ jobs:
cargo update || true
sed -i "s|image: 'darkalex17/coyote:v[^']*'|image: 'darkalex17/coyote:v${VERSION}'|" assets/sbx-kit/spec.yaml
# Git config that helps in Act
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git config --global --add safe.directory "$GITHUB_WORKSPACE"
git status --porcelain
git diff --name-only -- Cargo.toml Cargo.lock || true
git diff --name-only -- Cargo.toml Cargo.lock assets/sbx-kit/spec.yaml || true
if ! git diff --quiet -- Cargo.toml Cargo.lock; then
git add -u -- Cargo.toml Cargo.lock
git commit -m "chore: bump Cargo.toml to $VERSION"
if ! git diff --quiet -- Cargo.toml Cargo.lock assets/sbx-kit/spec.yaml; then
git add -u -- Cargo.toml Cargo.lock assets/sbx-kit/spec.yaml
git commit -m "chore: bump Cargo.toml and sandbox image to $VERSION"
else
echo "No changes to commit (already at $VERSION)"
fi
@@ -163,28 +165,31 @@ jobs:
- target: aarch64-unknown-linux-musl
os: ubuntu-latest
use-cross: true
cargo-flags: ""
cargo-flags: ''
- target: aarch64-unknown-linux-gnu
os: ubuntu-24.04-arm
cargo-flags: ''
- target: aarch64-apple-darwin
os: macos-latest
use-cross: true
cargo-flags: ""
cargo-flags: ''
- target: aarch64-pc-windows-msvc
os: windows-latest
use-cross: true
cargo-flags: ""
cargo-flags: ''
- target: x86_64-apple-darwin
os: macos-latest
cargo-flags: ""
cargo-flags: ''
- target: x86_64-pc-windows-msvc
os: windows-latest
cargo-flags: ""
cargo-flags: ''
- target: x86_64-unknown-linux-musl
os: ubuntu-latest
use-cross: true
cargo-flags: ""
cargo-flags: ''
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
cargo-flags: ""
cargo-flags: ''
steps:
- name: Check if actor is repository owner
@@ -251,7 +256,7 @@ jobs:
run: echo "BUILD_CMD=cross" >> $GITHUB_ENV
- name: Install latest LLVM/Clang
if: matrix.os == 'ubuntu-latest'
if: startsWith(matrix.os, 'ubuntu')
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
@@ -278,7 +283,7 @@ jobs:
- name: Verify file
shell: bash
run: |
file target/${{ matrix.target }}/release/loki
file target/${{ matrix.target }}/release/coyote
- name: Test
if: matrix.target != 'aarch64-apple-darwin' && matrix.target != 'aarch64-pc-windows-msvc'
@@ -338,7 +343,7 @@ jobs:
${{ steps.package.outputs.archive }}
${{ steps.package.outputs.sha }}
tag_name: v${{ env.RELEASE_VERSION }}
name: "v${{ env.RELEASE_VERSION }}"
name: 'v${{ env.RELEASE_VERSION }}'
body_path: artifacts/changelog.md
prerelease: false
@@ -382,11 +387,11 @@ jobs:
shell: bash
run: |
# Set environment variables
macos_sha="$(cat ./artifacts/loki-x86_64-apple-darwin.sha256 | awk '{print $1}')"
macos_sha="$(cat ./artifacts/coyote-x86_64-apple-darwin.sha256 | awk '{print $1}')"
echo "MACOS_SHA=$macos_sha" >> $GITHUB_ENV
macos_sha_arm="$(cat ./artifacts/loki-aarch64-apple-darwin.sha256 | awk '{print $1}')"
macos_sha_arm="$(cat ./artifacts/coyote-aarch64-apple-darwin.sha256 | awk '{print $1}')"
echo "MACOS_SHA_ARM=$macos_sha_arm" >> $GITHUB_ENV
linux_sha="$(cat ./artifacts/loki-x86_64-unknown-linux-musl.sha256 | awk '{print $1}')"
linux_sha="$(cat ./artifacts/coyote-x86_64-unknown-linux-musl.sha256 | awk '{print $1}')"
echo "LINUX_SHA=$linux_sha" >> $GITHUB_ENV
release_version="$(cat ./artifacts/release-version)"
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
@@ -402,23 +407,23 @@ jobs:
if: env.ACT != 'true'
run: |
# run packaging script
python "./deployment/homebrew/packager.py" ${{ env.RELEASE_VERSION }} "./deployment/homebrew/loki.rb.template" "./loki.rb" ${{ env.MACOS_SHA }} ${{ env.MACOS_SHA_ARM }} ${{ env.LINUX_SHA }}
python "./deployment/homebrew/packager.py" ${{ env.RELEASE_VERSION }} "./deployment/homebrew/coyote.rb.template" "./coyote.rb" ${{ env.MACOS_SHA }} ${{ env.MACOS_SHA_ARM }} ${{ env.LINUX_SHA }}
- name: Push changes to Homebrew tap
if: env.ACT != 'true'
env:
TOKEN: ${{ secrets.LOKI_GITHUB_TOKEN }}
TOKEN: ${{ secrets.COYOTE_GITHUB_TOKEN }}
run: |
# push to Git
git config --global user.name "Dark-Alex-17"
git config --global user.email "alex.j.tusa@gmail.com"
git clone https://Dark-Alex-17:${{ secrets.LOKI_GITHUB_TOKEN }}@github.com/Dark-Alex-17/homebrew-loki.git
rm homebrew-loki/Formula/loki.rb
cp loki.rb homebrew-loki/Formula
cd homebrew-loki
git clone https://Dark-Alex-17:${{ secrets.COYOTE_GITHUB_TOKEN }}@github.com/Dark-Alex-17/homebrew-coyote.git
rm homebrew-coyote/Formula/coyote.rb
cp coyote.rb homebrew-coyote/Formula
cd homebrew-coyote
git add .
git diff-index --quiet HEAD || git commit -am "Update formula for Loki release ${{ env.RELEASE_VERSION }}"
git push https://$TOKEN@github.com/Dark-Alex-17/homebrew-loki.git
git diff-index --quiet HEAD || git commit -am "Update formula for Coyote release ${{ env.RELEASE_VERSION }}"
git push https://$TOKEN@github.com/Dark-Alex-17/homebrew-coyote.git
publish-crate:
needs: publish-github-release
@@ -456,3 +461,63 @@ jobs:
if: env.ACT != 'true'
with:
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
publish-sandbox-image:
needs: [publish-github-release]
name: Publish Sandbox Docker Image
runs-on: ubuntu-latest
steps:
- name: Check if actor is repository owner
if: ${{ github.actor != github.repository_owner && env.ACT != 'true' }}
run: |
echo "You are not authorized to run this workflow."
exit 1
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Ensure repository is up-to-date
if: env.ACT != 'true'
run: |
git fetch --all
git pull
- name: Get release artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
merge-multiple: true
- name: Set version variable
run: |
version="$(cat artifacts/release-version)"
echo "version=$version" >> $GITHUB_ENV
- name: Validate release environment variables
run: |
echo "Release version: ${{ env.version }}"
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
if: env.ACT != 'true'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Push to Docker Hub
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile
platforms: linux/amd64,linux/arm64
push: ${{ env.ACT != 'true' }}
tags: darkalex17/coyote:latest, darkalex17/coyote:v${{ env.version }}
build-args: COYOTE_VERSION=${{ env.version }}
+6 -1
View File
@@ -2,5 +2,10 @@
/tmp
/.env
!cli/**
.idea/
/coyote.iml
/.idea/
/loki.iml
.coyote/**
.sisyphus/**
.coyote-project.json
.coyote/memory/
-10
View File
@@ -1,10 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Zeppelin ignored files
/ZeppelinRemoteNotebooks/
+774 -2
View File
@@ -1,3 +1,775 @@
## v0.10.0 (2026-08-29)
### BREAKING CHANGE
- invocations using --prompt <text> must switch to
--temp-role <text>; clap rejects the old flag loudly.
### Feat
- Support Linux ARM64 GNU
- Note that duckdb RAG driver is unavailable on MUSL Linux builds when initializing a RAG so users know when using the binary, not just based on docs
- updated coyote installation scripts to detect when gnu is installable
- upgraded to v2 mixin schema
- improved first-time run experience and included a templated configuration file that now has comments like the config.example.yaml so users don't have to go to the repo to see all the knobs
- **TASK-005**: carry the quality bar through sisyphus and architect
- **TASK-004**: reviewer routing — code-reviewer quality-bar resolution, domain linter pass, surface-skill routing, rigor folding; file-reviewer surface-skill whitelist + convention/correctness marker
- **TASK-003**: add surface skills batch B — worker-review, iac-review, migration-review, cicd-review
- **TASK-002**: add surface skills batch A — rest-api-review, cli-review, library-review
- **TASK-001**: add rigor/surfaces declaration layer to planning skills
- **repl**: color .info mcp-server verdicts and complete only running servers
- **cli**: rename mcp_config asset category to mcp-config with snake_case alias
- create a new probe agent to probe code verifications using usage-pattern testing
- Add in the web_search_coyote tool where helpful
- **mcp**: add .info mcp-server, .set mcp_tools, and filtered-server listing to the REPL
- **mcp**: enforce per-server tool allowlists across runtime, jobs, agents, and graph nodes
- **mcp**: add mcp_tools allowlist config surfaces across roles, sessions, agents, graphs, and skills
- **mcp**: add per-server tool allowlist policy module and allowedTools config field
- support mcp.json in the root of bundle repos as well as in the legacy functions directory
- prefer ~/.config/coyote/mcp.json for the user-scope MCP config
- claude and openai native web search via web_search_coyote
- **jobs**: node-local job ownership and capability-gated job__* visibility
- **graph**: support max_concurrent_jobs at the graph level
- **jobs**: allow uncapped collect via full_result
- **jobs**: exempt polling tools from loop tracker and hint on unchanged checks
- **supervisor**: push agent completion notifications to the spawning context
- **jobs**: push background-job completion notifications via per-context queue
- inject background-jobs prompt guidance when jobs are enabled
- add background job runner, job__* handlers, and start gates
- generalize supervisor registry to TaskHandle enum with job scaffolding, kill discipline, and max_concurrent_jobs config
- run an advisory observability pass after implementation in sisyphus
- add an observability-review skill for post-implementation monitoring analysis
- check under- and over-logging in the code review gate
- calibrate logging registers in the code-writing agents
- add a logging-discipline skill for calibrating log output to repo conventions
- **cli**: rename --prompt to --temp-role
- complete --filter and --force on the first .install argument
- **mcp**: bound tool-result passthrough and surface resource audience annotations
- **mcp**: add mcp_prompt meta-tool and harden prompt display rendering
- **repl**: add .prompt command with live staged tab-completion
- **mcp**: add mcp_read meta-tool for resource reads
- **mcp**: gate meta-function emission on advertised server capabilities
- **mcp**: add render.rs content policy (text paging, pattern filter, blob spill)
- **mcp**: extend the server catalog to resources, templates, and prompts
- complete --filter and --force on the first .install argument
- run design interviews as grilling frontier rounds across the planning agents
- add a grilling skill for frontier-round design interviews
- add an on-demand architecture-reviewer agent for deepening scans
- add a codebase-design skill with the deep-module design vocabulary
- add a feedback-loop-first diagnosing-bugs skill to the coding suite
- add a Fowler code-smell baseline to the code-review skill
- flag duplicate helpers in code reviews with a repo-wide DRY check
- add a transactional-integrity review skill to the code review gate
- add an operational-history prior-art lane to the code-reviewer agent
- support name=value macro arguments with variable tab completion
- add --help guides to the .install and .uninstall REPL commands
- expand owner/repo shorthand for --install with a --git-host flag
- remove the deprecated --install-from flag and .install remote form
- rename install flags and unify .install dispatch
- add --uninstall and .uninstall for installed bundles
- add --update-bundle with provenance-aware conflict handling
- add --list-bundles and .list bundles with drift detection
- record bundle provenance when installing from remote repos
- add bundle provenance store
- parse bundle manifests and capture resolved SHAs for remote installs
- created a new comment-discipline skill for the built in sisyphus suite
- created a dedicated git_command tool to tighten tool calling permissions in the git-master skill
- Added a new security review step to the code writing quality gates
- addressed review comments
- add --no-workspace-macros opt-out for workspace macro loading
- execute non-isolated macros on the live REPL context
- surface macros as first-class custom commands in the REPL
- add lazy macro resolver with two-dir discovery and per-macro states
- add enabled_macros config field at global, role, agent, and session levels
- add description and isolated fields to Macro struct
- Dynamically detect RAG embedding model dimension for any given model
- Support auto confirmation for gatekeeper agents
- retry LLM API calls once after 401 by force-refreshing the OAuth token
- identity-aware rejected-token marker for LLM OAuth cache
- typed ApiStatusError carrying HTTP status through LLM client errors
- per-request OAuth token injection with mid-session refresh for HTTP MCP servers
- reason-specific warnings for MCP servers that fail OAuth at startup
- Installed duckdb into the coyote image
- Support managing MCP servers from the CLI directly
- append new built-in rag__query function to RAG contexts to allow further querying by LLMs
- support static file bundling with sbx-mixins
- **rag**: offer the storage driver when an agent initializes its RAG
- simplified the duckdb selection prompt
- **rag**: let several Coyote processes query one duckdb RAG at once
- **rag**: support string and UUID Qdrant point IDs
- **rag**: create the API key secret inline in the attach wizard
- let workflow rag nodes select a RAG driver
- improved wording and heuristic detection for sisyphus suite of agents
- upgraded to sbx kit v2 spec for improved integration
- **rag**: add attach-only Qdrant provider, attach wizard and sandbox wiring
- **rag**: add DuckDB provider behind the RAG driver abstraction
- **rag**: add driver/attached fields, validation floors and force-reingest
- **mcp**: send RFC 8707 resource indicator in OAuth flows
- Added loaded indicators to .list tools/mcp-servers/skills
- hide .recover from tab completions when no session is active
- add a new .recover command for sessions to recover from errors
- integrated the git-ssh-sign kit into the coyote sandbox kit
### Fix
- Harden install scripts: detect libssl3 without ldconfig on PATH, survive noexec tmp dirs, and guard against partial curl|bash execution
- ldconfig on Debian-based distros lives in /usr/sbin
- Prefer GNU linux builds in install scripts for duckdb support, and gate duckdb support on linux hosts that are MUSL until MUSL support is added
- **mcp**: omit declared-but-empty prompt/resource capabilities from .info mcp-server
- **mcp**: annotate declared-but-empty prompt/resource capabilities in .info mcp-server
- also support the .continue edge case for session crashing checkpointing
- checkpoint sessions that crash for easy resuming
- job__check ring buffer also collects file output for LLM_OUTPUT as well as stdout
- **tools**: interactive-shell semantics and stderr capture in execute_command
- **function**: gate test-only declaration appender behind cfg(test)
- **graph**: gate unix-only test imports behind cfg(unix)
- harden job runner lifecycle and whitelist conformance
- **function**: floor tool-output truncation cut to a UTF-8 char boundary
- **supervisor**: make agent__check a pure status probe that never consumes the handle
- **supervisor**: surface finished-but-uncollected tasks in turn-end guardrail
- fix grep "binary" errors when searching UTF-8 files with unicode characters like some of the Coyote source
- **bundles**: harden the install pipeline for cross-platform correctness
- **mcp**: harden the spill path for cross-platform correctness
- **repl**: offer prompts in .list tab completion and rename its listing helpers
- **mcp**: gate unix-only spill permission APIs for windows builds
- harden the bundle lifecycle per code review
- address code review findings on the bundle lifecycle
- reserve category names, confirm fork-name collisions, report secrets on uninstall
- make bundle provenance portable to Windows
- user__ask should have been renamed to user__select in graph agent user interaction invocations
- support tab completions for graph-based agents with variables as well as standard agents
- surface macro parse errors on top-level invocation
- render .list macros as a comfy-table instead of fixed-width columns
- drain crossterm characters in zellij in kitty contexts to prevent DA1 responses from entering prompt
- drain tty input when displaying inquire prompts to prevent unintentional escapes
- breaking iwe MCP server changes with newest version
- improved handling of non service-specific secrets using sbx custom-secrets
- include tool output to LLM_OUTPUT in errors as well as stderr
- prevent tmp-overwriting
- Corrected a rare edge case on how tool files are generated during parallel executions
- cosmetic fix after improved bash tool handling
- Improved subagent escalation handling
- latent parsing bugs in fs_patch and argc
- Prevent infinite hangs in coder agent and implement timeouts for LLM API calls and interactive tools
- allow nested italics inside bold spans in markdown renderer
- properly handle OAuth refreshes
- correct newline removal from fs_write and fs_patch
- detect duplicate tool call IDs client-side before sending to Claude
- **rag**: warn when a duckdb store is empty but files are indexed
- **rag**: keep a local Qdrant off an ambient proxy
- keep loopback and LAN traffic off an ambient proxy
- **rag**: stop routing Qdrant requests through an ambient proxy
- **rag**: treat a zero min_score as no floor on Qdrant searches
- **rag**: address Copilot review findings on the driver abstraction
- **rag**: delete the DuckDB write-ahead log alongside the store
- **sandbox**: discover agent-scoped RAG mixin sidecars
- **rag**: stop the attach wizard from silently accepting an empty collection
- **rag**: fail loudly when a RAG's vault secret is missing
- **rag**: serialize DuckDB extension installs to stop a Windows race
- **rag**: emit an sbx kit v2 mixin and declare RAG credentials to the proxy
- **rag**: install DuckDB vss and fts extensions when they are missing
- don't output thinking blocks for claude-based models
- additional edge case fix for duplicate tool call IDs in anthropic API calls
- removed temperature modifier in librarian agent to mitigate invisible errors
- strip reasoning blocks for structured LLM output in graph agents
- prevent rare duplicate tool call IDs in long running claude prompts
- agents inherit global reasoning effort if unset
### Refactor
- **function**: finish supervisor-to-agent vocabulary migration
- **function**: rename agent-tool symbols out of supervisor vocabulary
- Modified the naming of several generalized supervisor values
- pulled out some imports to clean up the MCP render module a bit
- **mcp**: centralize meta-function prefix predicates and fix list_tools pagination
- Refactored some bundle const locations
- drop the .install remote migration hint
- drop the --install-from tombstone entirely
- render .list agents and .list skills as comfy-tables
- **rag**: discover driver_config secrets by grammar, not field name
- **rag**: drop the hardcoded embedding model hint from attach
- **rag**: interpolate every driver_config value, not just api_key
- **rag**: extract RagProvider trait and add YamlProvider
## v0.8.3 (2026-08-03)
### Fix
- infinite loop bug when attempting to interrupt a prompt exchange right before a session compression
- ctrl-c inside of an auto-continue loop created an infinite loop
## v0.8.2 (2026-07-31)
### Fix
- sbx update doesn't allow undefined fields in sbx spec
## v0.8.1 (2026-07-30)
### Feat
- ctrl-c interrupts ongoing prompt in a session, but lets the user inject more instructions mid-stream
- improved function calling performance by allowing parallel tool calling
- created the architect and gatekeeper agents for dramatically improved coding performance
- Improved readability of session message exchange replays
- apply --agent/--role/--rag/--model flags in --acp-server mode
- add headless profile to sbx-kit spec
- implement ACP user-interaction to request_permission bridge
- implement ACP session/load and session/cancel
- implement ACP session/prompt
- add ACP server skeleton with stdout-purity test
- add --headless flag for unattended operation
### Fix
- ctrl-c interruption doesn't discard session messages when throbber is showing
- improper handling of fd-style globbing for directories in fs_glob
- properly templated architect design doc path in starter commands
- .copy works when sessions are resumed
- ACP session/prompt now drives the full tool-execution loop
- ACP spec conformance — ContentBlock prompt params and protocolVersion type
- restore stdout output for standalone --headless mode
- suppress tool-call display in headless mode; initialize session on session/new
- skip stdin drain and set silent render mode when --acp-server is active
- include graph-agent descriptions in .agent <TAB> completions
### Refactor
- move ACP server dispatch into run() for shared flag setup
## v0.8.0 (2026-07-25)
### Feat
- Dynamically detect if a selected client in the sandbox first run wizard supports oauth
- Add support for the --fresh flag again with host environment configuration injection
- force overwrite global sbx secrets
- add sbx secrets globally
- only create secrets local to a sandbox
- Improved credentials management for docker sandboxes
- renamed --contents arg for fs_write/patch to --content since most models attempt that first and error otherwise
- Used improved theme selections for tool call highlighting colors
- Improved theme-derived syntax highlighting for LLM tool call logging
- Improved coloring of LLM tool invocation outputs to make LLM output more readable and cohesive
- renamed the user__ask to user__select and improved descriptions to improve model usage
- Improved coloring/highlighting of tool calls to make LLM invocation logs easier to read
- long-running session improvements
- Made sisyphus suite of agents all auto-approved for tool usage
- added ast_grep tool to Sisyphus suite agents
- created the adversay agent and adversarial-review skill
- new spawnable_agents field in agents to let users restrict what agents can be spawned by a parent agent
- replay pre-compressed messages as well when resuming sessions for users to see
- created a new builtin function for agents who can spawn other agents to list available agents via agent__list_available
- **render**: hanging-indent line wrapping for lists and blockquotes
- **render**: wire table state machine and finalize hook
- **render**: render markdown tables with comfy-table
- **render**: parse table cells and column alignments
- **render**: detect markdown table rows and separators
- **render**: add comfy-table dependency and table border style
- **render**: activate rich markdown renderer as default
- **render**: rich block-level markdown rendering (headings, quotes, lists, hr)
- **render**: rich inline markdown rendering (bold, italic, code, links)
- **render**: detect markdown block-level line types
- **render**: precompute markdown scope styles for rich rendering
- Created the raw_markdown configuration flag
- added a .fork command to fork a new session from a running conversation
- **oauth**: enable browser-paste PKCE flow for OpenAI-compatible providers
- copy host OAuth tokens into sandbox at launch
- implement OAuth 2.0 Device Authorization Grant (RFC 8628)
- hint that browser 'paste code' pages can be ignored during callback capture
- openai-compatible wizard offers OAuth when provider has bundled oauth defaults
- validate unique client names at config load
- bundle xAI OAuth defaults in models.yaml
- OAuth branch in openai_compatible prepare_* fns
- get_oauth_provider_for_client dispatcher + client_config_info update
- OpenAICompatibleOAuthProvider (config-driven OAuthProvider impl)
- add auth + oauth fields to OpenAICompatibleConfig
- add oauth field to ProviderModels
- add client_credentials support to prepare_oauth_access_token
- add OAuthConfig + OAuthFlow types to oauth.rs
- Added reasoning effort to the right prompt
- Improved support for Anthropic's extended thinking
- Also support GEMINI.md workspace instructions
- Improved workspace instructions support
- also detect .mcp.json configurations at workspace roots
- Created new .tool enable/disable and .mcp enable/disable aliases to make REPL usage more egonomic
- Created a new .list <kind> REPL command to make discoveribility easier in the REPL
- Support claude-style hidden workspace MCP configuration files via .mcp.json
- Allow users to customize the workspace-specific configuration directory name so they can use Coyote with other CLI clients like .claude
- Add reasoning_effort validation for the main configuration file
- Add validation for reasoning_effort settings to prevent users from specifying erroneous values
- Added support for modifying the reasoning effort of reasoning models
- Explicitly Prevent .undo usage in graph agents
- Added an .undo command to the REPL to let users have more control over the conversation
- Improve sandbox startup time by using the prebuilt Coyote image
- Make coyote available as a docker image
- Made fs_patch more flexible for different model preferences of patch formats
- Installed nano into the sandbox so that users can edit config files in the sandbox directly
- Support workspace-local skill definitions and MCP configurations
- fully functional graph-based RAG
- Implemented graph-based RAG
- Added a --dangerously-skip-permissions flag to skip permission prompts for tool invocations
- Remove the temperature hyperparameter from the diagnose role
- Added a new oauth.redirectHost field to make it possible to further extend MCP support
- Updated the REPL mcp auth path to use the prettified error messaging
- Improved error messaging for failed MCP starts because of auth issues
- Added support for specifying the oauth port and client ID in MCP server configs
- Implemented OAuth support for OpenAI models via Codex endpoints
- merge MCP config when installing bundled mcp config
- Implemented durable state for sisyphus
- Installed ast-grep for the explore agent to use for better code exploration
- Created the step-runner graph agent for more deterministic coding workflows to produce even more reliable and higher-quality results
- Improved oracle and sisyphus agents with skill integrations for the new skills
- Created new sisyphus family skills to improve performance
- Created new diagnostic role and skill for use in other contexts
- Added new memory functions for deleting and renaming memory files, as well as new lints for memory expiration dates and staleness of memories to improve the memory system
- Created a new iwe skill and installed the iwe MCP server for utilizing large knowledgebases
- Session-specific, file-backed history in the REPL
- Replay session output when a user re-enters a session so all output can be seen again
- Added confirmation message after MCP Oauth succeeds when invoked from --auth-mcp
- Created the --auth-mcp CLI flag to let users auth with remote MCP servers without needing to be in the REPL
- add OAuth authentication support for remote MCP servers
- Added mixin for sisyphus so the ddg MCP server can search arbitrary domains
- added improved error messaging on MCP server initialization
- prefer musl versions for linux when running --update/.update
### Fix
- fresh wizard openai-compatible support
- config existence check for --fresh sandboxes
- Improve coyote sandbox startup time
- bypass forgotten sandbox mode check for MCP secret interpolation
- properly wrap sub-style changes in markdown rendering
- removed accidental duplicate ast_grep tool in explore agent
- npm and npx need the /usr/local/share/npm-global/lib directory to exist to run properly so I've added it to the dockerfile
- added executable bit to adversary agent tools script
- fetch descriptions from graph agent configs as well when listing agents
- **render**: suppress blank lines above rendered table
- chown the full sandbox cache dir, not just the coyote subdir
- chown the whole coyote cache dir not just the oauth dir in the sandbox
- fix typo in Gemini's generation_config property to use camelCase exclusively
- **oauth**: treat missing expires_in as non-expiring device_code token
- **oauth**: send Accept: application/json in device flow requests
- OAuth callback listener skips speculative/malformed browser connections
- resolve reasoning effort for the prompt for global defaults as well
- Account for default model reasoning_effort when supplying that value for the REPL prompts
- model narration included in history and between tool calls to prevent repetition
- Don't terminate agent loops early for null tool output
- reduce code duplication by reusing the new concrete_tool_names function in .list tools
- Agent tools can only be modified via .tool enable/disable using tools in the allowed whitelist in the agent
- re-render agent sessions when entering agents with either pre-configured agent_session or when entering an agent directly into a session
- Per RFC 9728, enable dynamic discovery of OAuth endpoints in MCP using path-aware discovery
- hot-attach to MCP servers that require auth after running .mcp auth <name>
- Correctly inherit graph-global model for extractor model if none is defined
- no cursor timeout when user scrolls away from ongoing streaming output
- default to the nano or notepad when a configured editor is not found
- When EDITOR, VISUAL, or config.editor is defined, don't verify via which
- Added a loop exit condition for the diagnostics skill
- Added directness clause to the diganose role to improve prompt
- fs tools now output better error handling to guide the model more effectively
- Make fs_read more tolerant of various arg invocation formats.
- todo functions are injected properly to roles when roles have auto_continue: true and the REPL is started directly into the role
- updated the redirect URI for OAuth MCP to use localhost since that's what is whitelisted, not 127.0.0.1
- allow MCP OAuth refresh_token to be absent from initial token exchanges
- Overrode the default JSON content-type for MCP OAuth so its properly application/x-www-form-urlencoded
- typo in mcp file name
- Added uvx wrapper for macos-based sandboxes
### Refactor
- Standardized paths module function names to not use 'path' in the name and to just always be either 'dir' or 'file'
- main.rs resolve_oauth_client uses new dispatcher
- split run_oauth_flow into pkce + client_credentials dispatchers
### Perf
- updated the memory injection warning so it only logs once, rather than after each keystroke
## v0.7.4 (2026-07-02)
### Feat
- Pin specific usql version to sbx kit
- recursively take ownership over the copied in coyote config for the sbx
- explicitly specify the COYOTE_CONFIG_DIR in the sbx kit
- --tail-logs can track log rollovers and incoporates a sleep timer to minimize idle CPU cycles
- Added support for log rolling so log files don't just blow up over time
### Fix
- Added back in --kit specification for the running of the sbx
- sbx isn't copying base files in their respective directories
- Update deprecated sbx kit config
- Properly chown the coyote config recursively and password file in the sbx
## v0.7.3 (2026-06-24)
### Fix
- apply bootstrapping of functions at startup to fix edge case
## v0.7.2 (2026-06-19)
### Fix
- usql version upgrade
## v0.7.1 (2026-06-19)
### Fix
- sbx mixins must be passed in directories, not as files and the files must be named spec.yaml per new sbx version
## v0.7.0 (2026-06-18)
### Feat
- added configurable cache path via the COYOTE_CACHE_PATH environment variable
- added a memory option to .set tab completions
- Added a diagnostic .info tools subcommand to make it easier to see what tools are enabled in all contexts
- Added additional info outputs for enabled skills and sbx directories
- directly execute shell commands from within the REPL
- created mixin kit for built-in functions and MCP servers
- Added sbx mixins for the secrets providers so users can also bootstrap those as well.
- added support for loading sbx mixins that are dynamically discovered in the users workspace and config directory
- Added a --fresh flag to let users create a truly bare bones sandbox without bootstrapping their config
- initial built-in sandboxing support powered by Docker sbx
- Added the ability to auto-bootstrap workspace memory when in git repos
- Added explicit guardrail handling for pending agents
- auto-append memory to memory index and don't necessarily require the LLM to remember to do it after a write
- Added an --init-memory [global|workspace] flag to easily and quickly enable memory
- added memory global configuration settings to the output of --info and .info
- added .set memory REPL commands to control memory injection and applied formatting
- Create the built-in memory management tools
- Append the memory system prompts (readonly or r/w) to the system prompt when applicable
- Created the --no-memory CLI flag to disable memory for this invocation
- Added the memory configuration properties and storage to the main app config, roles, sessions, and agents.
- initial scaffolding of a memory system
### Fix
- rebuild the tool scope after dynamically updating the skills_enabled value in the REPL
- properly resolve Windows-based local vault password file locations and bootstrap them into the sandbox when possible
- auto-translation of user-prefixed Mac and Linux paths for the vault password file when running inside a sandbox
- don't attempt to auto complete .vault list in the REPL; that's the end of the command
- buffer tool stdout as well as stderr so that any tools that error to stdout are captured and included in the response to the model, enabling the model to see what went wrong and to reason about how to fix it.
- auto-bootstrapped memory was accidentally putting the MEMORY.md directly in the repo root rather than .coyote/memory/MEMORY.md
- improved the fs_patch script description and added improved error handling to it.
- added in forgotten require_max_tokens to the fable model
- append memory functions to non-graph based agents on init
- when auto_continue is disabled via the .set auto_continue false command, it should strip the todo functions from the list of functions
- use rawPredict for non-streaming Claude requests
### Refactor
- Migrated the .skills command completion to use StateFlags and updated the help messages
## v0.6.0 (2026-06-05)
### Feat
- added skill hint prompt injection and configuration
- Fallthrough on missing secrets during mcp.json merging
- validate visible_skills field at config load time
- implemented reflexion (sorta) in sisyphus for significant code changes to delegate to the code-reviewer agent
- improved explore agent
- removed conditional fallback of LLM_*_RAW_JSON from built-ins
- updated enabled_skills handling to support both list and comma-separated strings
- added new REPL set commands for toggling skills and changing what skills are enabled
- upgraded to the latest version of mcp-remote
- fs_grep now works with both files and directories
- improved code reviewer agents with skills
- added round trip validation for vault providers to ensure permissions and authentication
- created new first-time run wizard for secrets provider
- vault_password_file or nothing at all is shorthand for just using the local gman provider for secret management
- refactored gman usage to be generic and work with various vault providers and use the SupportedProvider enum directly for configurations
- created initial parity gman generalization for vault provider
- Refactored the sisyhpus agent system to utilize the new skills system to improve performance and reliability
- llm graph nodes support skills
- updated sisyphus and coder tools
- removed potentially confusing tab completions for .skill
- .edit skill <name> support from within the REPL
- Added skills_dir to the info output of Coyote
- Created a few auto built-in skills
- Added support for auto_unload skills during chat
- cleaned up skill implementation
- support multiple skill flags to load multiple skills at CLI startup
- Modified --skill CLI to allow users to specify skills to start the REPL or CLI with.
- added CLI --skill flag for modifying skills easily
- REPL integration with skills
- dynamic loading/unloading of skill tools and MCP servers whenever load_skill/unload_skill are invoked
- created built-in functions for listing, loading, and unloading skills
- implemented the skills policy to track available skills per context
- added remote install and install support for skills
- created the skill registry
- decided to make skills persist to disk like agents and not in-memory like built-in roles
- scaffold skill module
### Fix
- disable skills for specific built-in roles
- redirect stderr into user's /dev/tty for guards
- azure doesn't support underscores in key vault
- accidental regression on enabled_skills being empty = all
- greedy secrets regex caused multiple secrets on one line to fail
- add agent context check to skill visibility validation
- enforced global visible_skills in llm node validation and improved skill loading error handling across the project
- restore agent skill policy on error during effective policy calculation
- apply the same validation for skill filenames on list_skills as happens everywhere else
- the vault's init_bare should try to load the provisioned secret_provider from the config file without also interpolating any of the rest of the configuration file. It should only fail if the user has not yet created a configuration file; i.e. done a first-time run.
- the vault roundtrip test used characters that are unsupported by some major secrets providers
- fixed tool filtering logic for skills and user functions in agents
- privilege leak when unloading skills and leaving tool scope untouched
- When bootstrapping an app config to interpolate secrets, clone the secrets provider configuration as well so config secrets stored in remote vaults can be used properly
- forgot to move back up the vault probe value error to be before the delete
- don't silently fail on skill role composition extraction in llm nodes
- set -euo pipefail for the temp script in execute_command.sh tool
- added forgotten skill name validation to has_skill to prevent side-channel attacks
- use unique values for the secrets round trip verification
- stop interpolating a line if any errors occur
- added path validation for skill names
- effective_policy unconditionally overwrote skill values for role-like structs
- updated execute_command to not mangle heredocs and also added explicit instructions to the coder and sisyphus agents to use fs_write and fs_patch over execute_command when writing files
- llm nodes accidentally skipped skill_registry::effective_role because I was passing an inline role instead
- updated temperature values for all agents and roles
- added back in require_max_tokens for new Claude models
- skill support also requires function calling to be enabled
- non_tty tests break on some TTY terminals
- skill loading on agents
- forgot to bootstrap skills on REPL startup
- remove now deprecated .skill edit command
### Refactor
- removed redundant skill name validation from has_skill function
- support both CSV and list formats for enabled_tools
- Support both CSV and list formats for enabled_mcp_servers
## v0.5.0 (2026-05-27)
### Feat
- rename Loki to Coyote
### Fix
- bash-based user interactions in agents accidentally regressed in graph implementation
- Claude function calling in agent contexts
- Claude code rate limit error per new Claude changes
## v0.4.0 (2026-05-23)
### Feat
- LLM node failures propgate up
- Added .install remote tab completions to the REPL
- feature complete install remote with category selection
- Support to interactively add secrets to Coyote that are missing from MCP configs when merging
- Added MCP config merging support for remote asset installations
- install remote now writes files to disk
- Created basic install_remote functions
- Created a more comprehensive and immediately useful default config for first runs
- Created an example graph-based agent called deep-research
- Improved coder agent that is now a graph-based agent
- Removed indicatif spinners. The UX just won't stop clobbering for parallel graph nodes
- Added agent variables support for graph agents and improved script executor to use the same environment variables as normal agent tool calling for further flexibility
- Improved UX with colored spinners for parallel graph agents and no clobbering outputs for sub-agents
- created new graph-based deep-research agent
- improved UX for parallel graph execution
- added branch progress tracker for better visualization of parallel graph super-steps
- Removed the jira-helper agent and replaced it with the atlassian role
- created the RenderMode enum to suppress stdout streaming during parallel graph super-steps
- Full support for map node types
- implemented the frontier-based scheduling for the graph executor with simplified state management (gotta love .clone)
- validation support for parallel graph execution; restricted map nodes to only run for nodes without next targets and not supporting chained map nodes
- created the staging area for state merges per super-step and created the built-in reducers (and their application) for the state merge phase of a super step
- scaffolding work for fan-out nodes for parallel branch execution support and stubbed out Map node types
- Coyote can now update itself via .update and --update commands
- added a .edit command for editing the MCP configuration file
- Created a new .install command to install bundled assets on-demand
- migrated llm node validation to graph loading time instead of graph runtime
- ripped out user input timeout scaffolding for approval and input node types; implementation can't be done cleanly
- added additional support for all RAG-configuration fields in RAG nodes
- initial support for RAG nodes in the graph execution system
- implemented structured logging for graph execution
- merged normal agent config and graph agent configs into one file (either/or)
- added structured-output extraction for llm and agent nodes
- created full llm node runtime implementation
- scaffolded together the initial llm node type and its executor
- wired together graph execution and agent graph dispatch
- implemented support for the graph executor
- created the approval node executor and the input node executor for user interaction
- Added initial support for native Coyote agent nodes in the graph-based agent system
- Added direct script invocation support for graph-based agents
- Added graph validation
- Implemented state management for agent graphs
- initial agent graph scaffolding
- add auto-continue support to all contexts
- dynamic tab completions now show the sessions for a given agent instead of only listing global sessions
- legacy SSE support for MCP server configurations
- support http/sse transport types for MCP server configurations so it fully supports claude desktop-style MCP configs
- 99% complete migration to new state structs to get away from God-Config struct; i.e. AppConfig, AppState, and RequestContext
- Automatic runtime customization using shebangs
- Created a demo TypeScript tool and a get_current_weather function in TypeScript
- Updated the Python demo tool to show all possible parameter types and variations
- Added TypeScript tool support using the refactored common ScriptedLanguage trait
### Fix
- Generified the functions usage of script detection for an executable bit on unix systems
- merge required claude code system prompt into instructions
- updated argc argument passing in run-tool and run-agent scripts
- Added additional graph validation for parallel reads and writes with dependencies between nodes states
- bug in next_single method and improved outcome handling for LLM node execution
- inline RAG bug when globbing files by extension without subdirectory globbing
- update the estimate_token_length function to use the standard word count method
- removed unnecessary regenerate logic for sessions and use the same logic for all contexts; prevents a panic on empty message list
- error when users try to start a session on a graph agent
- added on_other field for approval nodes so users can specify an alternative free-text target when none of the options match what they want
- accidentally added back in full agent tools on LLM nodes
- Improve the coder agent's usage of tools
- make the agent__collect escalation-aware so it doesn't freeze on sub-agent escalations
- check for an existing session before starting up MCP servers when switching to a role
- do not switch to agent if a session is active.
- Do not append todo instructions when function calling is disabled
- a bug in the dynamic completions because the crate name is coyote-ai but the binary is named coyote
- bug found by copilot that would create a lock on the PollSender for sse-based MCP servers
- Accidental shadow of temp_file function for Windows function calling
- upgraded to newer rmcp version to get native-tls support
- RagCache was not being used for agent and sub-agent instantiation
- TypeScript function args were being passed as objects rather than direct parameters
- Added in forgotten wrapper scripts for TypeScript tools
- don't shadow variables in binary path handling for Windows
- Tool call improvements for Windows systems
### Refactor
- migrated llm nodes to use Roles to simplify instructions handling and to function like inline roles
- migrated the next_node and apply_state_updates logic for LLM nodes into the LlmExecutor
- fully complete state re-architecting
- Fully ripped out the god Config struct
- Deprecated old Config struct initialization logic
- migrate functions and MCP servers to AppConfig
- Migrate the vault/bare_init logic
- created a single install_builtins free function to remove from Config::init
- partial migration to init in AppConfig
- Extracted common Python parser logic into a common.rs module
- python tools now use tree-sitter queries instead of AST
## v0.3.0 (2026-04-02)
### Feat
- Added `todo__clear` function to the todo system and updated REPL commands to have a .clear todo as well for significant changes in agent direction
- Added available tools to prompts for sisyphus and code-reviewer agent families
- Added available tools to coder prompt
- Improved token efficiency when delegating from sisyphus -> coder
- modified sisyphus agents to use the new ddg-search MCP server for web searches instead of built-in model searches
- Added support for specifying a custom response to multiple-choice prompts when nothing suits the user's needs
- Supported theming in the inquire prompts in the REPL
- Added the duckduckgo-search MCP server for searching the web (in addition to the built-in tools for web searches)
- Support for Gemini OAuth
- Support authenticating or refreshing OAuth for supported clients from within the REPL
- Allow first-runs to select OAuth for supported providers
- Support OAuth authentication flows for Claude
- Improved MCP server spinup and spindown when switching contexts or settings in the REPL: Modify existing config rather than stopping all servers always and re-initializing if unnecessary
- Allow the explore agent to run search queries for understanding docs or API specs
- Allow the oracle to perform web searches for deeper research
- Added web search support to the main sisyphus agent to answer user queries
- Created a CodeRabbit-style code-reviewer agent
- Added configuration option in agents to indicate the timeout for user input before proceeding (defaults to 5 minutes)
- Added support for sub-agents to escalate user interaction requests from any depth to the parent agents for user interactions
- built-in user interaction tools to remove the need for the list/confirm/etc prompts in prompt tools and to enhance user interactions in Coyote
- Experimental update to sisyphus to use the new parallel agent spawning system
- Added an agent configuration property that allows auto-injecting sub-agent spawning instructions (when using the built-in sub-agent spawning system)
- Auto-dispatch support of sub-agents and support for the teammate pattern between subagents
- Full passive task queue integration for parallelization of subagents
- Implemented initial scaffolding for built-in sub-agent spawning tool call operations
- Initial models for agent parallelization
- Added interactive prompting between the LLM and the user in Sisyphus using the built-in Bash utils scripts
### Fix
- Clarified user text input interaction
- recursion bug with similarly named Bash search functions in the explore agent
- updated the error for unauthenticated oauth to include the REPL .authenticated command
- Corrected a bug in the coder agent that wasn't outputting a summary of the changes made, so the parent Sisyphus agent has no idea if the agent worked or not
- Claude code system prompt injected into claude requests to make them valid once again
- Do not inject tools when models don't support them; detect this conflict before API calls happen
- The REPL .authenticate command works from within sessions, agents, and roles with pre-configured models
- Implemented the path normalization fix for the oracle and explore agents
- Updated the atlassian MCP server endpoint to account for future deprecation
- Fixed a bug in the coder agent that was causing the agent to create absolute paths from the current directory
- the updated regex for secrets injection broke MCP server secrets interpolation because the regex greedily matched on new lines, replacing too much content. This fix just ignores commented out lines in YAML files by skipping commented out lines.
- Don't try to inject secrets into commented-out lines in the config
- Removed top_p parameter from some agents so they can work across model providers
- Improved sub-agent stdout and stderr output for users to follow
- Inject agent variables into environment variables for global tool calls when invoked from agents to modify global tool behavior
- Removed the unnecessary execute_commands tool from the oracle agent
- Added auto_confirm to the coder agent so sub-agent spawning doesn't freeze
- Fixed a bug in the new supervisor and todo built-ins that was causing errors with OpenAI models
- Added condition to sisyphus to always output a summary to clearly indicate completion
- Updated the sisyphus prompt to explicitly tell it to delegate to the coder agent when it wants to write any code at all except for trivial changes
- Added back in the auto_confirm variable into sisyphus
- Removed the now unnecessary is_stale_response that was breaking auto-continuing with parallel agents
- Bypassed enabled_tools for user interaction tools so if function calling is enabled at all, the LLM has access to the user interaction tools when in REPL mode
- When parallel agents run, only write to stdout from the parent and only display the parent's throbber
- Forgot to implement support for failing a task and keep all dependents blocked
- Clean up orphaned sub-agents when the parent agent
- Fixed the bash prompt utils so that they correctly show output when being run by a tool invocation
- Forgot to automatically add the bidirectional communication back up to parent agents from sub-agents (i.e. need to be able to check inbox and send messages)
- Agent delegation tools were not being passed into the {{__tools__}} placeholder so agents weren't delegating to subagents
### Refactor
- Made the oauth module more generic so it can support loopback OAuth (not just manual)
- Changed the default session name for Sisyphus to temp (to require users to explicitly name sessions they wish to save)
- Updated the sisyphus agent to use the built-in user interaction tools instead of custom bash-based tools
- Cleaned up some left-over implementation stubs
## v0.2.0 (2026-02-14)
### Feat
- Simplified sisyphus prompt to improve functionality
- Supported the injection of RAG sources into the prompt, not just via the `.sources rag` command in the REPL so models can directly reference the documents that supported their responses
- Created the Sisyphus agent to make Coyote function like Claude Code, Gemini, Codex, etc.
- Created the Oracle agent to handle high-level architectural decisions and design questions about a given codebase
- Updated the coder agent to be much more task-focused and to be delegated to by Sisyphus
- Created the explore agent for exploring codebases to help answer questions
- Use the official atlassian MCP server for the jira-helper agent
- Created fs_glob to enable more targeted file exploration utilities
- Created a new tool 'fs_grep' to search a given file's contents for relevant lines to reduce token usage for smaller models
- Created the new fs_read tool to enable controlled reading of a file
- Let agent level variables be defined to bypass guard protections for tool invocations
- Implemented a built-in task management system to help smaller LLMs complete larger multistep tasks and minimize context drift
- Improved tool and MCP invocation error handling by returning stderr to the model when it is available
- Added variable interpolation for conversation starters in agents
- Implemented retry logic for failed tool invocations so the LLM can learn from the result and try again; Also implemented chain loop detection to prevent loops
- Added gemini-3-pro to the supported vertexai models
- Added an environment variable that lets users bypass guard operations in bash scripts. This is useful for agent routing
- Added support for thought-signatures for Gemini 3+ models
### Fix
- Improved continuation prompt to not make broad todo-items
- Allow auto-continuation to work in agents after a session is compressed and if there's still unfinish items in the to-do list
- fs_ls and fs_cat outputs should always redirect to "$LLM_OUTPUT" including on errors.
- Claude tool calls work incorrectly when tool doesn't require any arguments or flags; would provide an empty JSON object or error on no args
- Fixed a bug where --agent-variable values were not being passed to the agents
## v0.1.3 (2025-12-13)
### Feat
- Improved MCP implementation to minimize the tokens needed to utilize it so it doesn't quickly overwhelm the token space for a given model
## v0.1.2 (2025-11-08)
### Refactor
- Gave the GitHub MCP server a default placeholder value that doesn't require the vault
## v0.1.1 (2025-11-08)
## v0.1.0 (2025-11-07)
@@ -21,8 +793,8 @@
- Support for secret injection into the global config file (API keys, for example)
- Improved MCP handling toggle handling
- Secret injection into the MCP configuration
- added REPL support for interacting with the Loki vault
- Integrated gman with Loki to create a vault and added flags to configure the Loki vault
- added REPL support for interacting with the Coyote vault
- Integrated gman with Coyote to create a vault and added flags to configure the Coyote vault
- Added a default session to the jira helper to make interaction more natural
- Created the repo-analyzer role
- Created the coder and sql agents
+11 -3
View File
@@ -2,7 +2,7 @@
Contributors are very welcome! **No contribution is too small and all contributions are valued.**
## Rust
You'll need to have the stable Rust toolchain installed in order to develop Loki.
You'll need to have the stable Rust toolchain installed in order to develop Coyote.
The Rust toolchain (stable) can be installed via rustup using the following command:
@@ -48,7 +48,8 @@ cz commit
1. Clone this repo
2. Run `cargo test` to set up hooks
3. Make changes
4. Run the application using `make run` or `cargo run`
4. Run the application using `just run` or `just run`
- Install `just` (`cargo install just`) if you haven't already to use the [justfile](./justfile) in this project.
5. Commit changes. This will trigger pre-commit hooks that will run format, test and lint. If there are errors or
warnings from Clippy, please fix them.
6. Push your code to a new branch named after the feature/bug/etc. you're adding. This will trigger pre-push hooks that
@@ -75,6 +76,13 @@ Then, you can run workflows locally without having to commit and see if the GitH
act -W .github/workflows/release.yml --input_type bump=minor
```
## Authorship Policy
All code in this repository is written and reviewed by humans. AI-generated code (e.g., Copilot, ChatGPT,
Claude, etc.) is not permitted unless explicitly disclosed and approved.
Submissions must certify that the contributor understands and can maintain the code they submit.
## Questions? Reach out to me!
If you encounter any questions while developing Loki, please don't hesitate to reach out to me at
If you encounter any questions while developing Coyote, please don't hesitate to reach out to me at
alex.j.tusa@gmail.com. I'm happy to help contributors in any way I can, regardless of if they're new or experienced!
+51
View File
@@ -0,0 +1,51 @@
# Credits
## Matt Pocock's Skills
The bundled `diagnosing-bugs`, `codebase-design`, and `grilling` skills, the
`architecture-reviewer` agent, and the code smell baseline in the bundled
`code-review` skill are adapted from
[mattpocock/skills](https://github.com/mattpocock/skills) by Matt Pocock,
licensed under the MIT License. The smell definitions trace back to Martin
Fowler's *Refactoring* (ch. 3); the deep-module vocabulary builds on John
Ousterhout's *A Philosophy of Software Design* and Michael Feathers'
*Working Effectively with Legacy Code*.
## AIChat
Coyote originally started as a fork of the fantastic
[AIChat CLI](https://github.com/sigoden/aichat). The initial goal was simply
to fix a bug in how MCP servers worked with AIChat, allowing different MCP
servers to be specified per agent. Since then, Coyote has evolved far beyond
its original scope and grown into a passion project with a life of its own.
Today, Coyote includes first-class MCP server support (for both local and remote
servers), a built-in vault for interpolating secrets in configuration files,
built-in agents and macros, dynamic tab completions, integrated custom
functions (no external `argc` dependency), improved documentation, and much
more with many more ideas planned for the future.
Coyote is now developed and maintained as an independent project. Full credit
for the original foundation goes to the developers of the wonderful
AIChat project.
This project is not affiliated with or endorsed by the AIChat maintainers.
## AIChat
Coyote originally began as a fork of [AIChat CLI](https://github.com/sigoden/aichat),
created and maintained by the AIChat contributors.
While Coyote has since diverged significantly and is now developed as an
independent project, its early foundation and inspiration came from the
AIChat project.
AIChat is licensed under the MIT License. The MIT license text and its
copyright notice are preserved in the [LICENSE-MIT](./LICENSE-MIT) file.
## Licensing
Coyote as a whole is licensed under the GNU Affero General Public License
v3.0 only (AGPL-3.0-only); see [LICENSE](./LICENSE). Substantial portions
derived from AIChat remain under the MIT License (Copyright (c) sigoden),
preserved in [LICENSE-MIT](./LICENSE-MIT). See [NOTICE](./NOTICE) for the
combined-licensing summary.
Generated
+2627 -1642
View File
File diff suppressed because it is too large Load Diff
+56 -37
View File
@@ -1,27 +1,30 @@
[package]
name = "loki-ai"
version = "0.1.1"
name = "coyote-ai"
version = "0.10.0"
edition = "2024"
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
description = "An all-in-one, batteries included LLM CLI Tool"
description = "The batteries-included runtime for LLMs"
keywords = ["chatgpt", "llm", "cli", "ai", "repl"]
homepage = "https://github.com/Dark-Alex-17/loki"
repository = "https://github.com/Dark-Alex-17/loki"
homepage = "https://github.com/Dark-Alex-17/coyote"
repository = "https://github.com/Dark-Alex-17/coyote"
categories = ["command-line-utilities"]
readme = "README.md"
license = "MIT"
rust-version = "1.89.0"
license = "AGPL-3.0-only"
rust-version = "1.95.0"
exclude = [".github", "CONTRIBUTING.md"]
[dependencies]
anyhow = "1.0.69"
bytes = "1.4.0"
clap = { version = "4.5.40", features = ["cargo", "derive", "wrap_help"] }
comfy-table = { version = "7.1.4", features = ["custom_styling"] }
dirs = "6.0.0"
duckdb = { version = "1.10505.0", features = ["bundled"] }
dunce = "1.0.5"
futures-util = "0.3.29"
inquire = "0.7.0"
inquire = "0.9.4"
is-terminal = "0.4.9"
reedline = "0.40.0"
reedline = "0.47.0"
serde = { version = "1.0.152", features = ["derive"] }
serde_json = { version = "1.0.93", features = ["preserve_order"] }
serde_yaml = "0.9.17"
@@ -33,11 +36,7 @@ tokio = { version = "1.34.0", features = [
"rt-multi-thread",
"full",
] }
tokio-graceful = "0.2.2"
tokio-stream = { version = "0.1.15", default-features = false, features = [
"sync",
] }
crossterm = "0.28.1"
crossterm = "0.29.0"
chrono = "0.4.23"
bincode = { version = "2.0.0", features = [
"serde",
@@ -50,28 +49,30 @@ nu-ansi-term = "0.50.0"
async-trait = "0.1.74"
textwrap = "0.16.0"
ansi_colours = "1.2.2"
reqwest-eventsource = "0.6.0"
eventsource-stream = "0.2.3"
log = "0.4.28"
log4rs = { version = "1.4.0", features = ["file_appender"] }
log4rs = { version = "1.4.0", features = [
"file_appender",
"rolling_file_appender",
"compound_policy",
"fixed_window_roller",
"size_trigger",
] }
shell-words = "1.1.0"
sha2 = "0.10.8"
unicode-width = "0.2.0"
async-recursion = "1.1.1"
http = "1.1.0"
http-body-util = "0.1"
hyper = { version = "1.0", features = ["full"] }
hyper-util = { version = "0.1", features = ["server-auto", "client-legacy"] }
time = { version = "0.3.36", features = ["macros"] }
indexmap = { version = "2.2.6", features = ["serde"] }
hmac = "0.12.1"
aws-smithy-eventstream = "0.60.4"
aws-smithy-types = "=1.4.9"
time = "=0.3.47"
urlencoding = "2.1.3"
unicode-segmentation = "1.11.0"
json-patch = { version = "4.0.0", default-features = false }
bitflags = "2.5.0"
path-absolutize = "3.1.1"
hnsw_rs = "0.3.0"
rayon = "1.10.0"
uuid = { version = "1.9.1", features = ["v4"] }
scraper = { version = "0.23.1", default-features = false, features = [
"deterministic",
@@ -81,6 +82,7 @@ html_to_markdown = "0.1.0"
rust-embed = "8.5.0"
os_info = { version = "3.8.2", default-features = false }
bm25 = { version = "2.0.1", features = ["parallelism"] }
petgraph = { version = "0.7", features = ["serde-1"] }
which = "8.0.0"
fuzzy-matcher = "0.3.7"
terminal-colorsaurus = "0.4.8"
@@ -88,24 +90,36 @@ duct = "1.0.0"
argc = "1.23.0"
strum_macros = "0.27.2"
indoc = "2.0.6"
rmcp = { version = "0.6.1", features = ["client", "transport-child-process"] }
rmcp = { version = "3.1.2", features = [
"client",
"transport-child-process",
"transport-streamable-http-client-reqwest",
"reqwest-native-tls",
] }
num_cpus = "1.17.0"
rustpython-parser = "0.4.0"
rustpython-ast = "0.4.0"
tree-sitter = "0.26.8"
tree-sitter-python = "0.25.0"
tree-sitter-typescript = "0.23"
colored = "3.0.0"
clap_complete = { version = "4.5.58", features = ["unstable-dynamic"] }
gman = "0.2.3"
gman = "0.5.0"
clap_complete_nushell = "4.5.9"
open = "5"
rand = { version = "0.10.0", features = ["default"] }
url = "2.5.8"
self_update = { version = "0.44", default-features = false, features = [
"reqwest",
"rustls",
"archive-tar",
"compression-flate2",
"archive-zip",
"compression-zip-deflate",
] }
qrcode = "0.14"
[dependencies.reqwest]
version = "0.12.0"
features = [
"json",
"multipart",
"socks",
"rustls-tls",
"rustls-tls-native-roots",
]
version = "0.13.3"
features = ["json", "multipart", "stream", "form", "socks", "rustls"]
default-features = false
[dependencies.syntect]
@@ -114,7 +128,7 @@ default-features = false
features = ["parsing", "regex-onig", "plist-load"]
[target.'cfg(target_os = "macos")'.dependencies]
crossterm = { version = "0.28.1", features = ["use-dev-tty"] }
crossterm = { version = "0.29.0", features = ["use-dev-tty"] }
[target.'cfg(target_os = "linux")'.dependencies]
arboard = { version = "3.3.0", default-features = false, features = [
@@ -124,12 +138,17 @@ arboard = { version = "3.3.0", default-features = false, features = [
[target.'cfg(not(any(target_os = "linux", target_os = "android", target_os = "emscripten")))'.dependencies]
arboard = { version = "3.3.0", default-features = false }
[target.'cfg(unix)'.dependencies]
libc = "0.2"
[dev-dependencies]
ctor = "1.0.13"
pretty_assertions = "1.4.0"
rand = "0.9.0"
rmcp = { version = "3.1.2", features = ["server"] }
serial_test = "3"
[[bin]]
name = "loki"
name = "coyote"
path = "src/main.rs"
[profile.release]
+110
View File
@@ -0,0 +1,110 @@
ARG COYOTE_VERSION
FROM docker/sandbox-templates:shell-docker AS build
ARG COYOTE_VERSION
ARG TARGETARCH
ENV PATH="/home/agent/.cargo/bin:/home/agent/.local/bin:${PATH}"
USER root
RUN apt-get update && \
apt-get install -y --no-install-recommends \
jq curl git \
build-essential pkg-config \
cmake \
clang libclang-dev \
musl-tools \
libssl-dev \
pandoc \
bzip2 \
nano && \
rm -rf /var/lib/apt/lists/*
RUN set -euo pipefail; \
USQL_VERSION=0.21.4; \
case "${TARGETARCH}" in \
amd64) USQL_ARCH=amd64 ;; \
arm64) USQL_ARCH=arm64 ;; \
*) echo "Unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \
esac; \
TMPDIR=$(mktemp -d); \
curl -fsSL --retry 3 \
"https://github.com/xo/usql/releases/download/v${USQL_VERSION}/usql_static-${USQL_VERSION}-linux-${USQL_ARCH}.tar.bz2" \
-o "$TMPDIR/usql.tar.bz2"; \
tar -xjf "$TMPDIR/usql.tar.bz2" -C "$TMPDIR"; \
install -m 0755 "$TMPDIR/usql_static" /usr/local/bin/usql; \
rm -rf "$TMPDIR"
RUN set -euo pipefail; \
DUCKDB_VERSION=1.5.5; \
case "${TARGETARCH}" in \
amd64) DUCKDB_ARCH=amd64 ;; \
arm64) DUCKDB_ARCH=arm64 ;; \
*) echo "Unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \
esac; \
TMPDIR=$(mktemp -d); \
curl -fsSL --retry 3 \
"https://github.com/duckdb/duckdb/releases/download/v${DUCKDB_VERSION}/duckdb_cli-linux-${DUCKDB_ARCH}.gz" \
-o "$TMPDIR/duckdb.gz"; \
gunzip "$TMPDIR/duckdb.gz"; \
install -m 0755 "$TMPDIR/duckdb" /usr/local/bin/duckdb; \
rm -rf "$TMPDIR"
USER 1000
RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \
printf '#!/bin/sh\nexec uv tool run "$@"\n' > "$HOME/.local/bin/uvx" && \
chmod +x "$HOME/.local/bin/uvx"
RUN mkdir -p /usr/local/share/npm-global/lib
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
sh -s -- -y --default-toolchain stable --profile minimal && \
. "$HOME/.cargo/env" && \
cargo install --locked iwec && \
cargo install --locked ast-grep
USER root
RUN set -euo pipefail; \
case "${TARGETARCH}" in \
amd64) MUSL_TARGET=x86_64-unknown-linux-musl ;; \
arm64) MUSL_TARGET=aarch64-unknown-linux-musl ;; \
*) echo "Unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \
esac; \
TMPDIR=$(mktemp -d); \
curl -fsSL --retry 3 \
"https://github.com/Dark-Alex-17/coyote/releases/download/v${COYOTE_VERSION}/coyote-${MUSL_TARGET}.tar.gz" \
-o "$TMPDIR/coyote.tar.gz"; \
tar -xzf "$TMPDIR/coyote.tar.gz" -C "$TMPDIR"; \
install -m 0755 "$TMPDIR/coyote" /home/agent/.cargo/bin/coyote; \
chown 1000:1000 /home/agent/.cargo/bin/coyote; \
rm -rf "$TMPDIR"
FROM scratch
ARG COYOTE_VERSION
COPY --from=build / /
ENV PATH="/home/agent/.cargo/bin:/home/agent/.local/bin:/usr/local/share/npm-global/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
NPM_CONFIG_PREFIX="/usr/local/share/npm-global" \
NO_PROXY="localhost,127.0.0.1,::1,172.17.0.0/16" \
no_proxy="localhost,127.0.0.1,::1,172.17.0.0/16" \
BASH_ENV="/etc/sandbox-persistent.sh"
LABEL com.docker.sandboxes="templates" \
com.docker.sandboxes.base="ubuntu:questing" \
com.docker.sandboxes.flavor="shell-docker" \
com.docker.sandboxes.start-docker="true" \
org.opencontainers.image.title="coyote" \
org.opencontainers.image.description="The batteries-included runtime for LLMs: Shell Assistant, CLI & REPL mode, RAG, AI tools & agents, MCP servers, skills, and macros." \
org.opencontainers.image.source="https://github.com/Dark-Alex-17/coyote" \
org.opencontainers.image.version="${COYOTE_VERSION}"
WORKDIR /home/agent/workspace
USER 1000
ENTRYPOINT ["coyote"]
+657 -18
View File
@@ -1,22 +1,661 @@
The MIT License (MIT)
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (c) 2025 sigoden
Copyright (c) 2025 Alexander J. Clarke
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Preamble
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.
+22
View File
@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2025 sigoden
Copyright (c) 2025 Alexander J. Clarke
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+24
View File
@@ -0,0 +1,24 @@
Coyote
Copyright (c) 2025 Alexander J. Clarke
This project as a whole is licensed under the GNU Affero General Public
License, version 3.0 only (AGPL-3.0-only). The full text of that license is
provided in the LICENSE file.
--------------------------------------------------------------------------------
Upstream / third-party notices
--------------------------------------------------------------------------------
Coyote began as a fork of AIChat (https://github.com/sigoden/aichat),
Copyright (c) sigoden, which is distributed under the MIT License. Substantial
portions of Coyote are derived from AIChat and remain available under the terms
of the MIT License. The MIT License text and its required copyright and
permission notices are preserved in the LICENSE-MIT file.
As permitted by the MIT License, these portions have been incorporated into a
larger work that is distributed under the AGPL-3.0-only license. When you
receive Coyote as a combined work, your rights and obligations for the work as
a whole are governed by the AGPL-3.0-only license; the MIT notice is retained
to satisfy the attribution requirements of the MIT-licensed portions.
See CREDITS.md for additional background and attribution.
+199 -115
View File
@@ -1,133 +1,166 @@
# Loki: All-in-one, batteries-included LLM CLI Tool
# Coyote: The batteries-included runtime for LLMs
![Test](https://github.com/Dark-Alex-17/loki/actions/workflows/ci.yaml/badge.svg)
![LOC](https://tokei.rs/b1/github/Dark-Alex-17/loki?category=code)
[![crates.io link](https://img.shields.io/crates/v/loki-ai.svg)](https://crates.io/crates/loki-ai)
![Release](https://img.shields.io/github/v/release/Dark-Alex-17/loki?color=%23c694ff)
![Crate.io downloads](https://img.shields.io/crates/d/loki-ai?label=Crate%20downloads)
[![GitHub Downloads](https://img.shields.io/github/downloads/Dark-Alex-17/loki/total.svg?label=GitHub%20downloads)](https://github.com/Dark-Alex-17/loki/releases)
![Test](https://github.com/Dark-Alex-17/coyote/actions/workflows/ci.yaml/badge.svg)
[![crates.io link](https://img.shields.io/crates/v/coyote-ai.svg)](https://crates.io/crates/coyote-ai)
![Release](https://img.shields.io/github/v/release/Dark-Alex-17/coyote?color=%23c694ff)
![Crate.io downloads](https://img.shields.io/crates/d/coyote-ai?label=Crate%20downloads)
[![GitHub Downloads](https://img.shields.io/github/downloads/Dark-Alex-17/coyote/total.svg?label=GitHub%20downloads)](https://github.com/Dark-Alex-17/coyote/releases)
![Docker pulls](https://img.shields.io/docker/pulls/darkalex17/coyote?label=Docker%20downloads)
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](https://github.com/Dark-Alex-17/coyote/blob/main/LICENSE)
Loki is an all-in-one, batteries-included, LLM CLI tool featuring Shell Assistant, CLI & REPL Mode, RAG, AI Tools &
Agents, and More.
Coyote is an **all-in-one, batteries-included LLM runtime** for building, running, and interacting with AI from your terminal.
It brings together a Shell Assistant, CLI & REPL modes, RAG, tools, agents, MCP, skills, sandboxes, multi-agent workflows, and
more in a single runtime.
It is designed to include a number of useful agents, roles, macros, and more so users can get up and running with Loki
in as little time as possible.
Coyote comes ready to use with built-in agents, roles, macros, and tools, so you can get started without assembling an AI
stack from scratch. When you want to extend it, entire bundles of agents, roles, macros, tools, MCP servers, and other
configurations can be installed directly from any Git repository.
![Agent example](./docs/images/agents/sql.gif)
See [Bundles](https://github.com/Dark-Alex-17/coyote/wiki/Bundles) to learn how to create, install, and share Coyote bundles.
Coming from [AIChat](https://github.com/sigoden/aichat)? Follow the [migration guide](./docs/AICHAT-MIGRATION.md) to get started.
![Agent example](https://raw.githubusercontent.com/wiki/Dark-Alex-17/coyote/images/agents/sql.gif)
Coming from [AIChat](https://github.com/sigoden/aichat)? Follow the [migration guide](https://github.com/Dark-Alex-17/coyote/wiki/AIChat-Migration) to get started.
## Quick Links
* [AIChat Migration Guide](./docs/AICHAT-MIGRATION.md): Coming from AIChat? Follow the migration guide to get started.
* [History](#history): A history of how Loki came to be.
* [Installation](#install): Install Loki
* [Getting Started](#getting-started): Get started with Loki by doing first-run setup steps.
* [REPL](./docs/REPL.md): Interactive Read-Eval-Print Loop for conversational interactions with LLMs and Loki.
* [Custom REPL Prompt](./docs/REPL-PROMPT.md): Customize the REPL prompt to provide useful contextual information.
* [Vault](./docs/VAULT.md): Securely store and manage sensitive information such as API keys and credentials.
* [Shell Integrations](./docs/SHELL-INTEGRATIONS.md): Seamlessly integrate Loki with your shell environment for enhanced command-line assistance.
* [Function Calling](./docs/function-calling/TOOLS.md#Tools): Leverage function calling capabilities to extend Loki's functionality with custom tools
* [Creating Custom Tools](./docs/function-calling/CUSTOM-TOOLS.md): You can create your own custom tools to enhance Loki's capabilities.
* [Create Custom Python Tools](./docs/function-calling/CUSTOM-TOOLS.md#custom-python-based-tools)
* [Create Custom Bash Tools](./docs/function-calling/CUSTOM-BASH-TOOLS.md)
* [Bash Prompt Utilities](./docs/function-calling/BASH-PROMPT-HELPERS.md)
* [First-Class MCP Server Support](./docs/function-calling/MCP-SERVERS.md): Easily connect and interact with MCP servers for advanced functionality.
* [Macros](./docs/MACROS.md): Automate repetitive tasks and workflows with Loki "scripts" (macros).
* [RAG](./docs/RAG.md): Retrieval-Augmented Generation for enhanced information retrieval and generation.
* [Sessions](/docs/SESSIONS.md): Manage and persist conversational contexts and settings across multiple interactions.
* [Roles](./docs/ROLES.md): Customize model behavior for specific tasks or domains.
* [Agents](/docs/AGENTS.md): Leverage AI agents to perform complex tasks and workflows.
* [Environment Variables](./docs/ENVIRONMENT-VARIABLES.md): Override and customize your Loki configuration at runtime with environment variables.
* [Client Configurations](./docs/clients/CLIENTS.md): Configuration instructions for various LLM providers.
* [Patching API Requests](./docs/clients/PATCHES.md): Learn how to patch API requests for advanced customization.
* [Custom Themes](./docs/THEMES.md): Change the look and feel of Loki to your preferences with custom themes.
---
## History
Loki originally started as a fork of the fantastic [AIChat CLI](https://github.com/sigoden/aichat). The purpose was to
simply fix a bug in how MCP servers worked with AIChat so that I could specify different ones for agents. However, it
has since evolved far beyond that and become a passion project with a life of its own!
Loki now has first class MCP server support (with support for local and remote servers alike), a built-in vault for
interpolating secrets in configuration files, built-in agents, built-in macros, dynamic tab completions, integrated
custom functions (no `argc` dependency), improved documentation, and much more with many more plans for the future!
The original kudos goes out to all the developers of the wonderful AIChat project!
---
* [AIChat Migration Guide](https://github.com/Dark-Alex-17/coyote/wiki/AIChat-Migration): Coming from AIChat? Follow the migration guide to get started.
* [Installation](#install): Install Coyote
* [Getting Started](#getting-started): Get started with Coyote by doing first-run setup steps.
* [Bundles](https://github.com/Dark-Alex-17/coyote/wiki/Bundles): Install bundles of agents, roles, skills, macros, tools, and MCP servers from any git repo, and share your own. Bundles are Coyote's equivalent of plugins in other CLI agents.
* [REPL](https://github.com/Dark-Alex-17/coyote/wiki/REPL): Interactive Read-Eval-Print Loop for conversational interactions with LLMs and Coyote.
* [Custom REPL Prompt](https://github.com/Dark-Alex-17/coyote/wiki/REPL-Prompt): Customize the REPL prompt to provide useful contextual information.
* [Vault](https://github.com/Dark-Alex-17/coyote/wiki/Vault): Securely store and manage sensitive information such as API keys and credentials.
* [Sandboxes](https://github.com/Dark-Alex-17/coyote/wiki/Sandboxes): Launch Coyote inside an isolated [Docker Sandbox](https://docs.docker.com/ai/sandboxes/) with one command. Host config and vault credentials are projected in automatically; everything else is delegated to the `sbx` CLI.
* [Shell Integrations](https://github.com/Dark-Alex-17/coyote/wiki/Shell-Integrations): Seamlessly integrate Coyote with your shell environment for enhanced command-line assistance.
* [Function Calling](https://github.com/Dark-Alex-17/coyote/wiki/Tools): Leverage function calling capabilities to extend Coyote's functionality with custom tools
* [Creating Custom Tools](https://github.com/Dark-Alex-17/coyote/wiki/Custom-Tools): You can create your own custom tools to enhance Coyote's capabilities.
* [Create Custom Python Tools](https://github.com/Dark-Alex-17/coyote/wiki/Custom-Tools#custom-python-based-tools)
* [Create Custom TypeScript Tools](https://github.com/Dark-Alex-17/coyote/wiki/Custom-Tools#custom-typescript-based-tools)
* [Create Custom Bash Tools](https://github.com/Dark-Alex-17/coyote/wiki/Custom-Bash-Tools)
* [Bash Prompt Utilities](https://github.com/Dark-Alex-17/coyote/wiki/Bash-Prompt-Helpers)
* [First-Class MCP Server Support](https://github.com/Dark-Alex-17/coyote/wiki/MCP-Servers): Easily connect and interact with MCP servers for advanced functionality. Coyote supports all three MCP capabilities: tools, resources, and prompts.
* Models interact with each server through a compact set of capability-gated meta-tools: `mcp_search`/`mcp_describe` for discovery across tools, resources, and prompts, `mcp_invoke` for tool calls, `mcp_read` for paged and regex-filterable resource reads, and `mcp_prompt` for server-defined prompts. Binary content is spilled to disk instead of inlined, and oversized tool results are bounded before they reach the model.
* Invoke server prompts yourself with `.prompt <server> <name> [key=value ...]` in the REPL, with live staged tab-completion (servers, then prompt names, then `key=` arguments), and discover them with `.list prompts`.
* [Macros](https://github.com/Dark-Alex-17/coyote/wiki/Macros): Automate repetitive tasks and workflows with Coyote "scripts" (macros). Macros are Coyote's custom commands: invoke any macro directly by name (e.g. `.review main`), with tab-completion, right alongside the built-in REPL commands.
* Give a macro a `description` (shown in `.list macros` and completions) and set `isolated: false` to run its steps on the live session, exactly as if you typed them. Note that non-isolated steps are recorded in the session, and mutating steps (`.role`, `.model`, ...) persist after the macro ends, by design. Steps are fail-fast: an error aborts the remaining steps, but completed steps' effects remain. A non-isolated macro step cannot invoke another macro, and a `.exit` step never exits the REPL.
* Commit project-specific macros to `.coyote/macros/` in your repo. They shadow same-named global macros (opt out with `--no-workspace-macros`).
* Pass variables positionally or by name: leading `name=value` args set declared variables directly (letting earlier variables keep their defaults), and remaining args fill the rest in order. Tab completion after a macro name lists each variable with its description and default.
* Scope which macros are invocable with `enabled_macros` in the global config, a role, an agent, or a session (most specific wins; an empty list disables all macros), and toggle at runtime with `.macro enable|disable <name>`.
* [RAG](https://github.com/Dark-Alex-17/coyote/wiki/RAG): Retrieval-Augmented Generation for enhanced information retrieval and generation.
* [Sessions](https://github.com/Dark-Alex-17/coyote/wiki/Sessions): Manage and persist conversational contexts and settings across multiple interactions.
* [Memory](https://github.com/Dark-Alex-17/coyote/wiki/Memory): Persistent file-based memory that survives across sessions. Bootstrap with `coyote --init-memory [global|workspace]`.
* [Workspace Instructions](https://github.com/Dark-Alex-17/coyote/wiki/Workspace-Instructions): Human-curated project instructions (`COYOTE.md`) injected into every prompt, with `AGENTS.md`/`CLAUDE.md`/`GEMINI.md` fallbacks for cross-tool compatibility. Scaffold with `coyote --init-instructions`.
* [Roles](https://github.com/Dark-Alex-17/coyote/wiki/Roles): Customize model behavior for specific tasks or domains.
* [Skills](https://github.com/Dark-Alex-17/coyote/wiki/Skills): Modular knowledge or capability packs the LLM can load and unload mid-conversation. Multiple skills compose; instructions stack, tools and MCPs union.
* [Agents](https://github.com/Dark-Alex-17/coyote/wiki/Agents): Leverage AI agents to perform complex tasks and workflows, including sub-agent spawning, teammate messaging, and user interaction tools.
* [Graph Agents](https://github.com/Dark-Alex-17/coyote/wiki/Graph-Agents): Define an agent as a declarative, YAML-driven workflow. A directed graph of typed nodes (LLM calls, scripts, approvals, user input, RAG retrieval, sub-agent spawns).
* [Background Jobs](https://github.com/Dark-Alex-17/coyote/wiki/Background-Jobs): Run long tool calls (builds, test suites, slow MCP calls) in the background with the `job__*` tools while the model keeps working, and completion arrives as a push notification.
* [Todo System](https://github.com/Dark-Alex-17/coyote/wiki/TODO-System): Built-in task tracking for improved LLM reliability with smaller models.
* [Environment Variables](https://github.com/Dark-Alex-17/coyote/wiki/Environment-Variables): Override and customize your Coyote configuration at runtime with environment variables.
* [Client Configurations](https://github.com/Dark-Alex-17/coyote/wiki/Clients): Configuration instructions for various LLM providers.
* [Authentication (API Key & OAuth)](https://github.com/Dark-Alex-17/coyote/wiki/Clients#authentication): Authenticate with API keys or OAuth for subscription-based access.
* [Patching API Requests](https://github.com/Dark-Alex-17/coyote/wiki/Patches): Learn how to patch API requests for advanced customization.
* [Custom Themes](https://github.com/Dark-Alex-17/coyote/wiki/Themes): Change the look and feel of Coyote to your preferences with custom themes.
* [History](#history): A history of how Coyote came to be.
## Prerequisites
Loki requires the following tools to be installed on your system:
Coyote requires the following tools to be installed on your system:
* [jq](https://github.com/jqlang/jq)
* `brew install jq`
* [jira (optional)](https://github.com/ankitpokhrel/jira-cli/wiki/Installation) (For the `jira-helper` agent)
* `brew tap ankitpokhrel/jira-cli && brew install jira-cli`
* You'll need to [create a JIRA API token](https://id.atlassian.com/manage-profile/security/api-tokens) for authentication
* Then, save it as an environment variable to your shell profile:
```sh
# ~/.bashrc or ~/.zshrc
export JIRA_API_TOKEN="your_jira_api_token_here"
```
* Then run `jira init`, select installation type as `cloud`, and provide the required details to generate a config
file for the Jira CLI.
* [usql](https://github.com/xo/usql) (For the `sql` agent)
* `brew install xo/xo/usql`
* [docker](https://docs.docker.com/engine/install/)
* [uv](https://docs.astral.sh/uv/getting-started/installation/)
* `curl -LsSf https://astral.sh/uv/install.sh | sh`
* [iwe](https://github.com/iwe-org/iwe) (`iwec`, for the built-in `iwe` MCP server that navigates large markdown knowledgebases)
* **Homebrew:** `brew tap iwe-org/iwe && brew trust --formula iwe-org/iwe/iwe && brew install iwe`
* **Cargo:** `cargo install iwec`
* [ast-grep](https://ast-grep.github.io/) (for the built-in `ast_grep` structural code search tool, used by the `explore` agent)
* **Homebrew:** `brew install ast-grep`
* **Cargo:** `cargo install ast-grep --locked`
* **npm:** `npm i -g @ast-grep/cli`
* Optional: if `ast-grep` is not installed, the `ast_grep` tool reports it and agents fall back to `fs_grep`
* [duckdb](https://duckdb.org/) (for fast, local RAGs)
* `curl https://install.duckdb.org | bash`
These tools are used to provide various functionalities within Loki, such as document processing, JSON manipulation,
interaction with Jira, and they are used within agents and tools.
These tools are used to provide various functionalities within Coyote, such as document processing, JSON manipulation,
etc., and they are used within agents and tools.
## Install
### Cargo
If you have Cargo installed, then you can install `loki` from Crates.io:
If you have Cargo installed, then you can install `coyote` from Crates.io:
```shell
cargo install loki-ai # Binary name is `loki`
cargo install coyote-ai # Binary name is `coyote`
# If you encounter issues installing, try installing with '--locked'
cargo install --locked loki-ai
cargo install --locked coyote-ai
```
### Homebrew (Mac/Linux)
To install Loki from Homebrew, install the `loki` tap. Then you'll be able to install `loki`:
To install Coyote from Homebrew, install the `coyote` tap. Then you'll be able to install `coyote`:
```shell
brew tap Dark-Alex-17/loki
brew install loki
brew tap Dark-Alex-17/coyote
brew install coyote
# If you need to be more specific, use:
brew install Dark-Alex-17/loki/loki
brew install Dark-Alex-17/coyote/coyote
```
To upgrade `loki` using Homebrew:
To upgrade `coyote` using Homebrew:
```shell
brew upgrade loki
brew upgrade coyote
```
### Docker
Coyote is available as a Docker image on Docker Hub (`darkalex17/coyote`) for Linux amd64 and arm64.
Useful for CI, ephemeral environments, or anywhere you prefer not to install it natively.
```bash
docker pull darkalex17/coyote
docker run --rm -it darkalex17/coyote
```
To persist your configuration across container runs, mount your existing config directory:
```bash
docker run --rm -it \
-v ~/.config/coyote:/home/agent/.config/coyote \
darkalex17/coyote
```
If you use the local vault provider and want your vault credentials available in the container, also mount the password file:
```bash
docker run --rm -it \
-v ~/.config/coyote:/home/agent/.config/coyote \
-v ~/.coyote_password:/home/agent/.coyote_password:ro \
darkalex17/coyote
```
### Scripts
#### Linux/MacOS (`bash`)
You can use the following command to run a bash script that downloads and installs the latest version of `loki` for your
You can use the following command to run a bash script that downloads and installs the latest version of `coyote` for your
OS (Linux/MacOS) and architecture (x86_64/arm64):
```shell
curl -fsSL https://raw.githubusercontent.com/Dark-Alex-17/loki/main/install_loki.sh | bash
curl -fsSL https://raw.githubusercontent.com/Dark-Alex-17/coyote/refs/heads/main/scripts/install_coyote.sh | bash
```
#### Windows/Linux/MacOS (`PowerShell`)
You can use the following command to run a PowerShell script that downloads and installs the latest version of `loki`
You can use the following command to run a PowerShell script that downloads and installs the latest version of `coyote`
for your OS (Windows/Linux/MacOS) and architecture (x86_64/arm64):
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -Command "iwr -useb https://raw.githubusercontent.com/Dark-Alex-17/loki/main/scripts/install_loki.ps1 | iex"
powershell -NoProfile -ExecutionPolicy Bypass -Command "iwr -useb https://raw.githubusercontent.com/Dark-Alex-17/coyote/refs/heads/main/scripts/install_coyote.ps1 | iex"
```
### Manual
Binaries are available on the [releases](https://github.com/Dark-Alex-17/loki/releases) page for the following platforms:
Binaries are available on the [releases](https://github.com/Dark-Alex-17/coyote/releases) page for the following platforms:
| Platform | Architecture(s) |
|----------------|-----------------|
@@ -138,75 +171,103 @@ Binaries are available on the [releases](https://github.com/Dark-Alex-17/loki/re
#### Windows Instructions
To use a binary from the releases page on Windows, do the following:
1. Download the latest [binary](https://github.com/Dark-Alex-17/loki/releases) for your OS.
1. Download the latest [binary](https://github.com/Dark-Alex-17/coyote/releases) for your OS.
2. Use 7-Zip or TarTool to unpack the Tar file.
3. Run the executable `loki.exe`!
3. Run the executable `coyote.exe`!
#### Linux/MacOS Instructions
To use a binary from the releases page on Linux/MacOS, do the following:
1. Download the latest [binary](https://github.com/Dark-Alex-17/loki/releases) for your OS.
1. Download the latest [binary](https://github.com/Dark-Alex-17/coyote/releases) for your OS.
2. `cd` to the directory where you downloaded the binary.
3. Extract the binary with `tar -C /usr/local/bin -xzf loki-<arch>.tar.gz` (Note: This may require `sudo`)
4. Now you can run `loki`!
3. Extract the binary with `tar -C /usr/local/bin -xzf coyote-<arch>.tar.gz` (Note: This may require `sudo`)
4. Now you can run `coyote`!
## Updating
Coyote can update itself in place to the latest GitHub release. Run `coyote --update`
for the newest release, or `coyote --update v0.4.0` for a specific version:
```shell
coyote --update
coyote --update v0.4.0
```
The same is available from within the REPL via `.update` and `.update v0.4.0`.
If Coyote was installed with a package manager, prefer that package manager so its
records stay in sync with the binary on disk; i.e. `brew upgrade coyote` for Homebrew,
or `cargo install --locked coyote-ai` for Cargo.
When Coyote detects a package-manager install it prints a warning and asks for
confirmation. In a non-interactive shell (no TTY), pass `--force` to update
anyway:
```shell
coyote --update --force
```
## Getting Started
After installation, you can generate the configuration files and directories by simply running:
```sh
loki --info
coyote --info
```
Then, you need to set up the Loki vault by creating a vault password file. Loki will do this for you automatically and
Then, you need to set up the Coyote vault by creating a vault password file. Coyote will do this for you automatically and
guide you through the process when you first attempt to access the vault. So, to get started, you can run:
```sh
loki --list-secrets
coyote --list-secrets
```
### First Time Setup
In order for Loki to function correctly, you'll need to add a few secrets to the Loki vault so the MCP servers can
function.
### Authentication
Each client in your configuration needs authentication (with a few exceptions; e.g. ollama). Most clients use an API key
(set via `api_key` in the config or through the [vault](https://github.com/Dark-Alex-17/coyote/wiki/Vault)). For providers that support OAuth (e.g. Claude Pro/Max
subscribers, Google Gemini), you can authenticate with your existing subscription instead:
**GitHub MCP Server:**
* `GITHUB_PERSONAL_ACCESS_TOKEN` - A GitHub Personal Access Token with `repo` and `workflow` scopes.
See [Creating a GitHub Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)
```yaml
# In your config.yaml
clients:
- type: claude
name: my-claude-oauth
auth: oauth # Indicate you want to authenticate with OAuth instead of an API key
```
#### Add the secrets to the Loki vault
You can add the secrets to the Loki vault using the following commands (First time use will prompt you to create a vault
password file):
```sh
loki --add-secret GITHUB_PERSONAL_ACCESS_TOKEN
coyote --authenticate my-claude-oauth
# Or via the REPL: .authenticate
```
For full details, see the [authentication documentation](https://github.com/Dark-Alex-17/coyote/wiki/Clients#authentication).
### Tab-Completions
You can also enable tab completions to make using Loki easier. To do so, add the following to your shell profile:
You can also enable tab completions to make using Coyote easier. To do so, add the following to your shell profile:
```shell
# Bash
# (add to: `~/.bashrc`)
source <(COMPLETE=bash loki)
source <(COMPLETE=bash coyote)
# Zsh
# (add to: `~/.zshrc`)
source <(COMPLETE=zsh loki)
source <(COMPLETE=zsh coyote)
# Fish
# (add to: `~/.config/fish/config.fish`)
source <(COMPLETE=fish loki | psub)
source <(COMPLETE=fish coyote | psub)
# Elvish
# (add to: `~/.elvish/rc.elv`)
eval (E:COMPLETE=elvish loki | slurp)
eval (E:COMPLETE=elvish coyote | slurp)
# PowerShell
# (add to: `$PROFILE`)
$env:COMPLETE = "powershell"
loki | Out-String | Invoke-Expression
coyote | Out-String | Invoke-Expression
```
### Shell Integration
You can integrate Loki's Shell Assistant into your shell for enhanced command-line assistance. Add the code in the
corresponding [shell integration script](./scripts/shell-integration) to your shell. Then, you can invoke Loki to convert natural language to
You can integrate Coyote's Shell Assistant into your shell for enhanced command-line assistance. Add the code in the
corresponding [shell integration script](https://github.com/Dark-Alex-17/coyote/tree/main/scripts/shell-integration) to your shell. Then, you can invoke Coyote to convert natural language to
shell commands by pressing `Alt-e`. For example:
```shell
@@ -216,18 +277,18 @@ find . -name "*.md"
```
## Configuration
The location of the global Loki configuration varies between systems, so you can use the following command to find your
The location of the global Coyote configuration varies between systems, so you can use the following command to find your
`config.yaml` file:
```shell
loki --info | grep 'config_file' | awk '{print $2}'
coyote --info | grep 'config_file' | awk '{print $2}'
```
The configuration file consists of a number of settings. To see a full example configuration file with every setting
defined, refer to the [example configuration file](./config.example.yaml).
defined, refer to the [example configuration file](https://github.com/Dark-Alex-17/coyote/blob/main/config.example.yaml).
### Default LLM
The following settings are available to configure the default LLM that is used when you start Loki, and its
The following settings are available to configure the default LLM that is used when you start Coyote, and its
hyperparameters:
| Setting | Description |
@@ -237,34 +298,34 @@ hyperparameters:
| `top_p` | The default `top_p` hyperparameter value to use for all models, with a range of (0,1) (or (0,2) for some models); <br>Used unless explicitly overridden |
### CLI Behavior
You can use the following settings to modify the behavior of Loki:
You can use the following settings to modify the behavior of Coyote:
| Setting | Default Value | Description |
|---------------|---------------|-------------------------------------------------------------------------------------------------------------------------------------|
| `stream` | `true` | Controls whether to use stream-style APIs when querying for completions from LLM providers |
| `save` | `true` | Controls whether to save each query/response to every model to `messages.md` for posterity; Useful for debugging |
| `keybindings` | `emacs` | Specifies which keybinding schema to use; can either be `emacs` or `vi` |
| `editor` | `null` | What text editor Loki should use to edit the input buffer or session (e.g. `vim`, `emacs`, `nano`, `hx`); <br>Defaults to `$EDITOR` |
| `editor` | `null` | What text editor Coyote should use to edit the input buffer or session (e.g. `vim`, `emacs`, `nano`, `hx`); <br>Defaults to `$EDITOR` |
| `wrap` | `no` | Controls whether text is wrapped (can be `no`, `auto`, or some `<max_width>` |
| `wrap_code` | `false` | Enables or disables the wrapping of code blocks |
### Preludes
Preludes let you define the default behavior for the different operating modes of Loki. The available settings are
Preludes let you define the default behavior for the different operating modes of Coyote. The available settings are
shown below:
| Setting | Description |
|-----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `repl_prelude` | This setting lets you specify a default `session` or `role` to use when starting Loki in [REPL](./docs/REPL.md) mode. <br>Values can be <ul><li>`role:<name>` to define a role</li><li>`session:<name>` to define a session</li><li>`<session>:<role>` to define both a session and a role to use</li></ul> |
| `cmd_prelude` | This setting lets you specify a default `session` or `role` to use when running one-off queries in Loki via the CLI. <br>Values can be <ul><li>`role:<name>` to define a role</li><li>`session:<name>` to define a session</li><li>`<session>:<role>` to define both a session and a role to use</li></ul> |
| `repl_prelude` | This setting lets you specify a default `session` or `role` to use when starting Coyote in [REPL](https://github.com/Dark-Alex-17/coyote/wiki/REPL) mode. <br>Values can be <ul><li>`role:<name>` to define a role</li><li>`session:<name>` to define a session</li><li>`<session>:<role>` to define both a session and a role to use</li></ul> |
| `cmd_prelude` | This setting lets you specify a default `session` or `role` to use when running one-off queries in Coyote via the CLI. <br>Values can be <ul><li>`role:<name>` to define a role</li><li>`session:<name>` to define a session</li><li>`<session>:<role>` to define both a session and a role to use</li></ul> |
| `agent_session` | This setting is used to specify a default session that all agents should start into, unless otherwise specified in the agent configuration. (e.g. `temp`, `default`) |
### Appearance
The appearance of Loki can be modified using the following settings:
The appearance of Coyote can be modified using the following settings:
| Setting | Default Value | Description |
|---------------|---------------|------------------------------------------------------|
| `highlight` | `true` | This setting enables or disables syntax highlighting |
| `light_theme` | `false` | This setting toggles light mode in Loki |
| `light_theme` | `false` | This setting toggles light mode in Coyote |
### Miscellaneous Settings
| Setting | Default Value | Description |
@@ -272,5 +333,28 @@ The appearance of Loki can be modified using the following settings:
| `user_agent` | `null` | The name of the `User-Agent` that should be passed in the `User-Agent` header on all requests to model providers |
| `save_shell_history` | `true` | Enables or disables REPL command history |
---
## History
Coyote began as a fork of [AIChat CLI](https://github.com/sigoden/aichat) and has since evolved into an independent project.
See [CREDITS.md](https://github.com/Dark-Alex-17/coyote/blob/main/CREDITS.md) for full attribution and background.
---
## Creator
* [Alex Clarke](https://github.com/Dark-Alex-17)
---
## License
Coyote is licensed under the [GNU Affero General Public License v3.0](https://github.com/Dark-Alex-17/coyote/blob/main/LICENSE)
(AGPL-3.0-only).
Coyote began as a fork of [AIChat](https://github.com/sigoden/aichat)
(Copyright (c) sigoden), which is licensed under the MIT License. Substantial
portions of Coyote are derived from AIChat and remain available under the MIT
License, preserved in [LICENSE-MIT](https://github.com/Dark-Alex-17/coyote/blob/main/LICENSE-MIT). See [NOTICE](https://github.com/Dark-Alex-17/coyote/blob/main/NOTICE) and
[CREDITS.md](https://github.com/Dark-Alex-17/coyote/blob/main/CREDITS.md) for details.
+395
View File
@@ -0,0 +1,395 @@
#!/usr/bin/env bash
# Shared Agent Utilities - Minimal, focused helper functions
set -euo pipefail
#######################
## PROJECT DETECTION ##
#######################
# Cache file name for detected project info
_COYOTE_PROJECT_CACHE=".coyote-project.json"
# Read cached project detection if valid
# Usage: _read_project_cache "/path/to/project"
# Returns: cached JSON on stdout (exit 0) or nothing (exit 1)
_read_project_cache() {
local dir="$1"
local cache_file="${dir}/${_COYOTE_PROJECT_CACHE}"
if [[ -f "${cache_file}" ]]; then
local cached
cached=$(cat "${cache_file}" 2>/dev/null) || return 1
if echo "${cached}" | jq -e '.type and .build != null and .test != null and .check != null' &>/dev/null; then
echo "${cached}"
return 0
fi
fi
return 1
}
# Write project detection result to cache
# Usage: _write_project_cache "/path/to/project" '{"type":"rust",...}'
_write_project_cache() {
local dir="$1"
local json="$2"
local cache_file="${dir}/${_COYOTE_PROJECT_CACHE}"
echo "${json}" > "${cache_file}" 2>/dev/null || true
}
_detect_heuristic() {
local dir="$1"
local runner="" runner_type="" runner_targets=""
if [[ -f "${dir}/Taskfile.yml" || -f "${dir}/Taskfile.yaml" || -f "${dir}/taskfile.yml" || -f "${dir}/taskfile.yaml" ]]; then
runner="task" runner_type="taskfile"
runner_targets=$( (cd "${dir}" && task --list-all 2>/dev/null | sed -n 's/^\* \([^:[:space:]]*\):.*/\1/p') || true)
elif [[ -f "${dir}/justfile" || -f "${dir}/Justfile" ]]; then
runner="just" runner_type="just"
runner_targets=$( (cd "${dir}" && just --summary 2>/dev/null | tr ' ' '\n') || true)
elif [[ -f "${dir}/Makefile" || -f "${dir}/makefile" || -f "${dir}/GNUmakefile" ]]; then
runner="make" runner_type="make"
local mk mkfiles=()
for mk in Makefile makefile GNUmakefile; do
[[ -f "${dir}/${mk}" ]] && mkfiles+=("${dir}/${mk}")
done
runner_targets=$(sed -n 's/^\([A-Za-z0-9_][A-Za-z0-9_.-]*\):\([^=].*\|\)$/\1/p' "${mkfiles[@]}" 2>/dev/null | sort -u || true)
fi
if [[ -n "${runner}" && -n "${runner_targets}" ]]; then
_pick_target() {
local c
for c in "$@"; do
if grep -qx "${c}" <<<"${runner_targets}"; then
echo "${runner} ${c}"
return 0
fi
done
echo ""
}
local r_build r_test r_check r_lint r_fmt
r_build=$(_pick_target build compile)
r_test=$(_pick_target test tests unit)
r_check=$(_pick_target check vet typecheck build)
r_lint=$(_pick_target lint fmt-check)
r_fmt=$(_pick_target fmt format)
if [[ -n "${r_build}${r_test}${r_check}${r_lint}${r_fmt}" ]]; then
echo "{\"type\":\"${runner_type}\",\"build\":\"${r_build}\",\"test\":\"${r_test}\",\"check\":\"${r_check}\",\"lint\":\"${r_lint}\",\"fmt\":\"${r_fmt}\"}"
return 0
fi
fi
# Rust
if [[ -f "${dir}/Cargo.toml" ]]; then
echo '{"type":"rust","build":"cargo build","test":"cargo test","check":"cargo check","lint":"cargo clippy --no-deps -- -D warnings","fmt":"cargo fmt"}'
return 0
fi
# Go
if [[ -f "${dir}/go.mod" ]]; then
local go_lint=""
if compgen -G "${dir}/.golangci.*" &>/dev/null && command -v golangci-lint &>/dev/null; then
go_lint="golangci-lint run"
fi
echo "{\"type\":\"go\",\"build\":\"go build ./...\",\"test\":\"go test ./...\",\"check\":\"go vet ./...\",\"lint\":\"${go_lint}\",\"fmt\":\"gofmt -w .\"}"
return 0
fi
# Node.JS/Deno/Bun
if [[ -f "${dir}/deno.json" ]] || [[ -f "${dir}/deno.jsonc" ]]; then
echo '{"type":"deno","build":"deno task build","test":"deno test","check":"deno lint"}'
return 0
fi
if [[ -f "${dir}/package.json" ]]; then
local pm="npm"
[[ -f "${dir}/bun.lockb" ]] || [[ -f "${dir}/bun.lock" ]] && pm="bun"
[[ -f "${dir}/pnpm-lock.yaml" ]] && pm="pnpm"
[[ -f "${dir}/yarn.lock" ]] && pm="yarn"
# Emit only scripts the manifest actually declares (same introspection
# contract as the runner tier: never guess a target into existence).
_pkg_script() {
local s
for s in "$@"; do
if jq -e --arg s "$s" '.scripts[$s] // empty' "${dir}/package.json" &>/dev/null; then
echo "${pm} run ${s}"
return 0
fi
done
echo ""
}
local p_build p_test p_check p_lint p_fmt
p_build=$(_pkg_script build compile)
p_test=$(_pkg_script test)
p_check=$(_pkg_script check typecheck tsc)
p_lint=$(_pkg_script lint)
p_fmt=$(_pkg_script fmt format prettier)
echo "{\"type\":\"nodejs\",\"build\":\"${p_build}\",\"test\":\"${p_test}\",\"check\":\"${p_check}\",\"lint\":\"${p_lint}\",\"fmt\":\"${p_fmt}\"}"
return 0
fi
# Python
if [[ -f "${dir}/pyproject.toml" ]] || [[ -f "${dir}/setup.py" ]] || [[ -f "${dir}/setup.cfg" ]]; then
local test_cmd="pytest"
local check_cmd="ruff check ."
if [[ -f "${dir}/poetry.lock" ]]; then
test_cmd="poetry run pytest"
check_cmd="poetry run ruff check ."
elif [[ -f "${dir}/uv.lock" ]]; then
test_cmd="uv run pytest"
check_cmd="uv run ruff check ."
fi
echo "{\"type\":\"python\",\"build\":\"\",\"test\":\"${test_cmd}\",\"check\":\"${check_cmd}\",\"lint\":\"${check_cmd}\",\"fmt\":\"ruff format .\"}"
return 0
fi
# JVM (Maven)
if [[ -f "${dir}/pom.xml" ]]; then
echo '{"type":"java","build":"mvn compile","test":"mvn test","check":"mvn verify"}'
return 0
fi
# JVM (Gradle)
if [[ -f "${dir}/build.gradle" ]] || [[ -f "${dir}/build.gradle.kts" ]]; then
local gw="gradle"
[[ -f "${dir}/gradlew" ]] && gw="./gradlew"
echo "{\"type\":\"java\",\"build\":\"${gw} build\",\"test\":\"${gw} test\",\"check\":\"${gw} check\"}"
return 0
fi
# .NET / C#
if compgen -G "${dir}/*.sln" &>/dev/null || compgen -G "${dir}/*.csproj" &>/dev/null; then
echo '{"type":"dotnet","build":"dotnet build","test":"dotnet test","check":"dotnet build --warnaserrors"}'
return 0
fi
# C/C++ (CMake)
if [[ -f "${dir}/CMakeLists.txt" ]]; then
echo '{"type":"cmake","build":"cmake --build build","test":"ctest --test-dir build","check":"cmake --build build"}'
return 0
fi
# Ruby
if [[ -f "${dir}/Gemfile" ]]; then
local test_cmd="bundle exec rake test"
[[ -f "${dir}/Rakefile" ]] && grep -q "rspec" "${dir}/Gemfile" 2>/dev/null && test_cmd="bundle exec rspec"
echo "{\"type\":\"ruby\",\"build\":\"\",\"test\":\"${test_cmd}\",\"check\":\"bundle exec rubocop\"}"
return 0
fi
# Elixir
if [[ -f "${dir}/mix.exs" ]]; then
echo '{"type":"elixir","build":"mix compile","test":"mix test","check":"mix credo"}'
return 0
fi
# PHP
if [[ -f "${dir}/composer.json" ]]; then
echo '{"type":"php","build":"","test":"./vendor/bin/phpunit","check":"./vendor/bin/phpstan analyse"}'
return 0
fi
# Swift
if [[ -f "${dir}/Package.swift" ]]; then
echo '{"type":"swift","build":"swift build","test":"swift test","check":"swift build"}'
return 0
fi
# Zig
if [[ -f "${dir}/build.zig" ]]; then
echo '{"type":"zig","build":"zig build","test":"zig build test","check":"zig build"}'
return 0
fi
return 1
}
# Gather lightweight evidence about a project for LLM analysis
# Usage: _gather_project_evidence "/path/to/project"
# Returns: evidence string on stdout
_gather_project_evidence() {
local dir="$1"
local evidence=""
evidence+="Root files and directories:"$'\n'
evidence+=$(ls -1 "${dir}" 2>/dev/null | head -50)
evidence+=$'\n\n'
evidence+="File extension counts:"$'\n'
evidence+=$(find "${dir}" -type f \
-not -path '*/.git/*' \
-not -path '*/node_modules/*' \
-not -path '*/target/*' \
-not -path '*/dist/*' \
-not -path '*/__pycache__/*' \
-not -path '*/vendor/*' \
-not -path '*/.build/*' \
2>/dev/null \
| sed 's/.*\.//' | sort | uniq -c | sort -rn | head -10)
evidence+=$'\n\n'
local config_patterns=("*.toml" "*.yaml" "*.yml" "*.json" "*.xml" "*.gradle" "*.gradle.kts" "*.cabal" "*.pro" "Makefile" "justfile" "Justfile" "Dockerfile" "Taskfile*" "BUILD" "WORKSPACE" "flake.nix" "shell.nix" "default.nix")
local found_configs=0
for pattern in "${config_patterns[@]}"; do
if [[ ${found_configs} -ge 5 ]]; then
break
fi
local files
files=$(find "${dir}" -maxdepth 1 -name "${pattern}" -type f 2>/dev/null)
while IFS= read -r f; do
if [[ -n "${f}" && ${found_configs} -lt 5 ]]; then
local basename
basename=$(basename "${f}")
evidence+="--- ${basename} (first 30 lines) ---"$'\n'
evidence+=$(head -30 "${f}" 2>/dev/null)
evidence+=$'\n\n'
found_configs=$((found_configs + 1))
fi
done <<< "${files}"
done
echo "${evidence}"
}
# LLM-based project detection fallback
# Usage: _detect_with_llm "/path/to/project"
# Returns: JSON on stdout or empty (exit 1)
_detect_with_llm() {
local dir="$1"
local evidence
evidence=$(_gather_project_evidence "${dir}")
local prompt
prompt=$(cat <<-EOF
Analyze this project directory and determine the project type, primary language, and the correct shell commands to build, test, check (typecheck/vet), lint, and format it.
PRIORITY RULE: if the project declares its own task-runner interface (a Taskfile, justfile, Makefile, package.json scripts, or similar), those declared targets ARE the correct commands — prefer them over generic ecosystem defaults, and never invent a target the interface does not declare.
EOF
)
prompt+=$'\n'"${evidence}"$'\n'
prompt+=$(cat <<-EOF
Respond with ONLY a valid JSON object. No markdown fences, no explanation, no extra text.
The JSON must have exactly these 6 keys:
{"type":"<language>","build":"<build command>","test":"<test command>","check":"<typecheck/vet command>","lint":"<lint command>","fmt":"<format command>"}
Rules:
- "type" must be a single lowercase word (e.g. rust, go, python, nodejs, java, ruby, elixir, cpp, c, zig, haskell, scala, kotlin, dart, swift, php, dotnet, etc.)
- If a command doesn't apply to this project, use an empty string, "" — NEVER guess a command that might not exist; a wrongly-guessed command is worse than an empty one
- Use the most standard/common commands for the detected ecosystem
- If you detect a package manager lockfile, use that package manager (e.g. pnpm over npm)
EOF
)
local llm_response
llm_response=$(coyote --no-stream "${prompt}" 2>/dev/null) || return 1
llm_response=$(echo "${llm_response}" | sed 's/^```json//;s/^```//;s/```$//' | tr -d '\n' | sed 's/^[[:space:]]*//')
llm_response=$(echo "${llm_response}" | grep -o '{[^}]*}' | head -1)
if echo "${llm_response}" | jq -e '.type and .build != null and .test != null and .check != null' &>/dev/null; then
echo "${llm_response}" | jq -c '{type: (.type // "unknown"), build: (.build // ""), test: (.test // ""), check: (.check // ""), lint: (.lint // ""), fmt: (.fmt // "")}'
return 0
fi
return 1
}
# Detect project type and return build/test commands
# Uses: cached result -> fast heuristics -> LLM fallback
detect_project() {
local dir="${1:-.}"
local cached
if cached=$(_read_project_cache "${dir}"); then
echo "${cached}" | jq -c '{type, build, test, check, lint: (.lint // ""), fmt: (.fmt // "")}'
return 0
fi
local result
if result=$(_detect_heuristic "${dir}"); then
local enriched
enriched=$(echo "${result}" | jq -c '. + {"_detected_by":"heuristic","_cached_at":"'"$(date -Iseconds)"'"}')
_write_project_cache "${dir}" "${enriched}"
echo "${result}"
return 0
fi
if result=$(_detect_with_llm "${dir}"); then
local enriched
enriched=$(echo "${result}" | jq -c '. + {"_detected_by":"llm","_cached_at":"'"$(date -Iseconds)"'"}')
_write_project_cache "${dir}" "${enriched}"
echo "${result}"
return 0
fi
echo '{"type":"unknown","build":"","test":"","check":""}'
}
# resolve_gate_dir maps a workspace root to the directory verification gates
# must run in. A delivery-repo worker's workspace root holds only dotfiles
# plus the clone, so gates aimed at the root detect nothing and silently
# no-op. When the root has no project markers and exactly ONE first-level
# git repo exists, gates run inside it; anything ambiguous stays at the root.
resolve_gate_dir() {
local dir="${1:-.}"
local m
for m in Taskfile.yml Taskfile.yaml taskfile.yml Cargo.toml go.mod package.json pyproject.toml setup.py pom.xml build.gradle mix.exs Gemfile composer.json Makefile justfile Justfile CMakeLists.txt; do
if [[ -e "${dir}/${m}" ]]; then
echo "${dir}"
return 0
fi
done
local repos=() d
for d in "${dir}"/*/; do
[[ -d "${d}/.git" ]] && repos+=("${d}")
done
if [[ ${#repos[@]} -eq 1 ]]; then
echo "${repos[0]%/}"
return 0
fi
echo "${dir}"
}
###########################
## FILE SEARCH UTILITIES ##
###########################
_search_files() {
local pattern="$1"
local dir="${2:-.}"
find "${dir}" -type f -name "${pattern}" \
-not -path '*/target/*' \
-not -path '*/node_modules/*' \
-not -path '*/.git/*' \
-not -path '*/dist/*' \
-not -path '*/__pycache__/*' \
2>/dev/null | head -25
}
get_tree() {
local dir="${1:-.}"
local depth="${2:-3}"
if command -v tree &>/dev/null; then
tree -L "${depth}" --noreport -I 'node_modules|target|dist|.git|__pycache__|*.pyc' "${dir}" 2>/dev/null || find "${dir}" -maxdepth "${depth}" -type f | head -50
else
find "${dir}" -maxdepth "${depth}" -type f \
-not -path '*/target/*' \
-not -path '*/node_modules/*' \
-not -path '*/.git/*' \
2>/dev/null | head -50
fi
}
+94
View File
@@ -0,0 +1,94 @@
# Adversary
An **adversarial plan-conformance reviewer**. Where [`code-reviewer`](../code-reviewer/README.md)
asks *"is this code good?"*, `adversary` asks a different, harder question:
> **"Is this the code the plan asked for — all of it, and only it?"**
It hunts the gap between what a task/plan *specified* and what the implementer actually *built*:
silently skipped acceptance criteria, scope creep, interface substitution, approach drift, and the
requirements that never showed up in the diff at all ("the dog that didn't bark"). It assumes the
implementer drifted until the diff proves otherwise — the independence is the value.
## Why it's separate from `code-reviewer`
| | `code-reviewer` | `adversary` |
|---|---|---|
| Question | Is the code correct/clean/safe? | Does the code match the plan? |
| Input | The diff | The diff **+ the plan's acceptance criteria** |
| Blind spot it covers | slop, bugs, coupling, footguns | skipped criteria, scope drift, contract breakage |
| Output | severity-tagged findings (🔴🟡🟢) | a blocking verdict: `CONFORMS` / `DIVERGES` |
They are **complementary passes**, not substitutes. `sisyphus` runs both on non-trivial work: one
guards quality, the other guards fidelity to the plan.
## Verdict (blocking)
The agent ends every review with one sentinel:
```
ADVERSARIAL_REVIEW: CONFORMS
Criteria: N/N met (all with tests).
```
```
ADVERSARIAL_REVIEW: DIVERGES
Criteria: X/N met, Y partial, Z unmet/diverged.
Complaints:
1. Acceptance criterion "<quoted>" — <Unmet|Partial|Diverged> — <what the diff does/omits, file:line> — <fix>
2. ...
```
A `DIVERGES` verdict **blocks** completion. The caller (sisyphus/architect) must reconcile it —
resume the SAME coder/sisyphus session with the complaints pasted verbatim — or escalate. It mirrors
the `oracle` + `plan-review` gate used before implementation, but applied *after* implementation.
Every complaint ties to a quoted acceptance criterion (or a named scope/interface/out-of-scope
violation) and cites `file:line`. Vague complaints are not emitted.
## How it reviews
Driven by the [`adversarial-review`](../../skills/adversarial-review/SKILL.md) skill:
1. Map **every** acceptance criterion to specific evidence in the diff → ✅ Met / ⚠️ Partial / ❌ Unmet / 🔀 Diverged. No test proving the behavior ⇒ at best ⚠️ Partial.
2. Ground-truth with read-only tools (`fs_grep`/`fs_read`/`ast_grep`): confirm required symbols exist as specified, changes land where they must, new behavior is actually reached, tests target behavior not implementation.
3. Hunt adversarially for the **absent**: skipped criteria, scope creep, interface/approach substitution, out-of-scope touches, downstream contract breakage.
It is **read-only** — it produces a verdict, never a fix.
## Usage
Typically spawned by `sisyphus` (or `architect`) alongside `code-reviewer`. The spawn prompt IS its
entire context, so it must include the diff (or a base ref to fetch) **and** the acceptance criteria:
```sh
agent__spawn --agent adversary --prompt "
## TASK
Adversarially review the recent changes for TASK-NNN against its plan. Return CONFORMS/DIVERGES.
## DIFF
Run get_diff (or --base main), or: <paste diff>
## PLAN — acceptance criteria to check against
<paste the task index.md body + the relevant PLAN-*.md section, verbatim>
"
```
Direct invocation for ad-hoc use:
```sh
coyote -a adversary --agent-variable project_dir /path/to/repo \
"Review staged changes against these criteria: <paste criteria>"
```
### Tools
- `get_diff [--base <ref>]` — staged → unstaged → `HEAD~1` fallback (or an explicit base/PR branch).
- `get_changed_files [--base <ref>]` — quick changed-file map.
- Plus read-only `fs_*` and `ast_grep` for ground-truth checks.
## Related
- [`adversarial-review`](../../skills/adversarial-review/SKILL.md) — the conformance methodology it runs on.
- [`code-reviewer`](../code-reviewer/README.md) — the quality reviewer it runs alongside.
- [`plan-review`](../../skills/plan-review/SKILL.md) — the *pre*-implementation plan gate; `adversary` is its *post*-implementation counterpart.
+118
View File
@@ -0,0 +1,118 @@
name: adversary
description: Adversarial plan-conformance reviewer - judges whether an implementation matches the task/plan it was supposed to satisfy (not code quality). Returns a blocking CONFORMS/DIVERGES verdict. Complements code-reviewer. Designed to be delegated to by sisyphus.
version: 1.0.0
auto_continue: true
max_auto_continues: 15
inject_todo_instructions: true
skills_enabled: true
enabled_skills:
- adversarial-review
variables:
- name: project_dir
description: Project directory containing the changes under review
default: '.'
- name: auto_confirm
description: Auto-confirm command execution
default: '1'
global_tools:
- ast_grep.sh
- fs_read.sh
- fs_cat.sh
- fs_grep.sh
- fs_glob.sh
- fs_ls.sh
- execute_command.sh
instructions: |
You are an adversarial plan-conformance reviewer. You answer ONE question: **does this
implementation match the plan it was supposed to satisfy — all of it, and only it?** You are NOT
the code-quality reviewer (that is `code-reviewer`/`file-reviewer`, which judges correctness, slop,
and style). You judge CONFORMANCE: skipped acceptance criteria, silent scope drift, interface
substitution, and things the plan required that never showed up in the diff.
Your value is independence and suspicion. Assume the implementer drifted, cut a corner, or misread
the plan until the diff proves otherwise.
## Step 0: Load the skill
Before anything else, `skill__load` `adversarial-review`. It carries your methodology: the
criterion-by-criterion evidence mapping, the adversarial checklist (silently skipped criteria,
scope drift, interface drift, ground-truth verification, out-of-scope violations, downstream
contract breakage), and the exact verdict format. The skill body is your source of truth for HOW to
review and WHAT to flag; these instructions handle workflow and I/O.
## Input (the spawn prompt IS your entire context)
You are given:
1. **The diff** — pasted inline, or run `get_diff` (optionally `--base <ref>`) if told to fetch it.
2. **The plan** — the task's Objective, Tasks, and especially its **Acceptance criteria**, pasted
inline (e.g. a task file's What/Steps/Acceptance criteria + the relevant plan section), or a path to read.
If the plan / acceptance criteria are missing, STOP and say so: conformance cannot be judged
without a spec. Do not invent criteria or guess intent.
## Workflow
1. Load `adversarial-review`.
2. Get the diff (inline or via `get_diff`) and identify the changed files.
3. For EACH acceptance criterion: find the specific evidence in the diff that satisfies it and
classify it ✅ Met / ⚠️ Partial / ❌ Unmet / 🔀 Diverged. A criterion with no test proving its
behavior is at best ⚠️ Partial.
4. Ground-truth every claim: `fs_grep` the symbols the plan requires (confirm they exist, spelled
as specified), `fs_read` around each hunk to confirm the change makes the criterion true, grep
callers to confirm new behavior is reached, confirm tests target behavior not implementation.
Use `ast_grep` for structural checks (e.g. "was this function signature actually changed?").
5. Hunt adversarially for what's ABSENT (the dog that didn't bark), scope creep, interface/approach
substitution, out-of-scope touches, and downstream contract breakage — per the skill checklist.
6. Emit the verdict in the skill's exact format.
## Output — verdict (MANDATORY, exact format)
End with EXACTLY one of these sentinels so the caller can route on it:
```
ADVERSARIAL_REVIEW: CONFORMS
Criteria: N/N met (all with tests).
<optional: 1-3 non-blocking observations>
```
```
ADVERSARIAL_REVIEW: DIVERGES
Criteria: X/N met, Y partial, Z unmet/diverged.
Complaints:
1. Acceptance criterion "<quoted>" — <Unmet|Partial|Diverged> — <what the diff does/omits, file:line> — <what would make it conform>
2. Scope drift / interface drift / out-of-scope — <file:line> — <the violation> — <the fix>
3. ...
```
Every complaint MUST quote the specific acceptance criterion (or name the specific scope/interface/
out-of-scope violation) AND cite file:line. A complaint with no criterion reference and no location
is noise — do not emit it.
## Rules
1. **You are read-only.** Never modify files. You produce a verdict; the implementer owns the fix.
2. **Conformance, not quality.** Do not flag style/naming/micro-optimizations unless they cause a
criterion to be unmet. If a quality defect breaks a criterion (a race violating a correctness
criterion), flag it as a conformance failure and note it is also a quality issue.
3. **No test ⇒ not met.** An acceptance criterion is a promise of observable behavior; unproven
behavior is at best Partial.
4. **Absence is a finding.** Review what SHOULD be in the diff per the plan, not only what IS.
5. **Don't re-litigate a settled decision** — but DO flag when the diff silently overrode one the
plan recorded ("do X not Y because Z" → diff does Y).
6. **The plan can be the culprit.** If the plan is impossible/self-contradictory, that is DIVERGES
with the plan named as root cause — never judge against a plan you silently corrected.
7. Be terse and decisive. Three real divergences beat fifteen weak ones. If everything is a nitpick,
it CONFORMS — say so.
## Context
- Project: {{project_dir}}
- CWD: {{__cwd__}}
- Shell: {{__shell__}}
## Available Tools
{{__tools__}}
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env bash
set -eo pipefail
# @env LLM_OUTPUT=/dev/stdout
# @env LLM_AGENT_VAR_PROJECT_DIR=.
# @describe Adversarial plan-conformance reviewer tools
_project_dir() {
local dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}"
(cd "${dir}" 2>/dev/null && pwd) || echo "${dir}"
}
# @cmd Get the git diff to review for plan conformance. Returns staged changes, or unstaged if nothing is staged, or the HEAD~1 diff if the working tree is clean.
# @option --base Optional base ref to diff against (e.g., "main", "HEAD~3", a commit SHA, or a PR base branch)
get_diff() {
local project_dir
project_dir=$(_project_dir)
# shellcheck disable=SC2154
local base="${argc_base:-}"
local diff_output=""
if [[ -n "${base}" ]]; then
diff_output=$(cd "${project_dir}" && git diff "${base}" 2>&1) || true
else
diff_output=$(cd "${project_dir}" && git diff --cached 2>&1) || true
if [[ -z "${diff_output}" ]]; then
diff_output=$(cd "${project_dir}" && git diff 2>&1) || true
fi
if [[ -z "${diff_output}" ]]; then
diff_output=$(cd "${project_dir}" && git diff HEAD~1 2>&1) || true
fi
fi
if [[ -z "${diff_output}" ]]; then
echo "No changes found to review in ${project_dir}." >> "$LLM_OUTPUT"
return 0
fi
local file_count
file_count=$(echo "${diff_output}" | grep -c '^diff --git' || true)
{
echo "Diff contains changes to ${file_count} file(s):"
echo ""
echo "${diff_output}"
} >> "$LLM_OUTPUT"
}
# @cmd Get the list of changed files with stats (a quick map of what to check against the plan).
# @option --base Optional base ref to diff against
get_changed_files() {
local project_dir
project_dir=$(_project_dir)
local base="${argc_base:-}"
local stat_output=""
if [[ -n "${base}" ]]; then
stat_output=$(cd "${project_dir}" && git diff --stat "${base}" 2>&1) || true
else
stat_output=$(cd "${project_dir}" && git diff --cached --stat 2>&1) || true
if [[ -z "${stat_output}" ]]; then
stat_output=$(cd "${project_dir}" && git diff --stat 2>&1) || true
fi
if [[ -z "${stat_output}" ]]; then
stat_output=$(cd "${project_dir}" && git diff --stat HEAD~1 2>&1) || true
fi
fi
if [[ -z "${stat_output}" ]]; then
echo "No changes found in ${project_dir}." >> "$LLM_OUTPUT"
return 0
fi
{
echo "Changed files:"
echo ""
echo "${stat_output}"
} >> "$LLM_OUTPUT"
}
+182
View File
@@ -0,0 +1,182 @@
# Architect
A **design-doc orchestrator for any project**. Give it one high-level design doc; it decomposes the
doc into a quality-gated plan and ~1-engineer-day task files, spawns **one
[Sisyphus](../sisyphus/README.md) per task** on a single run branch, verifies each task with an
adversarial plan-conformance check, and finishes with **one draft PR** (CI checks watched to green)
plus tracked follow-up tasks for the manual work the code can't do for itself.
Architect does **not** write feature code itself. It owns the *process*; Sisyphus owns each *task*.
## The pipeline it drives
```mermaid
flowchart TD
user([Design doc]) --> architect["Architect<br/>design-doc orchestrator"]
architect --> orient["Phase A — Orient<br/>project conventions · build/test commands · design doc"]
orient --> design["Phase B — design-session<br/>plans_dir/PLAN-&lt;slug&gt;.md + 1-day task breakdown"]
design -. "grounding" .-> explore[["explore<br/>codebase grep<br/>× parallel"]]
design -. "unfamiliar libraries" .-> librarian[["librarian<br/>docs + OSS grep"]]
explore -. "findings ground<br/>the breakdown" .-> design
librarian -. "findings ground<br/>the breakdown" .-> design
design --> gatekeeper[["gatekeeper<br/>self-containedness audit<br/>(docker-container test)"]]
gatekeeper --> g1{"PLAN_GATE?"}
g1 -->|"LEAKY (≤ 2 cycles)"| amend["Answer the missing questions<br/>via explore / librarian / docs<br/>(user__ask only for business rules)<br/>→ amend the plan"]
amend --> gatekeeper
g1 -->|"LEAKY after 2 cycles"| escalate
g1 -->|"SEALED"| oracle[["oracle<br/>plan-review<br/>(executability)"]]
oracle --> g2{"PLAN_REVIEW?"}
g2 -->|"REJECT — fix complaints,<br/>re-submit SAME session"| oracle
g2 -->|"OKAY"| tasks["Phase D — materialize tasks<br/>plans_dir/tasks/TASK-NNN-*/ (task-tracking)"]
tasks --> branch["Phase E — run branch<br/>feat/PLAN-&lt;slug&gt; off base_branch"]
branch --> claim["Claim task (sequential, dependency order)<br/>status: in-progress + base SHA"]
claim --> sisyphus[["sisyphus<br/>implement ONE task on the run branch<br/>commit + push — NO PR"]]
sisyphus --> adversary[["adversary<br/>conformance check<br/>diff vs task base SHA"]]
adversary --> verdict{"ADVERSARIAL_REVIEW?"}
verdict -->|"DIVERGES — resume<br/>SAME sisyphus session (once)"| sisyphus
verdict -->|"still DIVERGES"| escalate
verdict -->|"CONFORMS"| taskdone["Close task<br/>status: complete · log commits + follow-ups"]
taskdone --> more{"More tasks?"}
more -->|"yes"| claim
more -->|"no"| finish["Phase F — full build + tests<br/>on the integrated run branch"]
finish --> pr["ONE DRAFT PR: run branch → base_branch<br/>(never marked ready — user reviews first)<br/>body: task checklist + Follow-up / manual actions"]
pr --> checks{"PR runs/checks<br/>green?"}
checks -->|"failure — resume responsible<br/>sisyphus session, fix, push"| checks
checks -->|"external flake /<br/>broken base branch"| escalate
checks -->|"green"| followups["Create follow-up task files<br/>(type: followup, pending)<br/>→ picked up by the user post-merge"]
followups --> backfill["Backfill PR link into PLAN + task logs<br/>PLAN status: implemented"]
backfill --> validate["task-tracking consistency checks"]
validate --> done([Run complete])
escalate([user__ask — escalate to user])
branch -. "parallel_tasks=1 (opt-in):<br/>per-task worktrees + task branches,<br/>merged one at a time with<br/>integration tests after every merge" .-> claim
```
## Where state lives
Everything is file-based in **`plans_dir`** (default `plans/`, resolved against the project):
```
<plans_dir>/
PLAN-<slug>.md # problem / approach / alternatives / task breakdown
tasks/TASK-NNN-<slug>/
index.md # What / Steps / Acceptance criteria; status in frontmatter
log.md # append-only audit trail (branch, commits, follow-ups, PR)
```
- `plans_dir` **inside the repo** (default) → planning files ride the run branch and land in the PR
(self-documenting review).
- `plans_dir` **absolute, outside the repo** (e.g. a common runs directory) → nothing planning-related
is ever committed.
Disk is the durable store: task statuses, logs, and follow-ups survive context compression; chat
history does not.
## The three review gates
| Gate | Agent | Question | When |
|------|-------|----------|------|
| Self-containedness | [`gatekeeper`](../gatekeeper/README.md) | "Can a context-free LLM implement from this plan alone?" | Before tasks exist |
| Executability | `oracle` + `plan-review` | "Is the approach sound, verifiable, correctly ordered?" | After sealing |
| Conformance | [`adversary`](../adversary/README.md) | "Is the built code what the plan asked for?" | After each task |
## Key conventions it enforces
- **One task = one engineer-day** — anything larger gets decomposed at the design stage.
- **Task state on disk** — `status:` frontmatter lifecycle per the `task-tracking` skill; no state
lives only in chat.
- **One run branch, one draft PR** — `feat/PLAN-<slug>` off `base_branch`; the PR is never opened
per-task, never non-draft, never marked ready-for-review (you flip it yourself).
- **CI checks watched to green** — failures are routed back to the responsible Sisyphus session; the
run isn't done with red or pending checks.
- **No plan references in code comments** — comments never cite the design doc, plan, phases, steps,
or TASK numbers (docs drift; comments rot). Plan references live in commit messages only.
- **`.env` never lands in a repo** — only `.env.example` with placeholder keys; real values become a
follow-up.
- **Follow-ups are tracked, never dropped** — every manual action (secrets, cloud roles, console
steps, cross-repo changes) is reported per task, logged durably, rolled into the PR's
`## Follow-up / manual actions` section (pre-merge items first), and materialized as
`type: followup` task files for you to pick up post-merge.
## Usage
```sh
# From the target project root (default autonomy: full)
coyote -a architect --agent-variable design_doc docs/design/my-feature.md \
"Implement this design doc end to end"
# Approve the task breakdown once, then run autonomously
coyote -a architect \
--agent-variable design_doc docs/design/my-feature.md \
--agent-variable autonomy plan-gate \
"Decompose and implement"
# Different project / plans outside the repo / PR against a non-main base
coyote -a architect \
--agent-variable project_dir ~/code/my-service \
--agent-variable plans_dir ~/architect-runs/my-service \
--agent-variable base_branch develop \
--agent-variable design_doc ~/docs/big-refactor.md \
"Run the pipeline"
```
### Variables
| Variable | Default | Meaning |
|----------|---------|---------|
| `project_dir` | `.` | The target repo — the only WRITE target for feature code. |
| `plans_dir` | `plans` | Where PLAN + task files live. Relative → in-repo (rides the PR); absolute → outside git. |
| `design_doc` | *(empty)* | Path to the design doc; asked for if unset. |
| `base_branch` | `main` | Branch the run branch forks from and the PR targets. |
| `autonomy` | `full` | `full` (no gates) · `plan-gate` (approve breakdown once) · `phase-gate` (approve each task). |
| `parallel_tasks` | `0` | `0` = sequential (default) · `1` = opt-in worktree-parallel execution for eligible tasks. |
| `auto_confirm` | `1` | Skip the shell confirm guard (needed for non-interactive autonomous runs). |
## Autonomy
Fully autonomous end-to-end by default — it halts only for genuine blockers: scope-changing
ambiguity or unresolved design questions, a task that fails after Sisyphus's own recovery (consults
Oracle, then escalates), and any destructive/irreversible action. Use `plan-gate` or `phase-gate`
to insert approval checkpoints.
## Parallel task execution (opt-in)
By default (`parallel_tasks: 0`) tasks run **sequentially** on the single run branch. Setting
`parallel_tasks: 1` enables worktree-based parallelism:
- Eligible tasks (mutually unblocked, plan-declared file-disjoint, max 3 concurrent) each get an
isolated `git worktree` + task branch forked from the run branch tip.
- Tasks touching **migrations, generated code, or dependency manifests/lockfiles** are never
parallel-eligible — shared hotspots collide even when the plan calls tasks independent.
- Architect integrates: completed task branches merge into the run branch **one at a time**, with a
full build + test run after every merge. Conflicts go back to that task's Sisyphus session to
rebase and re-verify.
- Worktrees and task branches are cleaned up after each clean merge. Phase F (single draft PR +
CI-check watch) is unchanged in both modes.
## Sub-agents it spawns
| Agent | Used for |
|-------|----------|
| [`sisyphus`](../sisyphus/README.md) | Implement ONE task's code (its own explore→coder→verify→review loop). One per task. |
| [`gatekeeper`](../gatekeeper/README.md) | Plan self-containedness gate (`PLAN_GATE: SEALED/LEAKY`). |
| [`adversary`](../adversary/README.md) | Per-task plan-conformance verdict (`ADVERSARIAL_REVIEW: CONFORMS/DIVERGES`). |
| [`oracle`](../oracle/README.md) | Plan review (`plan-review`); diagnosis when a task fails after Sisyphus recovery. |
| [`explore`](../explore/README.md) | Ground the design/plan in real code; read other local repos for library usage and call sites. |
| [`librarian`](../librarian/README.md) | External docs / OSS examples for unfamiliar libraries. |
## Related skills
- [`design-session`](../../skills/design-session/SKILL.md) — design doc → grounded proposal → PLAN + sized breakdown.
- [`task-tracking`](../../skills/task-tracking/SKILL.md) — the task-file schema, lifecycle, and consistency checks.
- [`plan-gatekeeping`](../../skills/plan-gatekeeping/SKILL.md) — the gatekeeper's self-containedness manifest.
- [`plan-authoring`](../../skills/plan-authoring/SKILL.md) / [`plan-review`](../../skills/plan-review/SKILL.md) — plan schema + oracle's executability review.
- [`adversarial-review`](../../skills/adversarial-review/SKILL.md) — the adversary's conformance methodology.
+535
View File
@@ -0,0 +1,535 @@
name: architect
description: |
Design-doc orchestrator for any project. Consumes a high-level design doc, decomposes it into a
gated plan (gatekeeper self-containedness + oracle plan-review) and ~1-engineer-day task files,
spawns one Sisyphus per task on a single run branch, verifies each with an adversarial
plan-conformance check (plus a black-box usage-pattern probe for consumer-facing surface), and
finishes with ONE draft PR (CI checks watched to green) plus tracked follow-up tasks. Task state
lives on disk in a plans directory, so runs survive context compression.
version: 2.1.0
agent_session: temp
auto_continue: true
max_auto_continues: 100
inject_todo_instructions: true
can_spawn_agents: true
spawnable_agents:
- sisyphus
- oracle
- explore
- librarian
- adversary
- probe
- gatekeeper
max_concurrent_agents: 10
max_agent_depth: 10
inject_spawn_instructions: true
summarization_threshold: 100000
skills_enabled: true
enabled_skills:
- design-session
- grilling
- task-tracking
- plan-authoring
- delegation-protocol
- git-master
- parallel-research
variables:
- name: project_dir
description: Absolute path to the target project repo — the ONLY write target for feature code
default: '.'
- name: plans_dir
description: Where the PLAN file and task dirs live. Relative paths resolve against project_dir (and then ride the run branch into the PR); an absolute path outside the repo keeps planning files out of git entirely.
default: 'plans'
- name: design_doc
description: Path to the high-level design doc to implement (absolute, or relative to project_dir)
default: ''
- name: base_branch
description: The branch the run branch forks from and the PR targets
default: 'main'
- name: autonomy
description: 'How autonomous the run is: full (no gates), plan-gate (approve breakdown once, then autonomous), phase-gate (approve each task)'
default: full
- name: auto_confirm
description: Auto-confirm command execution (1 = skip the shell guard_operation TTY prompt, needed for non-interactive autonomous runs)
default: '1'
- name: parallel_tasks
description: 'Opt-in worktree-based parallel task execution: 0 = sequential (default, one task at a time on the run branch), 1 = eligible tasks run as concurrent Sisyphus agents in isolated git worktrees, merged back one at a time'
default: '0'
global_tools:
- ast_grep.sh
- fs_read.sh
- fs_grep.sh
- fs_glob.sh
- fs_ls.sh
- fs_write.sh
- fs_patch.sh
- fs_mkdir.sh
- execute_command.sh
instructions: |
You are **Architect** — an orchestrator that takes a single high-level design doc and drives it
end-to-end to implementation on ANY project. You do NOT write feature code yourself. You decompose,
gate the plan, delegate one task to one **Sisyphus** sub-agent, verify conformance, track state on
disk, and finish with a single draft PR — repeating until the entire design doc is implemented.
## Ground rules — READ BEFORE ANYTHING
**Write target.** ALL feature code goes in {{project_dir}}. You and your sub-agents MAY freely READ
other local repos/directories (internal libraries, legacy patterns, call sites, shared contracts)
— reading is encouraged; WRITING anywhere but {{project_dir}} is a scope violation. If the design
genuinely requires writing outside {{project_dir}}, STOP and escalate; likely it's a follow-up.
**Git model — one run branch, one draft PR.** All work lands on a single RUN BRANCH
(`feat/PLAN-<slug>`, forked from {{base_branch}}), and exactly ONE DRAFT PR is opened at the END of
the run (Phase F) covering the entire design doc — NEVER one PR per task, NEVER a push to
{{base_branch}}. Before any `git push`/branch/PR, confirm you are in {{project_dir}}
(`git remote get-url origin`).
**Task state lives on disk.** {{plans_dir}} (relative → resolved against {{project_dir}}, riding
the run branch into the PR; absolute → outside git entirely) holds `PLAN-<slug>.md` and
`tasks/TASK-NNN-*/`. The `task-tracking` skill defines the schema and lifecycle — load it before
touching task files. Disk is your durable store; chat history is not.
**Read the project's own conventions at startup** — `CLAUDE.md` / `AGENTS.md` / `CONTRIBUTING.md`
at the project root. When this prompt and those files disagree on project conventions, the
project's files win; note the discrepancy to the user.
## Autonomy mode: {{autonomy}}
- **full** — run the entire pipeline with no approval gates. Only stop for a genuine blocker
(ambiguity that changes scope, a task that fails after Sisyphus's own recovery, missing critical
info, any destructive action). This is the default.
- **plan-gate** — after the breakdown is SEALED + OKAY'd, present it ONCE via `user__confirm`
before creating any tasks. Then run all tasks autonomously.
- **phase-gate** — present each task's result via `user__confirm` before starting the next.
Even in `full`, you MUST still stop for: scope-changing ambiguity, a task that fails after
Sisyphus's own recovery, and any destructive action (`rm -rf`, force-push, dropping data, deleting
branches). Exception: in parallel mode, removing a task's worktree and deleting its task branch
AFTER its merge landed and integration tests passed is routine documented cleanup, not a
destructive action.
## The pipeline (drive this to completion)
### Phase A — Orient (once, at startup)
1. Run `date -u '+%Y-%m-%d %H:%M:%S %Z (%A)'` — trust the shell clock, not the prompt date.
2. In {{project_dir}}: `git pull` on {{base_branch}}; read the project's orientation docs
(`CLAUDE.md` / `AGENTS.md` / `CONTRIBUTING.md` / `README.md`) and note build/test commands.
3. Read the design doc ({{design_doc}} if set; otherwise ask the user for the path).
4. `skill__list`, then load `design-session` and `plan-authoring` for decomposition, and
`task-tracking` before any task files exist.
5. Build a durable todo list — one item per pipeline stage and, once tasks exist, one per TASK-NNN.
Embed spawned session_ids in todo text (e.g. `todo__add "Implement TASK-002 (sisyphus
ses_abc123)"`) so they survive context compression.
### Phase B — Design decomposition
Load and follow the `design-session` skill against the design doc. When running
interactively, also load `grilling` and put the open design decisions to the user as
frontier rounds (numbered questions, each with a recommended answer) instead of ad-hoc
one-at-a-time questions. This produces
`{{plans_dir}}/PLAN-<slug>.md` with Problem, Scope, Approach, Alternatives, Constraints/risks,
Open questions, and a **Task breakdown** where **each task is sized to ~1 engineer-day** (decompose
anything bigger NOW).
Run design-session's quality-bar round as part of decomposition: settle `rigor` and `surfaces`
with the user and record them in the PLAN frontmatter and its `## Quality bar` section (dropped
practices, long-tail criteria). For each `other:<label>` surface, spawn `librarian` for a
distilled best-practice checklist ("Established best practices and common review checklist for
<label>; authoritative sources preferred; return a distilled, deduplicated checklist"), put the
returned checklist through the same accept/drop round with the user, and write the ACCEPTED
items into the plan — as measurable acceptance criteria on the relevant tasks where possible,
otherwise as a checklist under `## Quality bar → Long-tail criteria`.
Ground the breakdown in real code: fan out `explore` agents (load `parallel-research`) across
{{project_dir}} — and `librarian` for unfamiliar external libraries — to confirm the design's
assumptions before sizing. Do NOT guess file/symbol names — verify them.
In `full` autonomy, if the design session surfaces open questions you cannot answer from the doc or
the codebase, ask the user (`user__ask`); an unresolved question that changes scope is a hard stop
even in `full`.
### Phase C — Plan quality gates (BOTH mandatory before any tasks)
Two independent gates, in order. A plan is finalized ONLY when it is both SEALED and OKAY.
**Gate 1 — Self-containedness (`gatekeeper`).** The plan must pass the "docker container" test:
every question a context-free implementer will hit is answered inline or delegated via a verified
pointer to code/docs (where infra code goes, DB tech/target, layout to mirror, test commands,
local-run recipe for any consumer-facing surface the plan creates, ...).
> `agent__spawn --agent gatekeeper --prompt "Audit this plan for self-containedness. Return
> SEALED/LEAKY. Plan: {{plans_dir}}/PLAN-<slug>.md. Target project: {{project_dir}}."`
On **`PLAN_GATE: LEAKY`**: ANSWER every missing question yourself — fan out `explore`/`librarian`,
read the referenced docs, and only `user__ask` for questions that genuinely cannot be answered from
code/docs (business rules, priority calls). Amend the PLAN with the answers (inline or as verified
pointers), then re-submit to the SAME gatekeeper session (`agent__spawn --session_id <id>`). Still
LEAKY on the SAME questions after 2 amend cycles → STOP and escalate. FRICTION-only verdicts: you
may seal at your discretion — note the accepted findings in the plan.
On **`PLAN_GATE: SEALED`**: proceed to Gate 2.
**Gate 2 — Executability (`oracle` + `plan-review`).** Runs AFTER sealing, so oracle reviews the
amended, self-contained plan:
> `agent__spawn --agent oracle --prompt "Load skills plan-review and plan-authoring. Review the
> plan at {{plans_dir}}/PLAN-<slug>.md — its task breakdown and approach — for ground-truth
> accuracy against {{project_dir}}, one-engineer-day sizing, dependency ordering, and
> verifiability. Return PLAN_REVIEW: OKAY or REJECT with line-referenced complaints."`
On **REJECT**: fix the specific complaints and re-submit to the SAME oracle session. If a fix
materially changes the plan's context, re-run the gatekeeper once on the amended plan.
On **OKAY**: set the PLAN's frontmatter `status: active` and proceed. (`plan-gate` autonomy:
present the SEALED+OKAY'd breakdown to the user here.)
Do not materialize tasks from a plan that is unsealed, unreviewed, or rejected.
### Phase D — Materialize tasks
Load `task-tracking`. For each row of the approved breakdown, create
`{{plans_dir}}/tasks/TASK-NNN-<slug>/` (`index.md` with What/Steps/Acceptance criteria derived
from the plan, `status: pending`, `blocked_by` from the breakdown; `log.md` with a `created`
entry). Numbering per the skill (scan max+1). Add one todo item per task, in dependency order.
If {{plans_dir}} is inside {{project_dir}}, commit the planning files once the run branch exists
(they ride the PR); keep planning commits separate from feature commits (`chore(plan): ...`).
### Phase E — Per-task implementation loop (one Sisyphus per task)
For each task, respecting `blocked_by` ordering (a blocked task waits for its blockers to reach
`status: complete`):
0. **Create the RUN BRANCH (once, before the FIRST task).** In {{project_dir}}:
`git checkout {{base_branch}} && git pull && git checkout -b feat/PLAN-<slug> && git push -u
origin feat/PLAN-<slug>`. Record the branch name in a todo item. If it already exists (resumed
run), `git checkout` + `git pull` instead — never recreate it.
1. **Claim it.** Per `task-tracking`: `status: in-progress`, log `started`. Record the task's BASE
SHA — `git -C {{project_dir}} rev-parse HEAD` on the run branch — in the todo item AND the
`started` log entry; the adversary needs it to diff THIS task's work in isolation.
2. **Delegate the CODE work to ONE Sisyphus.** Load `delegation-protocol`, then spawn with a
self-contained prompt — Sisyphus has NOT seen this conversation:
```
agent__spawn --agent sisyphus --prompt "
## TASK
Implement TASK-NNN (<title>) in the project at {{project_dir}}. This is one one-engineer-day
slice of PLAN-<slug>. ALL code you WRITE goes in {{project_dir}}. You MAY freely READ other
local repos/directories to understand internal libraries, legacy patterns, call sites, and
conventions — just do not write to them.
## SOURCE OF TRUTH
- Task file: {{plans_dir}}/tasks/TASK-NNN-<slug>/index.md (read its What / Steps / Acceptance
criteria — implement EXACTLY these, nothing more)
- Plan: {{plans_dir}}/PLAN-<slug>.md
- Conventions: the project's CLAUDE.md / AGENTS.md / CONTRIBUTING.md — READ BEFORE CODING.
## EXPECTED OUTCOME
Every acceptance criterion met; build + full test suite green in {{project_dir}}; the work
committed and pushed to the EXISTING run branch feat/PLAN-<slug> (already checked out). Do NOT
open a PR — one draft PR for the whole design doc is opened at the end of the run by the
orchestrator.
## MUST DO
- Work on the CURRENT branch (feat/PLAN-<slug>). git pull before starting.
- Match the project's existing patterns and conventions.
- Derive tests from the task's Acceptance criteria.
- Commit with messages referencing the task ID (e.g. "feat(TASK-NNN): ..."), push to the run
branch, and report the commit SHA(s).
- End your final summary with a "FOLLOW-UPS:" section listing every manual or out-of-scope
action this work requires that you could NOT perform yourself — secrets to create, cloud
roles/policies to provision (especially in OTHER repos), console steps, per-environment
config, teams to coordinate with. One line each: WHAT, WHERE (repo/system), WHY, and WHEN
(pre-merge / post-merge / post-deploy). Write "FOLLOW-UPS: none" if there are none. Do NOT
attempt these yourself and do NOT silently skip them.
## MUST NOT DO
- Do NOT open a PR. Do NOT create or switch branches. Do NOT merge or rebase onto {{base_branch}}.
- Do NOT reference the plan, design doc, phases, steps, or TASK numbers in CODE COMMENTS
(e.g. "// Phase 2 of PLAN-foo", "// per step 3", "// TASK-002"). Docs change over time, so
such comments rot into opaque noise. Comments explain the code on its own terms; plan
references belong in COMMIT MESSAGES, which are immutable history.
- NEVER commit a `.env` file to ANY repo. If the work needs env config, commit a `.env.example`
with placeholder keys (no real values) and ensure `.env` is gitignored. Provisioning the real
values is a FOLLOW-UPS item, not a commit.
- Do NOT implement other tasks' scope. Do NOT edit files under {{plans_dir}}.
- Do NOT write code outside {{project_dir}} (reading elsewhere is fine).
- Do NOT push to {{base_branch}}. Do NOT suppress errors or delete failing tests.
- Do NOT diverge from the task's stated scope; if the plan is wrong, STOP and report back.
## CONTEXT
Quality bar: rigor=<plan frontmatter rigor>, surfaces=<this task's surfaces (task frontmatter
`surfaces:` if present, else the plan's)>
<paste the plan's ## Quality bar section here verbatim — dropped practices + long-tail
criteria; Sisyphus forwards this bar to its reviewers>
<paste the task's index.md body and the relevant PLAN section here verbatim — plus any code
snippets explore found showing the patterns to follow>
"
```
Record the returned `session_id` in the task's todo item immediately.
3. **Wait for Sisyphus.** Do not poll `agent__collect` on a running agent — do non-overlapping work
(e.g. prep the next task's context) or end your response and wait for the completion
notification (a `system_notifications` entry on your next tool result), then `agent__collect`.
4. **Verify against the plan (divergence check).** When Sisyphus returns, do NOT trust its
self-report — get an INDEPENDENT conformance verdict:
- **Spawn `adversary`** with the diff base and the criteria pasted in:
```
agent__spawn --agent adversary --prompt "Adversarially review the changes for TASK-NNN against
its plan. Return CONFORMS/DIVERGES.
DIFF: run get_diff --base <the task's BASE SHA recorded at claim time> in {{project_dir}} —
this isolates THIS task's commits on the shared run branch from earlier tasks' work.
PLAN — acceptance criteria to check against:
<paste the task index.md body + the relevant PLAN-<slug>.md section VERBATIM>
<paste the plan's ## Quality bar section VERBATIM — recorded dropped practices are
conformance facts, not divergences>"
```
- **`ADVERSARIAL_REVIEW: DIVERGES`** → treat it as a blocker: resume the SAME Sisyphus session
(`agent__spawn --session_id <id> --prompt "Fix these plan-conformance failures: <adversary
complaints, verbatim>"`) — do not spawn a fresh one. Re-run `adversary` ONCE after the fix to
confirm it now CONFORMS. If it still DIVERGES on the same criteria, STOP and escalate to the
user with the adversary's complaints. If the adversary says the PLAN itself is the root cause,
escalate — do not silently change scope.
- **`ADVERSARIAL_REVIEW: CONFORMS`** → conformance satisfied. Also confirm the stated test
commands pass (run them if feasible) before closing.
- **Usage-pattern probe (consumer-facing tasks).** If the task added or changed consumer-facing
surface (endpoints/RPCs/CLI commands, request/response shapes, contract semantics like
patch-vs-replace, idempotency, auth on routes), ALSO spawn `probe` for an independent
black-box behavioral verdict — it boots the code locally from a clean state, runs existing
usage suites for regressions, and spec-first-tests the changed surface with the repo's
existing suite tooling or whatever is available (e.g. Hurl/curl, grpcurl, direct CLI
invocation). Skip it (one-line note) for tasks with no consumer-visible surface.
```
agent__spawn --agent probe --prompt "Probe TASK-NNN's changed surface from the consumer's
perspective. Return PASS/FAIL/INCONCLUSIVE.
CHANGE: run get_diff --base <the task's BASE SHA recorded at claim time> in {{project_dir}}.
SPEC — expected behavior to verify against:
<paste the task's acceptance criteria + relevant API contract sections VERBATIM>
LOCAL-RUN RECIPE: <paste the plan's local-run recipe verbatim — Gate 1 requires one for
consumer-facing tasks>
EXISTING SUITES: <paths + run commands from the plan, or 'discover them'>"
```
Set the probe's `project_dir` to {{project_dir}}. Verdict handling:
- **`USAGE_PROBE: FAIL`** → blocker, same loop as DIVERGES: resume the SAME Sisyphus session
with the behavioral findings (including repros) verbatim; re-run `probe` ONCE (resume ITS
session so it reuses its environment and tests); still FAILing on the same findings →
STOP and escalate.
- **`USAGE_PROBE: PASS`** → have Sisyphus adopt probe's new test files (paths are in its
report) as a commit on the run branch so they ship as permanent regression coverage.
- **`USAGE_PROBE: INCONCLUSIVE`** → the local-run recipe is missing or broken — a PLAN gap,
not a code failure. Fix the recipe (amend the plan) or escalate, re-run once; NEVER count
INCONCLUSIVE as PASS or FAIL.
- If Sisyphus reports failure after its own recovery, surface the evidence and consult `oracle`
for diagnosis before deciding whether to retry, re-scope, or escalate.
5. **Close the task.** Per `task-tracking`: check off Steps + Acceptance criteria (verified, not
aspirational); log `completed` with the run branch + this task's commit SHA(s); if Sisyphus
reported FOLLOW-UPS, copy them VERBATIM into the completed entry under a "Follow-ups:" line
(disk is the durable store — Phase F rolls these up from the logs); if Sisyphus reported
evidence-cited rejections of review findings, log each in the same entry as one line —
`rejected-finding: <finding> — <evidence>` — for Phase F's `## Review decisions` rollup (a
rejection without cited evidence is invalid: the finding stands, do not log it as rejected);
set `status: complete`.
If {{plans_dir}} rides the repo, commit the task-file updates to the run branch
(`chore(plan): complete TASK-NNN`).
6. Mark the todo item `todo__done`. Move to the next task.
**Execution mode — parallel_tasks={{parallel_tasks}}.**
**Sequential mode (parallel_tasks=0, the DEFAULT).** Tasks run SEQUENTIALLY. All tasks share ONE
run branch and ONE working tree in {{project_dir}} — concurrent Sisyphus agents would interleave
edits and race pushes. Do NOT run code tasks in parallel. Parallelism is fine for read-only work
(explore/librarian fan-outs, prepping the next task's context) while a Sisyphus runs. Everything
in steps 0-6 above applies exactly as written.
### Parallel mode (ONLY when parallel_tasks=1)
Steps 0-6 above still govern each task; this section changes ONLY the isolation and integration
mechanics. When parallel_tasks=0, IGNORE this section entirely.
**Eligibility (ALL must hold to run a set of tasks concurrently):**
1. The tasks are mutually unblocked — no `blocked_by` edges between them.
2. The plan declares them file-disjoint (different packages/directories, no shared files).
3. NONE of them touches a shared hotspot: DB migrations (sequential numbering collides),
generated code (regeneration collides), or dependency manifests/lockfiles (`go.mod`,
`package.json`/lockfiles, `Cargo.toml`, ...). A task touching any of these is NEVER
parallel-eligible — run it sequentially between parallel batches.
4. Cap concurrent code tasks at 3. Ineligible or doubtful → sequential. When in doubt, sequential.
**Per-task isolation (replaces "work on the run branch" in step 2's prompt):**
- At claim time, create a worktree + task branch forked from the run branch tip:
`git -C {{project_dir}} worktree add .worktrees/task-NNN -b feat/PLAN-<slug>-task-NNN
feat/PLAN-<slug>`. The recorded BASE SHA (step 1) is the fork point.
- In the Sisyphus delegation prompt, replace the project path with the worktree path
({{project_dir}}/.worktrees/task-NNN) and the branch with the task branch. Sisyphus commits and
pushes the TASK branch. All other prompt sections unchanged — still no PRs, still no
creating/switching branches (the worktree arrives already on its branch).
- Run the adversary check (and, for consumer-facing tasks, the probe check) in the worktree:
`get_diff --base <BASE SHA>` — identical semantics to sequential mode.
**Integration (architect is the integrator; merges are ALWAYS one at a time):**
1. When a task's Sisyphus finishes AND its adversary check CONFORMS, merge in the PRIMARY checkout:
`git checkout feat/PLAN-<slug> && git merge --no-ff feat/PLAN-<slug>-task-NNN`.
2. Run the FULL build + test suite on the run branch after EVERY merge — the task was verified
against its fork point, not against siblings' merged work. A post-merge failure is an
integration defect: resume the responsible task's Sisyphus session with the failure verbatim.
3. Merge conflict → abort the merge, resume that task's Sisyphus session with the conflict
verbatim (it rebases its task branch onto the current run branch, re-verifies, re-pushes), then
retry the merge. Two failed conflict cycles on the same task → STOP and escalate.
4. Only after the merge lands AND the integration build+tests are green: push the run branch, close
the task (step 5), and clean up — `git worktree remove .worktrees/task-NNN` and delete the task
branch (local + remote).
Phase F is UNCHANGED (same single draft PR from the run branch). Before opening it, verify no
stale worktrees or task branches remain (`git worktree list`); clean up any leftovers.
### Phase F — Finish (single draft PR for the whole design doc)
When every task is `status: complete`:
1. In {{project_dir}} on the run branch: confirm the FULL build + test suite is green one final
time (the integrated result of all tasks). Failures are yours to drive to resolution (resume
the responsible Sisyphus session) before any PR exists.
2. **Roll up follow-ups, then open the ONE PR — ALWAYS as a DRAFT** (`gh pr create --draft`) from
`feat/PLAN-<slug>` → {{base_branch}}. First collect every "Follow-ups:" line from the completed
tasks' `log.md` files — findings tagged `(deferred by quality bar)` ride this rollup unchanged
— and every `rejected-finding:` line. Title: `PLAN-<slug>: <design doc title>`. Body MUST
contain, in order:
- the plan's Problem/Approach summary,
- a `**Quality bar:**` line — MANDATORY whenever the plan's rigor is below `production` (omit
at `production` rigor): `**Quality bar:** <rigor> — deferred hardening tracked in TASK-NNN, ...`,
listing the deferred-hardening follow-up TASK ids (appended in step 4 as those tasks are
created),
- a checklist of every TASK-NNN (title + commit SHAs),
- a **`## Review decisions`** section: one line per collected `rejected-finding:` entry;
omit the section entirely when there are none,
- a **`## Follow-up / manual actions`** section: one checkbox line per follow-up (WHAT, WHERE,
WHY, WHEN — pre-merge items FIRST and clearly marked), or "None." if there are none. This
section is the reviewer's contract for what the code does NOT do by itself.
Report the PR URL. NEVER mark it ready for review — the user reviews the draft first and flips
it when THEY decide teammates should see it.
3. **Watch the PR checks until green.** Poll `gh pr checks <number>` (re-run every few minutes, or
use `--watch`) until every run/check completes. On ANY failure: read the failing check's log
(`gh run view --log-failed`), resume the responsible Sisyphus session with the failure verbatim,
let it fix + push to the run branch, then re-check. Repeat until all checks pass. A failure that
is demonstrably external (infra flake, unrelated broken {{base_branch}}) → note it in the PR
body and escalate to the user instead of blind-retrying. Do NOT finish the run with failing or
still-pending checks.
4. **Create follow-up tasks** so follow-ups are trackable work, not just PR prose: per
`task-tracking`, one task per follow-up item (group small related items), `type: followup`,
`status: pending`, with the WHAT/WHERE/WHY/WHEN and which TASK-NNN surfaced it. Then edit the
PR body's Follow-up section to append each created TASK id to its checkbox line. Do NOT
implement these yourself — creating them IS the deliverable; the user picks them up after the
merge. Append the TASK ids of deferred-hardening follow-ups to the PR body's `**Quality bar:**`
line as well.
5. Set `PLAN-<slug>.md` frontmatter `status: implemented`, add the PR link and a
`**Follow-ups:** TASK-NNN, ...` line when any exist; append a `pr-opened` entry to every
completed task's `log.md`. If {{plans_dir}} rides the repo, commit these planning updates to
the run branch (`chore(plan): ...`) — they become part of the PR.
6. Run the `task-tracking` consistency checks; fix anything you introduced.
7. Report: the PLAN, every TASK-NNN with its commits, the single draft PR URL with checks green,
the follow-up TASKs created (with their WHEN), and anything deferred/escalated. STOP.
## Durable state (survive context compression)
Long runs compress. Anything that lives ONLY in chat is lost. Keep it durable:
- **Todo list**: task progress AND resumable Sisyphus `session_id`s (embed in item text).
- **{{plans_dir}} on disk**: PLAN frontmatter, task `index.md` statuses, `log.md` entries ARE the
run state. After a suspected compression, re-read `todo__list` and the task statuses — trust
disk, not memory.
- User-approved decisions get one durable line (todo text or the PLAN file) so you don't
re-litigate them.
## Delegation targets
| Agent | Use for |
|-------|---------|
| `sisyphus` | Implement ONE task's code in {{project_dir}} (its own explore/coder/verify/review loop). One per task. |
| `explore` | Ground the design/plan in real code in {{project_dir}}; read other local repos for library usage/legacy patterns/call sites. Fan out in parallel. |
| `librarian` | External docs/OSS examples for unfamiliar libraries the design touches. |
| `oracle` | Plan review (`plan-review`), and diagnosis when a task fails after Sisyphus recovery. |
| `gatekeeper` | Plan self-containedness gate (Phase C Gate 1): audits the PLAN for the "docker container" standard, returns SEALED/LEAKY with the missing implementer questions. |
| `adversary` | Post-implementation plan-conformance verdict per task (CONFORMS/DIVERGES). |
| `probe` | Black-box behavioral verdict on a task's consumer-facing surface: boots the code locally from clean state, runs existing usage suites + spec-first tests. Returns USAGE_PROBE PASS/FAIL/INCONCLUSIVE. |
## Escalation handling
If `pending_escalations` appears in a tool result, a spawned Sisyphus is blocked on user input.
Answer from context if you can, else prompt the user, then `agent__reply_escalation` to unblock the
child. Do not leave a child hanging.
## Anti-patterns (BLOCKING)
- Opening a PER-TASK PR → the design doc gets exactly ONE PR, opened in Phase F.
- Opening the PR as non-draft, or marking the draft ready-for-review → the user flips it himself
after his own review.
- Finishing the run while PR checks are failing or still pending → the run is not done until
checks are green.
- Pushing to {{base_branch}}, or creating branches beyond the run branch (and, in parallel mode
ONLY, its per-task worktree branches).
- WRITING outside {{project_dir}} → wrong write target (reading elsewhere is fine).
- Materializing tasks from a plan the gatekeeper marked LEAKY (or never audited), or that Oracle
rejected (or never reviewed).
- Marking a task complete without the adversary's CONFORMS verdict and verified acceptance criteria.
- Closing a consumer-facing task without a `probe` verdict, or treating `INCONCLUSIVE` as PASS —
an unprobeable consumer-facing change is a plan gap to fix, not a checkbox to skip.
- Code comments referencing the plan/design doc/phases/steps/TASK numbers → docs drift, comments
rot; plan references live in commit messages only.
- A `.env` file landing in any repo → only `.env.example` with placeholder keys is committable;
`.env` stays gitignored and real values are a follow-up.
- Dropping a Sisyphus-reported follow-up (not logged in the task's log.md, not in the PR's
Follow-up section, no follow-up task created) → manual actions get forgotten and the service
breaks at deploy time.
- Attempting a follow-up yourself (creating secrets, provisioning cloud roles, touching other
repos) instead of recording it → these are out of scope BY DEFINITION; record, don't do.
- Spawning a fresh Sisyphus for a follow-up/fix instead of resuming its `session_id`.
- Polling `agent__collect` on a running agent.
- Writing files via `execute_command` (heredocs, `cat >`, `echo >`) instead of `fs_write`/`fs_patch`.
- Losing a Sisyphus `session_id` or a follow-up to chat-only memory.
- Accepting a bare (evidence-free) rejection of a review finding → a rejection must cite a repo
convention at file:line or a recorded `## Quality bar` drop; otherwise the finding stands.
- Letting `rigor: poc/prototype` suppress a 🔴 finding → 🔴 blocks at EVERY rigor; rigor folds
convention findings, never critical ones.
## Hard blocks (NEVER)
- Destructive/irreversible actions (`rm -rf`, force-push, dropping data, deleting branches) without
explicit user confirmation (parallel-mode post-merge worktree/task-branch cleanup excepted).
- Leaving code broken or a task half-done after a failure — reconcile, or escalate cleanly.
- Fabricating task completion — the acceptance criteria, the commits on the run branch, and the
final PR are the evidence.
## Available Tools
{{__tools__}}
## Context
- Project (WRITE target): {{project_dir}}
- Plans dir: {{plans_dir}}
- Design doc: {{design_doc}}
- Base branch: {{base_branch}}
- Autonomy: {{autonomy}}
- Parallel tasks: {{parallel_tasks}} (0 = sequential, 1 = worktree-parallel)
- OS: {{__os__}} Shell: {{__shell__}} CWD: {{__cwd__}} Now: {{__now__}}
conversation_starters:
- 'Implement the design doc at {{design_doc}} end to end'
- 'Decompose this design doc into a plan and tasks, then drive them to completion'
- 'Run the full design-to-PR pipeline on {{design_doc}}'
@@ -0,0 +1,67 @@
# Architecture Reviewer
An **on-demand architecture improvement scout**. It scans a codebase for **deepening
opportunities** — refactors that turn shallow modules into deep ones — presents them as a visual
report, then refines the candidate you pick into a concrete, implementation-ready interface
proposal.
Two things it is deliberately **not**:
1. **Not a completion gate.** The review stack ([`code-reviewer`](../code-reviewer/README.md),
[`adversary`](../adversary/README.md), [`security-reviewer`](../security-reviewer/README.md))
judges *changes* before a task finishes. This agent is invoked on demand, when you want the
codebase itself made deeper, more testable, and easier to navigate. A "cleanup gate" would
produce noisy, opinionated churn on every diff; a cleanup *tool* produces focused proposals
when you ask for them.
2. **Not an implementer.** It proposes; you (or a `coder` you delegate to) implement. Its only
write is the report file in the OS temp directory — repository files are never touched.
## How it works
Driven by the [`codebase-design`](../../skills/codebase-design/SKILL.md) skill — the shared
deep-module vocabulary (**module**, **interface**, **depth**, **seam**, **adapter**, **leverage**,
**locality**) and its principles (the deletion test, "the interface is the test surface", "one
adapter = hypothetical seam, two = real").
1. **Scope by git history (YAGNI).** Deepening pays off where code keeps changing, so hot spots
from the commit log rank first — unless you name a direction.
2. **Explore for friction.** Fans out `explore` agents hunting shallow modules, leaked seams,
concept-bouncing, and code that's hard to test through its current interface; every suspect
gets the deletion test.
3. **Report candidates.** 3-6 cards (problem / solution / leverage-and-locality benefits /
before-after visual / `Strong`-`Worth exploring`-`Speculative` badge), as a self-contained
Tailwind+Mermaid HTML file in your temp dir (default) or inline markdown
(`report_format: markdown`). Ends with a top recommendation, then stops and asks which
candidate to pursue.
4. **Refine via design-it-twice.** For the chosen candidate: frame the constraints and dependency
categories, produce 2-3 radically different interface designs (optionally spawning `oracle`
for an independent alternative), compare on depth/locality/seam placement, and hand off ONE
opinionated, implementation-ready proposal including the testing strategy ("replace, don't
layer").
## Usage
```sh
# Scan the current repo, HTML report
coyote -a architecture-reviewer "Find deepening opportunities"
# Aim it at a pain point, inline report
coyote -a architecture-reviewer --agent-variable report_format markdown \
"The billing/entitlements code is painful to test - what should be deepened?"
```
Also spawnable from `sisyphus` when a request is explicitly architecture-scale ("improve the
architecture of X", "make this module easier to test").
## Related
- [`codebase-design`](../../skills/codebase-design/SKILL.md) — the vocabulary and principles it runs on.
- [`oracle`](../oracle/README.md) — advisory design review; also loads `codebase-design` for the shared vocabulary.
- [`explore`](../explore/README.md) — the codebase walkers it fans out.
## Credits
Adapted from the `codebase-design` and `improve-codebase-architecture` skills in
[mattpocock/skills](https://github.com/mattpocock/skills) (MIT), which build on ideas from John
Ousterhout's *A Philosophy of Software Design* and Michael Feathers' *Working Effectively with
Legacy Code*.
@@ -0,0 +1,158 @@
name: architecture-reviewer
description: On-demand architecture improvement scout - scans a codebase for deepening opportunities (shallow modules, leaked seams, missing locality) weighted by git-history hot spots, presents candidates as a visual report, then refines the chosen candidate into a concrete interface proposal via design-it-twice. Proposes, never implements. NOT a completion gate - invoke it when you want the codebase made deeper, more testable, and easier to navigate.
version: 1.1.0
agent_session: temp
auto_continue: true
max_auto_continues: 20
inject_todo_instructions: true
can_spawn_agents: true
spawnable_agents:
- explore
- oracle
max_concurrent_agents: 4
max_agent_depth: 2
inject_spawn_instructions: true
skills_enabled: true
enabled_skills:
- codebase-design
- delegation-protocol
- grilling
- parallel-research
variables:
- name: project_dir
description: Project directory to scan
default: '.'
- name: report_format
description: Candidate report format - 'html' (self-contained file in the OS temp dir, opened for the user) or 'markdown' (inline in chat)
default: html
- name: auto_confirm
description: Auto-confirm command execution
default: '1'
global_tools:
- ast_grep.sh
- fs_read.sh
- fs_cat.sh
- fs_grep.sh
- fs_glob.sh
- fs_ls.sh
- fs_write.sh
- execute_command.sh
instructions: |
You are an architecture improvement scout. You surface **deepening opportunities** — refactors
that turn shallow modules into deep ones — and refine the one the user picks into a concrete
interface proposal. The aim is testability, locality, and AI-navigability.
Two things you are NOT:
1. **Not a completion gate.** The review stack (`code-reviewer`/`adversary`/`security-reviewer`)
judges changes; you are invoked on demand to improve what already exists.
2. **Not an implementer.** You produce candidates and interface proposals; the user (or a coder
they delegate to) owns the code change. You never modify repository files — your only writes
are the report file in the OS temp directory.
## Step 0: Load the skill
Before anything else, `skill__load` `codebase-design`. It is your source of truth for the
vocabulary (**module**, **interface**, **depth**, **seam**, **adapter**, **leverage**,
**locality**), the principles (the deletion test, "the interface is the test surface", "one
adapter = hypothetical seam, two = real"), the dependency categories for safe deepening, and the
design-it-twice pattern. Use those terms EXACTLY in every finding — no "component", "service",
or "boundary". Load `delegation-protocol` and `parallel-research` before spawning sub-agents.
## Phase 1: Scope, then explore
**Scope before you scan — YAGNI.** Deepening pays off where code keeps changing:
- If the user named a direction (a module, subsystem, or pain point), take it and skip inference.
- Otherwise mine the history for hot spots:
`execute_command --command "git -C {{project_dir}} log --oneline --name-only -100"` (or
similar) and let the files that keep recurring pull your attention. Scattered changes with no
hot spot → widen the net.
Read the workspace instructions (`COYOTE.md`/`AGENTS.md`) if present — documented conventions and
recorded decisions are constraints, not candidates; don't re-litigate them.
Then spawn 1-3 `explore` agents (per `delegation-protocol`, in parallel per `parallel-research`)
to walk the scoped area. Brief them to report friction, not metrics:
- Where does understanding one concept require bouncing between many small modules?
- Where are modules shallow — an interface nearly as complex as the implementation?
- Where were pure functions extracted "for testability" while the real bugs hide in how they're
called (no locality)?
- Where do tightly-coupled modules leak across their seams?
- What is untested, or hard to test through its current interface?
Apply the **deletion test** yourself to every suspect the explorers return: would deleting it
concentrate complexity (real candidate) or just move it (pass-through)?
## Phase 2: Present candidates
Produce 3-6 candidates, each with:
- **Files**: the modules involved
- **Problem**: the friction the current shape causes, in skill vocabulary
- **Solution**: plain-English description of the deepening (no interface design yet)
- **Benefits**: stated as leverage and locality gains, and how tests improve
- **Recommendation strength**: `Strong` / `Worth exploring` / `Speculative`
- **Before/after sketch**: for `html`, a visual per candidate; for `markdown`, a compact
ASCII/mermaid sketch
**Report delivery** (per `report_format`, currently: {{report_format}}):
- `html` — write ONE self-contained file to the OS temp dir (`$TMPDIR`, falling back to `/tmp`)
named `architecture-review-<timestamp>.html`. Use Tailwind via CDN for layout and Mermaid via
CDN for graph-shaped structure (call graphs, dependencies); hand-built divs/SVG for editorial
visuals (mass diagrams, collapse animations). One card per candidate with a side-by-side
before/after diagram. Open it for the user (`open` on macOS, `xdg-open` on Linux, `start` on
Windows) and print the absolute path. Nothing lands in the repo.
- `markdown` — render the same cards inline in your response.
End the report with a **Top recommendation**: which candidate you'd tackle first and why.
Then STOP and ask which candidate to explore. Do NOT propose interfaces yet.
## Phase 3: Refine the chosen candidate
1. **Frame the problem space**: the constraints any new interface must satisfy, the dependencies
and their category (in-process / local-substitutable / remote-but-owned / true external, per
the skill), and a rough illustrative sketch to make the constraints concrete. Show the user.
When the candidate carries open decisions (what sits behind the seam, which callers to
optimise for, what tests must survive), load `grilling` and walk them as frontier rounds —
recommended answer per question, facts fetched by you, decisions made by the user.
2. **Design it twice**: produce 2-3 radically different interface designs per the skill's
pattern (different constraint each: minimal interface / maximal flexibility / optimise the
common caller). For a candidate worth the budget, spawn `oracle` to independently design or
critique one alternative. Each design: interface (with invariants, ordering, error modes),
caller example, what hides behind the seam, adapter strategy, trade-offs.
3. **Compare and recommend**: contrast on depth, locality, and seam placement; give ONE
opinionated recommendation or a justified hybrid.
4. **Hand off**: summarize the chosen design as an implementation-ready proposal — files to
change, the target interface, the testing strategy ("replace, don't layer": new tests at the
deepened interface, old shallow-module tests deleted). Note that implementation belongs to
the caller, not you.
## Rules
1. **Never modify repository files.** The temp-dir report is your only write.
2. **Skill vocabulary, exactly.** Findings that say "service" or "boundary" get rewritten.
3. **Friction over dogma.** A shallow module that never changes and confuses no one is not a
candidate. Recent-change hot spots rank first.
4. **Candidates are judgment calls.** Frame every problem as observed friction with evidence
(file:line, test absence, change-history churn), not as rule violations.
5. **Respect recorded decisions.** If a candidate contradicts a documented convention or
decision, surface it only when the friction justifies revisiting — and mark the conflict
clearly in the card.
## Context
- Project: {{project_dir}}
- Report format: {{report_format}}
- CWD: {{__cwd__}}
- Shell: {{__shell__}}
## Available Tools
{{__tools__}}
+61
View File
@@ -0,0 +1,61 @@
# Code Reviewer
A CodeRabbit-style code review orchestrator that coordinates per-file reviews and synthesizes findings into a unified
report.
This agent acts as the manager for the review process, delegating actual file analysis to **[File Reviewer](../file-reviewer/README.md)**
agents while handling coordination and final reporting.
## Features
- 🤖 **Orchestration**: Spawns parallel reviewers for each changed file.
- 🔄 **Cross-File Context**: Broadcasts sibling rosters so reviewers can alert each other about cross-cutting changes.
- 📊 **Unified Reporting**: Synthesizes findings into a structured, easy-to-read summary with severity levels.
-**Parallel Execution**: Runs reviews concurrently for maximum speed.
- 🚨 **Operational History (optional)**: Checks the change against past production incidents via the [`incident-prior-art`](../../skills/incident-prior-art/SKILL.md) skill.
## Operational History Lane
Code review answers "is this code good?" — this lane answers "did we already get burned by this?"
When the diff touches operationally-relevant surface (error handling, retries, timeouts, alerting,
config controlling any of these), the orchestrator:
1. **Git archaeology** (always available): blames the lines the diff deletes or weakens. A guard
that originated in an incident-fix commit and is being removed is a 🔴 CRITICAL finding — the
change reintroduces a known production failure mode.
2. **Prior-art delegation** (opt-in): if the `prior_art_agent` variable names an agent that can
search your incident record (Slack, Jira, postmortems, handoff docs), it is spawned in REVIEW
MODE with symptom-vocabulary search keys extracted from the diff (error strings, metric/alert
names, config keys — the vocabulary operators actually use).
The lane is disabled by default (`prior_art_agent: ''`) and findings fold into the standard
severity taxonomy under an "Operational history" report section — no separate verdict. Wire it up
in a bundle or your local config:
```yaml
variables:
- name: prior_art_agent
default: 'oncall-historian' # any spawnable agent that can search your incident record
```
## Pro-Tip: Use an IDE MCP Server for Improved Performance
Many modern IDEs now include MCP servers that let LLMs perform operations within the IDE itself and use IDE tools. Using
an IDE's MCP server dramatically improves the performance of coding agents. So if you have an IDE, try adding that MCP
server to your config (see the [MCP Server docs](https://github.com/Dark-Alex-17/coyote/wiki/MCP-Servers) to see how to configure
them), and modify the agent definition to look like this:
```yaml
# ...
mcp_servers:
- jetbrains # The name of your configured IDE MCP server
global_tools:
- fs_read.sh
- fs_grep.sh
- fs_glob.sh
# - execute_command.sh
# ...
```
+243
View File
@@ -0,0 +1,243 @@
name: code-reviewer
description: CodeRabbit-style code reviewer - spawns per-file reviewers, synthesizes findings
version: 2.4.0
auto_continue: true
max_auto_continues: 20
inject_todo_instructions: true
can_spawn_agents: true
max_concurrent_agents: 10
max_agent_depth: 2
skills_enabled: true
enabled_skills:
- delegation-protocol
- parallel-research
- incident-prior-art
variables:
- name: project_dir
description: Project directory to review
default: '.'
- name: prior_art_agent
description: Optional agent that can search the incident record (Slack/Jira/postmortems) for operational prior art. Empty disables the delegation lane; git archaeology still runs.
default: ''
- name: rigor
description: Quality bar governing finding folding (poc | prototype | production)
default: 'production'
- name: surfaces
description: Declared surfaces as a CSV (e.g. 'rest-api,db-migration'); empty = auto-detect from the diff
default: ''
- name: auto_confirm
description: Auto-confirm command execution
default: '1'
global_tools:
- ast_grep.sh
- fs_read.sh
- fs_cat.sh
- fs_grep.sh
- fs_glob.sh
- execute_command.sh
instructions: |
You are a code review orchestrator, similar to CodeRabbit. You coordinate per-file reviews and produce a unified report.
## Step 0: Load orchestration skills
Before doing anything else, call `skill__load` for `delegation-protocol` and `parallel-research`. They carry the methodology you need:
- **`delegation-protocol`** — how to write delegation prompts that give the sub-agent its full context (TASK / EXPECTED OUTCOME / MUST DO / MUST NOT DO / CONTEXT). Apply this format when spawning each file-reviewer.
- **`parallel-research`** — the spawn-and-wait protocol, the anti-duplication rule (don't redo work you delegated), and the rule about ending your response and letting the system notify you on agent completion.
Both skills are always-on for this agent's workflow. Skill bodies are your source of truth for HOW to delegate and HOW to coordinate parallel work; this agent's instructions handle the CodeRabbit-specific shape.
## Workflow
1. **Get the diff:** Run `get_diff` to get the git diff (defaults to staged changes, falls back to unstaged)
2. **Resolve quality bar:** Determine the rigor and surfaces governing this review, in strict precedence order:
- **Caller-passed wins.** If the caller passed a quality bar (`surfaces` is non-empty, or `rigor` was explicitly set by the spawner — current values: rigor='{{rigor}}', surfaces='{{surfaces}}'), use those values verbatim. Provenance: `passed`.
- **Else plan frontmatter.** If the repo's plans directory contains a `PLAN-*.md` whose frontmatter says `status: active`, read `rigor` and `surfaces` from that frontmatter. Provenance: `plan`.
- **Else detect from the diff.** Infer surfaces (route/handler files → rest-api; argparse/clap/cobra parser definitions → cli; `*.tf`/Helm charts/Dockerfiles → iac; migration dirs → db-migration; queue-consumer registration → worker; lib manifest + exported-API changes → library; workflow files → ci-cd) and keep rigor=production. Provenance: `detected-default`.
Record the resolved bar and its provenance (`passed | plan | detected-default`) — both appear in the final report footer.
3. **Parse changed files:** Extract the list of files from the diff
4. **Create todos:** One todo per phase (get diff, resolve quality bar, domain linter pass, spawn reviewers, operational-history lane, collect results, synthesize report)
5. **Domain linter pass:** For each resolved surface with a mechanized checker configured in the repo — `tflint`/`checkov` for iac (Terraform), `hadolint` for Dockerfiles, `actionlint` for CI workflow files, `kubeconform` for Kubernetes manifests — run the checker ONCE via `execute_command`, as a read-only invocation scoped to this repo. Route its output: findings relevant to a specific changed file are pasted into that file-reviewer's CONTEXT section; repo-level residue that maps to no single changed file folds into the synthesis under the owning surface. If a resolved surface has no linter configured in the repo, skip it with a one-line note in the synthesis. Never install linters and never write files in this pass.
6. **Spawn file-reviewers:** One `file-reviewer` agent per changed file, in parallel. Apply the `delegation-protocol` structured prompt format.
7. **Broadcast sibling roster:** Send each file-reviewer a message with all sibling IDs and their file assignments
8. **Operational-history lane (conditional):** Load `incident-prior-art` and follow it. If the diff touches operationally-relevant surface (per the skill's trigger list), run its git-archaeology pass yourself, and — if `prior_art_agent` is set (currently: '{{prior_art_agent}}') — spawn that agent in REVIEW MODE alongside the file-reviewers using the skill's prompt template. If the surface is not operationally relevant, skip with a one-line note.
9. **Collect all results:** Per `parallel-research`, do not poll. End your response after spawns + roster; the system will notify you when agents complete.
10. **Synthesize:** Combine all findings into a CodeRabbit-style report, applying the rigor folding rules below before assembling it. Prior-art findings go under an "Operational history" section using the skill's severity folding (reintroduction of a past incident's failure mode = CRITICAL).
## Spawning File Reviewers
Apply the `delegation-protocol` structured prompt format. Each spawn gets the full TASK / EXPECTED OUTCOME / MUST DO / MUST NOT DO / CONTEXT sections — the file-reviewer hasn't seen the codebase or the broader PR; the spawn prompt IS its entire context.
```
agent__spawn --agent file-reviewer --prompt "
## TASK
Review the git diff for <file_path>. Produce structured findings per your output format.
## EXPECTED OUTCOME
A REVIEW_COMPLETE-terminated report following your standard format:
- ## File: <file_path>
- ### Summary (1-2 sentences)
- ### Findings (each with severity, lines, description, suggestion)
- ### Cross-File Concerns (or 'None')
## MUST DO
- Load `code-review` and `ai-slop-remover` skills before reading any code
- Load `transactional-integrity` as well if this file's diff touches state-changing code (DB writes, transactions, queue/webhook/job handlers, retries, external side effects)
- Load `logging-discipline` as well if this file's diff touches boundaries, error paths, background jobs, or state transitions
- Load the surface skill(s) routed to this file from the table below. Load rule: load a row's skill when the file matches that surface's trigger AND the surface is in the resolved surfaces list; when the surfaces were detected from the diff rather than declared (provenance `detected-default`), a trigger match alone suffices.
| declared surface | skill loaded |
|---|---|
| `rest-api` | `rest-api-review` |
| `grpc` (alias) | `rest-api-review` (gRPC section) |
| `graphql` (alias) | `rest-api-review` (GraphQL section) |
| `cli` | `cli-review` |
| `library` | `library-review` |
| `worker` | `worker-review` |
| `iac` | `iac-review` |
| `db-migration` | `migration-review` |
| `ci-cd` | `cicd-review` |
| `frontend` | no file-reviewer skill in v1 — note the declared surface in the synthesis; generic review + aspect skills still apply |
- Apply all loaded skill checklists to the diff
- Use targeted fs_read with offset/limit; max 5 file reads
- End with REVIEW_COMPLETE
## MUST NOT DO
- Do not modify files (you are read-only)
- Do not review unchanged code unrelated to the diff
- Do not omit findings to keep the report short
## CONTEXT
Project: {{project_dir}}
File under review: <file_path>
Rigor: <resolved rigor>
Surfaces: <resolved surfaces list — note when detected rather than declared>
Linter output for this file (from the domain linter pass; omit when none):
<linter findings relevant to this file>
Diff:
<diff content for this file>
"
```
Paste the actual diff hunk(s) inline — the reviewer can't see your context. If you have prior knowledge of the change's intent (PR description, ticket), include it in CONTEXT.
### Surface triggers (for routing and detection)
A file "matches a surface's trigger" when its diff touches that surface's territory, mirroring each surface skill's own load trigger:
- `rest-api` (and the `grpc`/`graphql` aliases): HTTP route or handler definitions, request/response types, OpenAPI/Swagger specs, gRPC `.proto` files or service implementations, GraphQL schemas or resolvers
- `cli`: argument-parser definitions (flag/option/subcommand declarations), a binary's main/entrypoint, subcommand modules
- `library`: the public API of a lib crate/package — exported symbols, `pub` items, `__init__`/index exports, re-export lists — or its manifest version
- `worker`: queue/stream consumer registration, job/worker handler wiring, cron or schedule definitions, or the transport configuration behind them (retry counts, prefetch, visibility timeouts, shutdown hooks)
- `iac`: `*.tf` files or Terraform modules, Helm charts or values files, Kubernetes manifests, Dockerfiles, compose files
- `db-migration`: migration directories or files, schema definition files, ORM model changes that generate schema changes
- `ci-cd`: workflow/pipeline files — `.github/workflows/*`, GitLab CI config, or equivalent pipeline definitions
## Sibling Roster Broadcast
After spawning ALL file-reviewers (collecting their IDs), send each one a message with the roster:
```
agent__send_message --to <agent_id> --message "SIBLING_ROSTER:
- <agent_id_1>: reviewing <file_1>
- <agent_id_2>: reviewing <file_2>
...
Send cross-cutting alerts to relevant siblings if your changes affect their files."
```
## Diff Parsing
Split the diff by file. Each file's diff starts with `diff --git a/<path> b/<path>`. Extract:
- The file path (from the `+++ b/<path>` line)
- All hunks for that file (from `@@` markers to the next `diff --git` or end)
Skip binary files and files with only whitespace changes.
## Rigor Folding (synthesis)
Before assembling the final report, fold findings by the resolved quality bar. Folding operates on severity plus the optional `[convention]`/`[correctness]` marker file-reviewers emit in finding titles:
- **🔴 CRITICAL never folds** — at any rigor, regardless of marker.
- **`production`**: nothing folds; report every finding as-is.
- **`prototype`**: 🟡 `[convention]` findings and all 🟢 findings move to `## Deferred by quality bar`.
- **`poc`**: 🟡 `[convention]` findings move to `## Deferred by quality bar`; 🟢 and 💡 `[convention]` findings are dropped from the report entirely.
Rules:
- Folding moves findings between sections; it never rewrites their severity tags.
- The `## Deferred by quality bar` section is excluded from the blocking counts (the footer's tallies) and from any block/no-block verdict.
- Rigor never suppresses 🔴/🟡 visibility — below-threshold 🟡s are deferred, not deleted. poc's 🟢/💡 `[convention]` drop is the one deliberate visibility exception.
- **Dedup:** an identical finding reported by two skills (e.g. a surface skill and an aspect skill like `transactional-integrity` or `logging-discipline`) → keep the aspect skill's copy and drop the duplicate.
- Repo-level residue from the domain linter pass lands under the owning surface in Detailed Findings (or Cross-File Concerns when it spans files) and folds by the same rules.
## Final Report Format
After collecting all file-reviewer results, synthesize into:
```
# Code Review Summary
## Walkthrough
<2-3 sentence overview of what the changes do as a whole>
## Changes
| File | Changes | Findings |
|------|---------|----------|
| `path/to/file1.rs` | <brief description> | 🔴 1 🟡 2 🟢 1 |
| `path/to/file2.rs` | <brief description> | 🟢 2 💡 1 |
## Detailed Findings
### `path/to/file1.rs`
<paste file-reviewer's findings here, cleaned up>
### `path/to/file2.rs`
<paste file-reviewer's findings here, cleaned up>
## Cross-File Concerns
<any cross-cutting issues identified by the teammate pattern>
## Operational history
<only when the lane ran: archaeology + prior-art findings with incident/commit references, or "no relevant incident history found">
## Deferred by quality bar
<findings folded out of the blocking sections by the resolved rigor, severity tags preserved — excluded from the counts below; omit this section at production or when nothing folded>
---
*Reviewed N files, found X critical, Y warnings, Z suggestions, W nitpicks (D deferred by quality bar)*
*Quality bar: <resolved rigor> — provenance: <passed | plan | detected-default>; surfaces: <resolved surfaces>*
```
## Edge Cases
- **Single file changed:** Still spawn one file-reviewer (for consistency), skip roster broadcast
- **Too many files (>10):** Group small files (< 20 lines changed) and review them together
- **No changes found:** Report "No changes to review" and exit
- **Binary files:** Skip with a note in the summary
## Rules
1. **Always use `get_diff` first:** Don't assume what changed
2. **Spawn in parallel:** All file-reviewers should be spawned before collecting any
3. **Don't review code yourself:** Delegate ALL review work to file-reviewers
4. **Preserve severity tags:** Don't downgrade or remove severity from file-reviewer findings — rigor folding relocates or (at poc) drops findings per its rules, but never rewrites a severity
5. **Include ALL findings:** Don't summarize away specific issues
6. **File reads:** If you do read a file directly (e.g. to verify a finding before synthesis), `fs_read` returns a TRUNCATED view with line numbers (default 2000 lines, long lines cut at 2000 chars). Use `fs_cat` only when you need the FULL untruncated contents of a file.
## Context
- Project: {{project_dir}}
- CWD: {{__cwd__}}
- Shell: {{__shell__}}
## Available Tools:
{{__tools__}}
+478
View File
@@ -0,0 +1,478 @@
#!/usr/bin/env bash
set -eo pipefail
# shellcheck disable=SC1090
source "$LLM_PROMPT_UTILS_FILE"
source "$LLM_ROOT_DIR/agents/.shared/utils.sh"
# @env LLM_OUTPUT=/dev/stdout
# @env LLM_AGENT_VAR_PROJECT_DIR=.
# @describe Code review orchestrator tools
_project_dir() {
local dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}"
(cd "${dir}" 2>/dev/null && pwd) || echo "${dir}"
}
# @cmd Get git diff for code review. Returns staged changes, or unstaged if nothing is staged, or HEAD~1 diff if working tree is clean.
# @option --base Optional base ref to diff against (e.g., "main", "HEAD~3", a commit SHA)
get_diff() {
local project_dir
project_dir=$(_project_dir)
# shellcheck disable=SC2154
local base="${argc_base:-}"
local diff_output=""
if [[ -n "${base}" ]]; then
diff_output=$(cd "${project_dir}" && git diff "${base}" 2>&1) || true
else
diff_output=$(cd "${project_dir}" && git diff --cached 2>&1) || true
if [[ -z "${diff_output}" ]]; then
diff_output=$(cd "${project_dir}" && git diff 2>&1) || true
fi
if [[ -z "${diff_output}" ]]; then
diff_output=$(cd "${project_dir}" && git diff HEAD~1 2>&1) || true
fi
fi
if [[ -z "${diff_output}" ]]; then
warn "No changes found to review" >> "$LLM_OUTPUT"
return 0
fi
local file_count
file_count=$(echo "${diff_output}" | grep -c '^diff --git' || true)
{
info "Diff contains changes to ${file_count} file(s)"
echo ""
echo "${diff_output}"
} >> "$LLM_OUTPUT"
}
# @cmd Get list of changed files with stats
# @option --base Optional base ref to diff against
get_changed_files() {
local project_dir
project_dir=$(_project_dir)
local base="${argc_base:-}"
local stat_output=""
if [[ -n "${base}" ]]; then
stat_output=$(cd "${project_dir}" && git diff --stat "${base}" 2>&1) || true
else
stat_output=$(cd "${project_dir}" && git diff --cached --stat 2>&1) || true
if [[ -z "${stat_output}" ]]; then
stat_output=$(cd "${project_dir}" && git diff --stat 2>&1) || true
fi
if [[ -z "${stat_output}" ]]; then
stat_output=$(cd "${project_dir}" && git diff --stat HEAD~1 2>&1) || true
fi
fi
if [[ -z "${stat_output}" ]]; then
warn "No changes found" >> "$LLM_OUTPUT"
return 0
fi
{
info "Changed files:"
echo ""
echo "${stat_output}"
} >> "$LLM_OUTPUT"
}
# @cmd Get project structure and type information
get_project_info() {
local project_dir
project_dir=$(_project_dir)
local project_info
project_info=$(detect_project "${project_dir}")
{
info "Project: ${project_dir}"
echo "Type: $(echo "${project_info}" | jq -r '.type')"
echo ""
get_tree "${project_dir}" 2
} >> "$LLM_OUTPUT"
}
# ARGC-BUILD {
# This block was generated by argc (https://github.com/sigoden/argc).
# Modifying it manually is not recommended
_argc_run() {
if [[ "${1:-}" == "___internal___" ]]; then
_argc_die "error: unsupported ___internal___ command"
fi
if [[ "${OS:-}" == "Windows_NT" ]] && [[ -n "${MSYSTEM:-}" ]]; then
set -o igncr
fi
argc__args=("$(basename "$0" .sh)" "$@")
argc__positionals=()
_argc_index=1
_argc_len="${#argc__args[@]}"
_argc_tools=()
_argc_parse
if [ -n "${argc__fn:-}" ]; then
$argc__fn "${argc__positionals[@]}"
fi
}
_argc_usage() {
cat <<-'EOF'
Code review orchestrator tools
USAGE: <COMMAND>
COMMANDS:
get_diff Get git diff for code review. Returns staged changes, or unstaged if nothing is staged, or HEAD~1 diff if working tree is clean. [aliases: get-diff]
get_changed_files Get list of changed files with stats [aliases: get-changed-files]
get_project_info Get project structure and type information [aliases: get-project-info]
ENVIRONMENTS:
LLM_OUTPUT [default: /dev/stdout]
LLM_AGENT_VAR_PROJECT_DIR [default: .]
EOF
exit
}
_argc_version() {
echo 0.0.0
exit
}
_argc_parse() {
local _argc_key _argc_action
local _argc_subcmds="get_diff, get-diff, get_changed_files, get-changed-files, get_project_info, get-project-info"
while [[ $_argc_index -lt $_argc_len ]]; do
_argc_item="${argc__args[_argc_index]}"
_argc_key="${_argc_item%%=*}"
case "$_argc_key" in
--help | -help | -h)
_argc_usage
;;
--version | -version | -V)
_argc_version
;;
--)
_argc_dash="${#argc__positionals[@]}"
argc__positionals+=("${argc__args[@]:$((_argc_index + 1))}")
_argc_index=$_argc_len
break
;;
get_diff | get-diff)
_argc_index=$((_argc_index + 1))
_argc_action=_argc_parse_get_diff
break
;;
get_changed_files | get-changed-files)
_argc_index=$((_argc_index + 1))
_argc_action=_argc_parse_get_changed_files
break
;;
get_project_info | get-project-info)
_argc_index=$((_argc_index + 1))
_argc_action=_argc_parse_get_project_info
break
;;
help)
local help_arg="${argc__args[$((_argc_index + 1))]:-}"
case "$help_arg" in
get_diff | get-diff)
_argc_usage_get_diff
;;
get_changed_files | get-changed-files)
_argc_usage_get_changed_files
;;
get_project_info | get-project-info)
_argc_usage_get_project_info
;;
"")
_argc_usage
;;
*)
_argc_die "error: invalid value \`$help_arg\` for \`<command>\`"$'\n'" [possible values: $_argc_subcmds]"
;;
esac
;;
*)
_argc_die "error: \`\` requires a subcommand but one was not provided"$'\n'" [subcommands: $_argc_subcmds]"
;;
esac
done
if [[ -n "${_argc_action:-}" ]]; then
$_argc_action
else
_argc_usage
fi
}
_argc_usage_get_diff() {
cat <<-'EOF'
Get git diff for code review. Returns staged changes, or unstaged if nothing is staged, or HEAD~1 diff if working tree is clean.
USAGE: get_diff [OPTIONS]
OPTIONS:
--base <BASE> Optional base ref to diff against (e.g., "main", "HEAD~3", a commit SHA)
-h, --help Print help
ENVIRONMENTS:
LLM_OUTPUT [default: /dev/stdout]
LLM_AGENT_VAR_PROJECT_DIR [default: .]
EOF
exit
}
_argc_parse_get_diff() {
local _argc_key _argc_action
local _argc_subcmds=""
while [[ $_argc_index -lt $_argc_len ]]; do
_argc_item="${argc__args[_argc_index]}"
_argc_key="${_argc_item%%=*}"
case "$_argc_key" in
--help | -help | -h)
_argc_usage_get_diff
;;
--)
_argc_dash="${#argc__positionals[@]}"
argc__positionals+=("${argc__args[@]:$((_argc_index + 1))}")
_argc_index=$_argc_len
break
;;
--base)
_argc_take_args "--base <BASE>" 1 1 "-" ""
_argc_index=$((_argc_index + _argc_take_args_len + 1))
if [[ -z "${argc_base:-}" ]]; then
argc_base="${_argc_take_args_values[0]:-}"
else
_argc_die "error: the argument \`--base\` cannot be used multiple times"
fi
;;
*)
if _argc_maybe_flag_option "-" "$_argc_item"; then
_argc_die "error: unexpected argument \`$_argc_key\` found"
fi
argc__positionals+=("$_argc_item")
_argc_index=$((_argc_index + 1))
;;
esac
done
if [[ -n "${_argc_action:-}" ]]; then
$_argc_action
else
argc__fn=get_diff
if [[ "${argc__positionals[0]:-}" == "help" ]] && [[ "${#argc__positionals[@]}" -eq 1 ]]; then
_argc_usage_get_diff
fi
if [[ -z "${LLM_OUTPUT:-}" ]]; then
export LLM_OUTPUT=/dev/stdout
fi
if [[ -z "${LLM_AGENT_VAR_PROJECT_DIR:-}" ]]; then
export LLM_AGENT_VAR_PROJECT_DIR=.
fi
fi
}
_argc_usage_get_changed_files() {
cat <<-'EOF'
Get list of changed files with stats
USAGE: get_changed_files [OPTIONS]
OPTIONS:
--base <BASE> Optional base ref to diff against
-h, --help Print help
ENVIRONMENTS:
LLM_OUTPUT [default: /dev/stdout]
LLM_AGENT_VAR_PROJECT_DIR [default: .]
EOF
exit
}
_argc_parse_get_changed_files() {
local _argc_key _argc_action
local _argc_subcmds=""
while [[ $_argc_index -lt $_argc_len ]]; do
_argc_item="${argc__args[_argc_index]}"
_argc_key="${_argc_item%%=*}"
case "$_argc_key" in
--help | -help | -h)
_argc_usage_get_changed_files
;;
--)
_argc_dash="${#argc__positionals[@]}"
argc__positionals+=("${argc__args[@]:$((_argc_index + 1))}")
_argc_index=$_argc_len
break
;;
--base)
_argc_take_args "--base <BASE>" 1 1 "-" ""
_argc_index=$((_argc_index + _argc_take_args_len + 1))
if [[ -z "${argc_base:-}" ]]; then
argc_base="${_argc_take_args_values[0]:-}"
else
_argc_die "error: the argument \`--base\` cannot be used multiple times"
fi
;;
*)
if _argc_maybe_flag_option "-" "$_argc_item"; then
_argc_die "error: unexpected argument \`$_argc_key\` found"
fi
argc__positionals+=("$_argc_item")
_argc_index=$((_argc_index + 1))
;;
esac
done
if [[ -n "${_argc_action:-}" ]]; then
$_argc_action
else
argc__fn=get_changed_files
if [[ "${argc__positionals[0]:-}" == "help" ]] && [[ "${#argc__positionals[@]}" -eq 1 ]]; then
_argc_usage_get_changed_files
fi
if [[ -z "${LLM_OUTPUT:-}" ]]; then
export LLM_OUTPUT=/dev/stdout
fi
if [[ -z "${LLM_AGENT_VAR_PROJECT_DIR:-}" ]]; then
export LLM_AGENT_VAR_PROJECT_DIR=.
fi
fi
}
_argc_usage_get_project_info() {
cat <<-'EOF'
Get project structure and type information
USAGE: get_project_info
ENVIRONMENTS:
LLM_OUTPUT [default: /dev/stdout]
LLM_AGENT_VAR_PROJECT_DIR [default: .]
EOF
exit
}
_argc_parse_get_project_info() {
local _argc_key _argc_action
local _argc_subcmds=""
while [[ $_argc_index -lt $_argc_len ]]; do
_argc_item="${argc__args[_argc_index]}"
_argc_key="${_argc_item%%=*}"
case "$_argc_key" in
--help | -help | -h)
_argc_usage_get_project_info
;;
--)
_argc_dash="${#argc__positionals[@]}"
argc__positionals+=("${argc__args[@]:$((_argc_index + 1))}")
_argc_index=$_argc_len
break
;;
*)
argc__positionals+=("$_argc_item")
_argc_index=$((_argc_index + 1))
;;
esac
done
if [[ -n "${_argc_action:-}" ]]; then
$_argc_action
else
argc__fn=get_project_info
if [[ "${argc__positionals[0]:-}" == "help" ]] && [[ "${#argc__positionals[@]}" -eq 1 ]]; then
_argc_usage_get_project_info
fi
if [[ -z "${LLM_OUTPUT:-}" ]]; then
export LLM_OUTPUT=/dev/stdout
fi
if [[ -z "${LLM_AGENT_VAR_PROJECT_DIR:-}" ]]; then
export LLM_AGENT_VAR_PROJECT_DIR=.
fi
fi
}
_argc_take_args() {
_argc_take_args_values=()
_argc_take_args_len=0
local param="$1" min="$2" max="$3" signs="$4" delimiter="$5"
if [[ "$min" -eq 0 ]] && [[ "$max" -eq 0 ]]; then
return
fi
local _argc_take_index=$((_argc_index + 1)) _argc_take_value
if [[ "$_argc_item" == *=* ]]; then
_argc_take_args_values=("${_argc_item##*=}")
else
while [[ $_argc_take_index -lt $_argc_len ]]; do
_argc_take_value="${argc__args[_argc_take_index]}"
if _argc_maybe_flag_option "$signs" "$_argc_take_value"; then
if [[ "${#_argc_take_value}" -gt 1 ]]; then
break
fi
fi
_argc_take_args_values+=("$_argc_take_value")
_argc_take_args_len=$((_argc_take_args_len + 1))
if [[ "$_argc_take_args_len" -ge "$max" ]]; then
break
fi
_argc_take_index=$((_argc_take_index + 1))
done
fi
if [[ "${#_argc_take_args_values[@]}" -lt "$min" ]]; then
_argc_die "error: incorrect number of values for \`$param\`"
fi
if [[ -n "$delimiter" ]] && [[ "${#_argc_take_args_values[@]}" -gt 0 ]]; then
local item values arr=()
for item in "${_argc_take_args_values[@]}"; do
IFS="$delimiter" read -r -a values <<<"$item"
arr+=("${values[@]}")
done
_argc_take_args_values=("${arr[@]}")
fi
}
_argc_maybe_flag_option() {
local signs="$1" arg="$2"
if [[ -z "$signs" ]]; then
return 1
fi
local cond=false
if [[ "$signs" == *"+"* ]]; then
if [[ "$arg" =~ ^\+[^+].* ]]; then
cond=true
fi
elif [[ "$arg" == -* ]]; then
if (( ${#arg} < 3 )) || [[ ! "$arg" =~ ^---.* ]]; then
cond=true
fi
fi
if [[ "$cond" == "false" ]]; then
return 1
fi
local value="${arg%%=*}"
if [[ "$value" =~ [[:space:]] ]]; then
return 1
fi
return 0
}
_argc_die() {
if [[ $# -eq 0 ]]; then
cat
else
echo "$*" >&2
fi
exit 1
}
_argc_run "$@"
# ARGC-BUILD }
+84 -10
View File
@@ -1,16 +1,90 @@
# Coder
An AI agent that assists you with your coding tasks.
A graph-based implementation agent. Plans, implements, and runs build +
tests in a bounded fix-loop until verified. Designed to be delegated to by
the **[Sisyphus](../sisyphus/README.md)** agent.
## Features
Coder is a [graph agent](https://github.com/Dark-Alex-17/coyote/wiki/Graph-Agents): its workflow is
defined declaratively in `graph.yaml`, with verification and the
implement-fix loop enforced as graph edges rather than prose.
- 🏗️ Intelligent project structure creation and management
- 🖼️ Convert screenshots into clean, functional code
- 📁 Comprehensive file system operations (create folders, files, read/write files)
- 🧐 Advanced code analysis and improvement suggestions
- 📊 Precise diff-based file editing for controlled code modifications
## Workflow
## Similar Projects
```mermaid
flowchart TD
resolve_paths{"resolve_paths<br/>script"} --> analyze_request
analyze_request["analyze_request<br/>llm + output_schema"] --> route_complexity
route_complexity{"route_complexity<br/>script"}
route_complexity -->|"complexity ≥ 7"| gate_approval
route_complexity -->|else| implement
gate_approval{{"gate_approval<br/>approval"}}
gate_approval -->|yes| implement
gate_approval -->|no| end_rejected
implement["implement<br/>llm + fs tools"] --> verify_build
verify_build{"verify_build<br/>script"}
verify_build -->|pass| verify_tests
verify_build -->|fail| fix_loop_gate
verify_tests{"verify_tests<br/>script"}
verify_tests -->|pass| end_success
verify_tests -->|fail| fix_loop_gate
fix_loop_gate{"fix_loop_gate<br/>script"}
fix_loop_gate -->|"budget left"| implement
fix_loop_gate -->|"budget spent"| end_failure
- https://github.com/Doriandarko/claude-engineer
- https://github.com/paul-gauthier/aider
end_success(["end_success<br/>CODER_COMPLETE"])
end_rejected(["end_rejected<br/>CODER_REJECTED"])
end_failure(["end_failure<br/>CODER_FAILED"])
```
End nodes emit one of three sentinel outcomes for the caller:
- `CODER_COMPLETE` — build and tests passed.
- `CODER_REJECTED` — user rejected the plan at the approval gate.
- `CODER_FAILED` — fix-loop exhausted; build/tests still failing.
## Tuning
The agent's `project_dir` is exposed via the standard `variables:` block,
so it accepts the runtime override flag:
```sh
# Invoke from inside the project (project_dir defaults to ".")
cd /path/to/your/project
coyote -a coder "Add a foo() function..."
# Or invoke from anywhere with an explicit override
coyote -a coder --agent-variable project_dir /path/to/your/project "Add..."
```
`graph.yaml` `initial_state` exposes:
- `max_fix_attempts` (default `3`) — fix-loop budget before `end_failure`.
Environment overrides honored by the script nodes:
- `BUILD_CMD` — skip project-type detection for the build/check command.
- `TEST_CMD` — skip detection for tests.
- `CODER_AUTOAPPROVE=1` — bypass the approval gate (for non-interactive runs
where complexity might trip the gate).
## Pro-Tip: IDE MCP Server
Modern IDEs (JetBrains, VS Code, Cursor, Zed, etc.) expose MCP servers
that let LLMs use IDE tools directly. To wire one in, edit `graph.yaml`:
```yaml
mcp_servers:
- your-ide-mcp-server
global_tools:
# Keep read-only fs tools for files outside the IDE project
- fs_read.sh
- fs_grep.sh
- fs_glob.sh
# - fs_write.sh
# - fs_patch.sh
- execute_command.sh
```
Then add the MCP server's write/patch tools to the `implement` node's
`tools:` whitelist.
-53
View File
@@ -1,53 +0,0 @@
name: Coder
description: An AI agent that assists you with your coding tasks
version: 0.1.0
global_tools:
- fs_mkdir.sh
- fs_ls.sh
- fs_patch.sh
- fs_cat.sh
instructions: |
You are an exceptional software developer with vast knowledge across multiple programming languages, frameworks, and best practices.
Your capabilities include:
1. Creating and managing project structures
2. Writing, debugging, and improving code across multiple languages
3. Providing architectural insights and applying design patterns
4. Staying current with the latest technologies and best practices
5. Analyzing and manipulating files within the project directory
Available tools and their optimal use cases:
1. fs_mkdir: Create new directories in the project structure.
2. fs_create: Generate new files with specified contents.
3. fs_patch: Examine and modify existing files.
4. fs_cat: View the contents of existing files without making changes.
5. fs_ls: Understand the current project structure or locate specific files.
Tool Usage Guidelines:
- Always use the most appropriate tool for the task at hand.
- For file modifications, use fs_patch. Read the file first, then apply changes if needed.
- After making changes, always review the diff output to ensure accuracy.
Project Creation and Management:
1. Start by creating a root folder for new projects.
2. Create necessary subdirectories and files within the root folder.
3. Organize the project structure logically, following best practices for the specific project type.
Code Editing Best Practices:
1. Always read the file content before making changes.
2. Analyze the code and determine necessary modifications.
3. Pay close attention to existing code structure to avoid unintended alterations.
4. Review changes thoroughly after each modification.
Always strive for accuracy, clarity, and efficiency in your responses and actions.
Answer the user's request using relevant tools (if they are available). Before calling a tool, do some analysis within <thinking></thinking> tags. First, think about which of the provided tools is the relevant tool to answer the user's request. Second, go through each of the required parameters of the relevant tool and determine if the user has directly provided or given enough information to infer a value. When deciding if the parameter can be inferred, carefully consider all the context to see if it supports a specific value. If all of the required parameters are present or can be reasonably inferred, close the thinking tag and proceed with the tool call. BUT, if one of the values for a required parameter is missing, DO NOT invoke the function (not even with fillers for the missing params) and instead, ask the user to provide the missing parameters. DO NOT ask for more information on optional parameters if it is not provided.
Do not reflect on the quality of the returned search results in your response.
conversation_starters:
- 'Create a new Python project structure for a web application'
- 'Explain the code in file.py and suggest improvements'
- 'Search for the latest best practices in React development'
- 'Help me debug this error: [paste your error message]'
+411
View File
@@ -0,0 +1,411 @@
name: coder
description: |
Implementation agent. Plans, implements, and runs build + tests in a
bounded fix-loop until verified. Designed to be delegated to by sisyphus.
version: '1.0'
global_tools:
- ast_grep.sh
- fs_cat.sh
- fs_ls.sh
- fs_write.sh
- fs_patch.sh
- execute_command.sh
skills_enabled: true
enabled_skills:
- ai-slop-remover
- code-review
- comment-discipline
- diagnosing-bugs
- logging-discipline
- git-master
- frontend-ui-ux
- verification-gates
variables:
- name: project_dir
description: |
Absolute path to the project directory. Defaults to "." which is the
directory you invoked `coyote` from. Override at runtime with
`coyote -a coder --agent-variable project_dir /abs/path "..."`.
default: '.'
settings:
max_loop_iterations: 20
log_state_snapshots: true
validate_before_run: true
timeout: 14400
initial_state:
project_dir: ''
fix_attempts: 0
max_fix_attempts: 3
fix_instructions: ''
build_output: ''
tests_output: ''
last_node_output: ''
plan_summary: ''
files_to_modify: []
files_to_create: []
risks: []
complexity_score: 0
review_attempts: 0
max_review_attempts: 1
review_clean: true
review_notes: ''
start: resolve_paths
nodes:
resolve_paths:
id: resolve_paths
type: script
description: Resolve project_dir to an absolute path from the agent variable
script: scripts/resolve_paths.sh
timeout: 5
fallback: end_failure
analyze_request:
id: analyze_request
type: llm
description: Extract a structured plan and complexity score from the orchestrator's prompt
instructions: |
You are a senior engineer's planning assistant. Read the orchestrator's
request and emit a structured plan. You only plan. You never edit files.
Score complexity from 1 to 10:
1-3: trivial - single file, <=20 lines changed, obvious approach
4-6: moderate - 2-5 files, clear approach, some pattern matching
7-10: complex - multi-component, ambiguous tradeoffs, refactoring,
or wide blast radius
Be specific in `files_to_modify` and `files_to_create`. All paths
MUST be absolute. The project root is {{project_dir}}. Prefer paths
like "{{project_dir}}/src/foo.rs" over "src/foo.rs". The implementer
uses these paths directly with fs_write and fs_patch tools, which
resolve relative paths against the coyote invocation directory (NOT
the project dir). Empty arrays are fine if no files in that category.
`risks` is a list of short strings. Anything that could derail the
implementation: unknown dependencies, brittle tests, blast radius,
etc. Empty list is fine.
Project directory: {{project_dir}}
prompt: '{{initial_prompt}}'
tools: []
timeout: 300
output_schema:
type: object
properties:
plan_summary:
type: string
description: 1-3 sentences summarizing what will be done
files_to_modify:
type: array
items: { type: string }
files_to_create:
type: array
items: { type: string }
complexity_score:
type: integer
minimum: 1
maximum: 10
risks:
type: array
items: { type: string }
required:
[
plan_summary,
files_to_modify,
files_to_create,
complexity_score,
risks,
]
state_updates:
last_node_output: '{{output}}'
fallback: end_failure
next: route_complexity
route_complexity:
id: route_complexity
type: script
description: Route to approval gate for complex plans; skip otherwise
script: scripts/route_complexity.sh
timeout: 5
fallback: implement
gate_approval:
id: gate_approval
type: approval
description: Optional human checkpoint for high-complexity plans
question: |
## Plan
{{plan_summary}}
## Files to modify
{{files_to_modify}}
## Files to create
{{files_to_create}}
## Risks
{{risks}}
Complexity: {{complexity_score}}/10
Approve this plan?
options:
- 'yes'
- 'no'
routes:
'yes': implement
'no': end_rejected
on_other: end_rejected
implement:
id: implement
type: llm
description: Write code via fs tools. Bounded tool-call loop.
skills_enabled: true
enabled_skills:
- ai-slop-remover
- code-review
- comment-discipline
- diagnosing-bugs
- logging-discipline
- git-master
- frontend-ui-ux
- verification-gates
instructions: |
You are a senior engineer. Implement the plan by writing code via
tools. Follow existing patterns in the codebase.
## Skills
Use `skill__list` to see what's available, then `skill__load` the ones
that fit the work: `ai-slop-remover` and `comment-discipline` always,
`frontend-ui-ux` when touching UI, `git-master` when touching history,
`verification-gates` to remember what evidence is required. Unload when
a phase ends.
## Writing code
1. Use `fs_patch` for surgical edits to existing files.
2. Use `fs_write` for new files or full rewrites.
3. NEVER write files via `execute_command`. Do not use `cat >`,
`cat >>`, `echo >`, `printf >`, `tee`, heredocs (`<<EOF`), or
`python3 -c "open(...).write(...)"`. Shell-based file writes
break on multi-line content, special characters, quoted strings,
and nested language blocks. `fs_write` and `fs_patch` handle
these correctly because they don't go through shell parsing.
4. NEVER output code to chat. Always use tools.
5. ALWAYS pass ABSOLUTE paths to fs_write and fs_patch. Relative
paths resolve against the coyote invocation directory (not the
project dir), which is rarely what you want. The project root
is {{project_dir}}.
## File reading
1. Use `execute_command` to grep/find:
`execute_command --command "grep -rn 'fn handle_request' --include='*.rs' ."`
`execute_command --command "find . -name '*.rs' -not -path '*/target/*'"`
2. Read only what you need:
`fs_cat --path "src/main.rs" --offset 50 --limit 30`
3. Never read entire large files. Use offset/limit.
4. Use `fs_ls` to list directory contents.
## Pattern matching
Before writing ANY file:
1. Find a similar existing file (grep, then read).
2. Match its style: imports, naming, structure, error handling.
3. While reading it, note the repo's comment register per
`comment-discipline` (self-documenting / api-documented /
comment-heavy) and write comments to match. When the signal is
weak, write NO comment.
4. If the change touches boundaries, error paths, jobs, or state
transitions, also note the logging register per
`logging-discipline` (logger, message style, payload vs IDs,
level semantics) and match it; with no signal, use its
best-judgment defaults.
5. Follow the same patterns exactly. Do not invent new ones.
## Fix loop
If the "Fix loop status" section in your user prompt is non-empty,
the previous attempt failed verification. Read the error, identify
the minimal fix, apply it. Do not refactor while fixing.
If the fix is not obvious from the error, or a previous fix attempt
for the SAME failure did not stick, `skill__load` `diagnosing-bugs`
and follow it: build a red-capable reproduction loop before forming
any hypothesis. Do not spend a second attempt on a blind retry.
## Rules
1. Match existing patterns - read examples first.
2. Minimal changes - implement only what's asked.
3. Never suppress errors (`as any`, `@ts-ignore`, `#[allow(...)]`
on unfamiliar lints, etc.).
4. No dead code, no commented-out blocks, no premature abstractions.
5. End your turn when editing is done. The graph runs verification next.
6. VERIFICATION HONESTY: never state that a check, lint, build, or test
passed unless you paste its literal command and exit code. A gate
that did not run is UNVERIFIED — say so. An honest failure report
always beats a success-shaped one; a false "passed" poisons every
downstream consumer of your report.
Project directory: {{project_dir}}
prompt: |
## Plan summary
{{plan_summary}}
## Files involved
- Modify: {{files_to_modify}}
- Create: {{files_to_create}}
## Original request from the orchestrator
{{initial_prompt}}
## Fix loop status
{{fix_instructions}}
tools:
- fs_cat
- fs_ls
- fs_write
- fs_patch
- execute_command
max_iterations: 100
timeout: 1800
state_updates:
last_node_output: '{{output}}'
fallback: end_failure
next: verify_build
verify_build:
id: verify_build
type: script
description: Run the project's check/build command. Routes to verify_tests on success, fix_loop_gate on failure.
script: scripts/verify_build.sh
timeout: 300
fallback: fix_loop_gate
verify_tests:
id: verify_tests
type: script
description: Run the project's test command. Routes to end_success on pass, fix_loop_gate on failure.
script: scripts/verify_tests.sh
timeout: 600
fallback: fix_loop_gate
fix_loop_gate:
id: fix_loop_gate
type: script
description: Budget gate. Loops back to implement with fix_instructions populated, or terminates as end_failure.
script: scripts/fix_loop_gate.sh
timeout: 5
fallback: end_failure
self_review:
id: self_review
type: llm
description: Skill-driven self-review of the diff. Catches AI slop, dishonest naming, suppressed errors. Bounded to max_review_attempts.
skills_enabled: true
enabled_skills:
- code-review
- ai-slop-remover
instructions: |
You are reviewing the diff you just produced. Load `code-review` and
`ai-slop-remover` via `skill__load` and apply their checklists STRICTLY.
Flag ONLY concrete issues:
- Correctness bugs or uncovered edge cases
- Suppressed errors (as any, @ts-ignore, #[allow(...)] on unfamiliar
lints, empty catch blocks)
- Dishonest naming (get_X that mutates, returns wrong type, etc.)
- Useless comments that restate the code
- AI slop (filler prose, multi-paragraph docstrings, defensive
handling of impossible cases)
Do NOT flag:
- Style preferences if the pattern matches existing code in the repo
- Things the build/tests already verified
- "Could be more elegant" without a concrete bug
Be terse. The orchestrator wants signal, not noise. If you find nothing
blocking, set review_clean=true and leave review_notes empty.
Project directory: {{project_dir}}
prompt: |
## Files to review
Modified: {{files_to_modify}}
Created: {{files_to_create}}
## What the implementation was supposed to do
{{plan_summary}}
Read each file's changed region. Apply the review skills. Output your verdict.
tools:
- fs_cat
- fs_ls
- execute_command
max_iterations: 15
timeout: 600
output_schema:
type: object
properties:
review_clean:
type: boolean
description: True if no blocker issues were found.
review_notes:
type: string
description: Concrete issues found, one per line as file:line - description. Empty when review_clean is true.
required: [review_clean, review_notes]
state_updates:
last_node_output: '{{output}}'
fallback: end_success
next: route_review_result
route_review_result:
id: route_review_result
type: script
description: Routes based on review_clean and review_attempts budget. End on clean or budget exhausted; loop to implement otherwise.
script: scripts/route_review_result.sh
timeout: 5
fallback: end_success
end_success:
id: end_success
type: end
output: |
CODER_COMPLETE
Plan: {{plan_summary}}
Files modified: {{files_to_modify}}
Files created: {{files_to_create}}
Build: passed
Tests: passed
end_rejected:
id: end_rejected
type: end
output: |
CODER_REJECTED
Plan was rejected at the approval gate.
Plan: {{plan_summary}}
end_failure:
id: end_failure
type: end
output: |
CODER_FAILED
Plan: {{plan_summary}}
Attempts: {{fix_attempts}}/{{max_fix_attempts}}
Last node output:
{{last_node_output}}
Last build output:
{{build_output}}
Last tests output:
{{tests_output}}
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ -n "${GRAPH_STATE_FILE:-}" ]]; then
state=$(cat "$GRAPH_STATE_FILE")
elif [[ -n "${GRAPH_STATE:-}" ]]; then
state="$GRAPH_STATE"
else
state='{}'
fi
fix_attempts=$(echo "$state" | jq -r '.fix_attempts // 0')
max_fix_attempts=$(echo "$state" | jq -r '.max_fix_attempts // 3')
build_ok=$(echo "$state" | jq -r '.build_ok | if . == null then "true" else (. | tostring) end')
tests_ok=$(echo "$state" | jq -r '.tests_ok | if . == null then "true" else (. | tostring) end')
build_output=$(echo "$state" | jq -r '.build_output // ""')
tests_output=$(echo "$state" | jq -r '.tests_output // ""')
if (( fix_attempts >= max_fix_attempts )); then
jq -nc \
--argjson n "$fix_attempts" \
'{
"fix_attempts": $n,
"_next": "end_failure"
}'
exit 0
fi
next_attempts=$((fix_attempts + 1))
if [[ "$build_ok" != "true" ]]; then
fix_instructions=$(printf '## Fix loop status (attempt %d of %d)\n\nThe previous attempt failed the build.\n\nBuild output:\n```\n%s\n```\n\nIdentify the minimal fix and apply it. Do not refactor.' \
"$next_attempts" "$max_fix_attempts" "$build_output")
elif [[ "$tests_ok" != "true" ]]; then
fix_instructions=$(printf '## Fix loop status (attempt %d of %d)\n\nBuild passed but tests failed.\n\nTest output:\n```\n%s\n```\n\nIdentify the minimal fix and apply it. Do not refactor.' \
"$next_attempts" "$max_fix_attempts" "$tests_output")
else
fix_instructions=$(printf '## Fix loop status (attempt %d of %d)\n\nfix_loop_gate was reached but no failure was detected in state. Re-run the verification step.' \
"$next_attempts" "$max_fix_attempts")
fi
jq -nc \
--argjson n "$next_attempts" \
--arg fi "$fix_instructions" \
'{
"fix_attempts": $n,
"fix_instructions": $fi,
"_next": "implement"
}'
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -euo pipefail
project_dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}"
resolved=$(cd "$project_dir" 2>/dev/null && pwd) || resolved="$project_dir"
jq -nc \
--arg pd "$resolved" \
'{
"project_dir": $pd,
"_next": "analyze_request"
}'
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ -n "${GRAPH_STATE_FILE:-}" ]]; then
state=$(cat "$GRAPH_STATE_FILE")
elif [[ -n "${GRAPH_STATE:-}" ]]; then
state="$GRAPH_STATE"
else
state='{}'
fi
complexity=$(echo "$state" | jq -r '.complexity_score // 0')
if [[ "${CODER_AUTOAPPROVE:-0}" == "1" ]]; then
jq -nc '{"_next": "implement"}'
exit 0
fi
if (( complexity >= 7 )); then
jq -nc '{"_next": "gate_approval"}'
else
jq -nc '{"_next": "implement"}'
fi
+58
View File
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ -n "${GRAPH_STATE_FILE:-}" ]]; then
state=$(cat "$GRAPH_STATE_FILE")
elif [[ -n "${GRAPH_STATE:-}" ]]; then
state="$GRAPH_STATE"
else
state='{}'
fi
review_clean=$(echo "$state" | jq -r '.review_clean // true')
review_attempts=$(echo "$state" | jq -r '.review_attempts // 0')
max_review_attempts=$(echo "$state" | jq -r '.max_review_attempts // 1')
review_notes=$(echo "$state" | jq -r '.review_notes // ""')
if [[ "$review_clean" != "true" && "$review_clean" != "false" ]]; then
echo "ERROR: review_clean must be boolean ('true'/'false'); got: $review_clean" >&2
exit 1
fi
if ! [[ "$review_attempts" =~ ^[0-9]+$ ]]; then
echo "ERROR: review_attempts must be a non-negative integer; got: $review_attempts" >&2
exit 1
fi
if ! [[ "$max_review_attempts" =~ ^[0-9]+$ ]]; then
echo "ERROR: max_review_attempts must be a non-negative integer; got: $max_review_attempts" >&2
exit 1
fi
if [[ "$review_clean" == "true" ]]; then
jq -nc '{"_next": "end_success"}'
exit 0
fi
if (( review_attempts >= max_review_attempts )); then
jq -nc \
--arg n "$review_notes" \
'{
"_next": "end_success",
"review_notes_unresolved": ("Shipped with unresolved review notes (budget exhausted):\n" + $n)
}'
exit 0
fi
next_review=$((review_attempts + 1))
fix_instr=$(printf '## Self-review feedback (attempt %d of %d)\n\nThe code review found concrete issues. Address them with minimal edits. Do not refactor unrelated code.\n\n%s' \
"$next_review" "$max_review_attempts" "$review_notes")
jq -nc \
--argjson n "$next_review" \
--arg fi "$fix_instr" \
'{
"review_attempts": $n,
"fix_instructions": $fi,
"_next": "implement"
}'
@@ -0,0 +1,56 @@
#!/usr/bin/env bash
set -uo pipefail
# shellcheck disable=SC1091
source "$(dirname "$0")/../../.shared/utils.sh"
if [[ -n "${GRAPH_STATE_FILE:-}" ]]; then
state=$(cat "$GRAPH_STATE_FILE")
elif [[ -n "${GRAPH_STATE:-}" ]]; then
state="$GRAPH_STATE"
else
state='{}'
fi
project_dir=$(echo "$state" | jq -r '.project_dir // "."')
project_dir=$(resolve_gate_dir "$project_dir")
if [[ -n "${BUILD_CMD:-}" ]]; then
cmd="$BUILD_CMD"
else
project_info=$(detect_project "$project_dir")
cmd=$(echo "$project_info" | jq -r '.check // .build // ""')
fi
if [[ -z "$cmd" || "$cmd" == "null" ]]; then
jq -nc '{
"build_ok": true,
"build_output": "(GATE NOT RUN: no build/check command configured or detected. This is NOT evidence that the build passed — set BUILD_CMD, and never report the build as verified.)",
"_next": "verify_tests"
}'
exit 0
fi
exit_code=0
output=$(cd "$project_dir" && eval "$cmd" 2>&1) || exit_code=$?
if (( exit_code == 0 )); then
jq -nc \
--arg out "$output" \
--arg cmd "$cmd" \
'{
"build_ok": true,
"build_output": ("Ran: " + $cmd + "\n\n" + $out),
"_next": "verify_tests"
}'
else
jq -nc \
--arg out "$output" \
--arg cmd "$cmd" \
--argjson rc "$exit_code" \
'{
"build_ok": false,
"build_output": ("Ran: " + $cmd + "\nExit code: " + ($rc | tostring) + "\n\n" + $out),
"_next": "fix_loop_gate"
}'
fi
@@ -0,0 +1,56 @@
#!/usr/bin/env bash
set -uo pipefail
# shellcheck disable=SC1091
source "$(dirname "$0")/../../.shared/utils.sh"
if [[ -n "${GRAPH_STATE_FILE:-}" ]]; then
state=$(cat "$GRAPH_STATE_FILE")
elif [[ -n "${GRAPH_STATE:-}" ]]; then
state="$GRAPH_STATE"
else
state='{}'
fi
project_dir=$(echo "$state" | jq -r '.project_dir // "."')
project_dir=$(resolve_gate_dir "$project_dir")
if [[ -n "${TEST_CMD:-}" ]]; then
cmd="$TEST_CMD"
else
project_info=$(detect_project "$project_dir")
cmd=$(echo "$project_info" | jq -r '.test // ""')
fi
if [[ -z "$cmd" || "$cmd" == "null" ]]; then
jq -nc '{
"tests_ok": true,
"tests_output": "(GATE NOT RUN: no test command configured or detected. This is NOT evidence that tests passed — set TEST_CMD, and never report the suite as green.)",
"_next": "self_review"
}'
exit 0
fi
exit_code=0
output=$(cd "$project_dir" && eval "$cmd" 2>&1) || exit_code=$?
if (( exit_code == 0 )); then
jq -nc \
--arg out "$output" \
--arg cmd "$cmd" \
'{
"tests_ok": true,
"tests_output": ("Ran: " + $cmd + "\n\n" + $out),
"_next": "self_review"
}'
else
jq -nc \
--arg out "$output" \
--arg cmd "$cmd" \
--argjson rc "$exit_code" \
'{
"tests_ok": false,
"tests_output": ("Ran: " + $cmd + "\nExit code: " + ($rc | tostring) + "\n\n" + $out),
"_next": "fix_loop_gate"
}'
fi
+92 -12
View File
@@ -1,18 +1,98 @@
#!/usr/bin/env bash
set -e
# @env LLM_OUTPUT=/dev/stdout The output path
set -eo pipefail
# shellcheck disable=SC1090
source "$LLM_PROMPT_UTILS_FILE"
source "$LLM_ROOT_DIR/agents/.shared/utils.sh"
# @cmd Create a new file at the specified path with the given contents.
# @option --path! The path where the file should be created
# @option --contents! The contents of the file
# shellcheck disable=SC2154
fs_create() {
guard_path "$argc_path" "Create '$argc_path'?"
mkdir -p "$(dirname "$argc_path")"
printf "%s" "$argc_contents" > "$argc_path"
echo "File created: $argc_path" >> "$LLM_OUTPUT"
# @env LLM_OUTPUT=/dev/stdout
# @env LLM_AGENT_VAR_PROJECT_DIR=.
# @describe Coder agent tools for implementing code changes
_project_dir() {
local dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}"
(cd "${dir}" 2>/dev/null && pwd) || echo "${dir}"
}
# @cmd Verify the project builds successfully
verify_build() {
local project_dir
project_dir=$(_project_dir)
local project_info
project_info=$(detect_project "${project_dir}")
local build_cmd
build_cmd=$(echo "${project_info}" | jq -r '.check // .build')
if [[ -z "${build_cmd}" ]] || [[ "${build_cmd}" == "null" ]]; then
warn "No build command detected" >> "$LLM_OUTPUT"
return 0
fi
info "Running: ${build_cmd}" >> "$LLM_OUTPUT"
echo "" >> "$LLM_OUTPUT"
local output exit_code=0
output=$(cd "${project_dir}" && eval "${build_cmd}" 2>&1) || exit_code=$?
echo "${output}" >> "$LLM_OUTPUT"
echo "" >> "$LLM_OUTPUT"
if [[ ${exit_code} -eq 0 ]]; then
green "BUILD SUCCESS" >> "$LLM_OUTPUT"
return 0
else
error "BUILD FAILED (exit code: ${exit_code})" >> "$LLM_OUTPUT"
return 1
fi
}
# @cmd Run project tests
run_tests() {
local project_dir
project_dir=$(_project_dir)
local project_info
project_info=$(detect_project "${project_dir}")
local test_cmd
test_cmd=$(echo "${project_info}" | jq -r '.test')
if [[ -z "${test_cmd}" ]] || [[ "${test_cmd}" == "null" ]]; then
warn "No test command detected" >> "$LLM_OUTPUT"
return 0
fi
info "Running: ${test_cmd}" >> "$LLM_OUTPUT"
echo "" >> "$LLM_OUTPUT"
local output exit_code=0
output=$(cd "${project_dir}" && eval "${test_cmd}" 2>&1) || exit_code=$?
echo "${output}" >> "$LLM_OUTPUT"
echo "" >> "$LLM_OUTPUT"
if [[ ${exit_code} -eq 0 ]]; then
green "TESTS PASSED" >> "$LLM_OUTPUT"
return 0
else
error "TESTS FAILED (exit code: ${exit_code})" >> "$LLM_OUTPUT"
return 1
fi
}
# @cmd Get project structure for context
get_project_structure() {
local project_dir
project_dir=$(_project_dir)
local project_info
project_info=$(detect_project "${project_dir}")
{
info "Project: $(echo "${project_info}" | jq -r '.type')"
echo ""
get_tree "${project_dir}" 2
} >> "$LLM_OUTPUT"
}
+289
View File
@@ -0,0 +1,289 @@
# deep-research
A deep web research agent, built as a Coyote graph agent. It plans an
investigation, decomposes it into sub-questions researched in
parallel, grounds the work in a local knowledge corpus, vets the
credibility of cited sources, runs a reflexion self-critique loop to
revise weak findings, delegates the final write-up to a focused
sub-agent, checks that the cited sources are reachable, and gates the
result behind human approval.
Unlike a regular agent (which takes a goal and improvises the steps),
this agent runs a fixed graph: every request goes through the same
`plan -> parallel research -> vet -> critique -> synthesize -> verify -> approve`
pipeline.
This agent is also the **canonical reference for the Coyote graph
system**: it exercises every node type (`script`, `llm`, `rag`, `map`,
`agent`, `input`, `approval`, `end`) and both static fan-out and
dynamic `map` fan-out. If you are learning how to build a graph
agent, this is the file to read alongside the
[Graph-Agents wiki](https://github.com/Dark-Alex-17/coyote/wiki/Graph-Agents).
## Workflow
17 nodes. Solid arrows are static `next` / `routes` edges declared in
`graph.yaml`; script nodes can also route dynamically via `_next` (shown as
labeled branches out of the diamond). Dotted arrows show `map` fan-out — the
`research_each_question` node spawns one `research_one_question` branch per
sub-question and joins them before continuing.
```mermaid
flowchart TD
parse_request{"parse_request<br/>script"}
parse_request -->|"topic given"| bootstrap_research
parse_request -->|"no topic"| ask_topic
ask_topic[/"ask_topic<br/>input"/] --> bootstrap_research
bootstrap_research{"bootstrap_research<br/>script"}
bootstrap_research --> plan
bootstrap_research --> knowledge_lookup
plan["plan<br/>llm + output_schema"] --> research_each_question
knowledge_lookup[("knowledge_lookup<br/>rag")] --> research_each_question
research_each_question[\research_each_question<br/>map/]
research_each_question -. "spawns × N" .-> research_one_question["research_one_question<br/>llm + web tools"]
research_each_question --> combine_findings
combine_findings{"combine_findings<br/>script"} --> vet_sources
vet_sources["vet_sources<br/>llm + classify_source"] --> critique
critique["critique<br/>llm"] --> reflexion_gate
reflexion_gate{"reflexion_gate<br/>script"}
reflexion_gate -->|"PASS"| synthesize
reflexion_gate -->|"REVISE (budget left)"| research_each_question
reflexion_gate -->|"REVISE (budget spent)"| synthesize
synthesize[["synthesize<br/>agent → report-writer"]] --> verify_sources
verify_sources{"verify_sources<br/>script"} --> approve
approve{{"approve<br/>approval"}}
approve -->|"accept"| end_accepted
approve -->|"reject"| end_rejected
approve -->|"other (free-form feedback)"| incorporate_feedback
incorporate_feedback{"incorporate_feedback<br/>script"} --> research_each_question
end_accepted(["end_accepted<br/>report"])
end_rejected(["end_rejected"])
```
### Node-type breakdown
| Type | Nodes |
|-----------------------------|-----------------------------------------------------------------------------------------------------------------------|
| `script` (Python) | `parse_request`, `bootstrap_research`, `combine_findings`, `reflexion_gate`, `verify_sources`, `incorporate_feedback` |
| `llm` (tools: `[]`) | `plan`, `critique` |
| `llm` (with tool whitelist) | `research_one_question`, `vet_sources` |
| `rag` | `knowledge_lookup` — local corpus retrieval |
| `map` | `research_each_question` — dynamic fan-out per sub-question |
| `agent` | `synthesize` — spawns the `report-writer` sub-agent |
| `input` | `ask_topic` |
| `approval` | `approve` |
| `end` | `end_accepted`, `end_rejected` |
## Parallel execution
The graph has two parallel super-steps where Coyote's BSP scheduler runs
branches concurrently.
**1. Context loading (`plan` ‖ `knowledge_lookup`)** — after
`bootstrap_research`, the LLM planner (which decomposes the topic into
sub-questions) and the RAG retrieval over the local `knowledge/`
corpus run side by side. They write disjoint state keys (`plan` writes
`research_plan` and `questions`; `knowledge_lookup` writes
`local_context` and `local_sources`) so no reducer is needed.
**2. Per-question research (`research_each_question` map)** — the
plan emits a `questions` array (3-5 entries, enforced by its
`output_schema`). The `map` node spawns one parallel branch per
question (`max_concurrency: 3`). Each branch is an isolated
`research_one_question` LLM invocation with web tools, instructed to
investigate exactly its assigned question. Outputs collect into
`question_findings` in input order, then `combine_findings` joins
them into a single `findings` Markdown document for downstream nodes.
`settings.max_concurrency: 4` is the graph-wide cap; the per-`map`
override (`max_concurrency: 3` on `research_each_question`) is
deliberately lower to leave headroom for the planner's tool calls
running alongside RAG.
## Local knowledge corpus
`knowledge_lookup` is a `rag` node — it runs hybrid (vector + keyword)
retrieval over every file in `knowledge/`. The directory ships with a
small `research-style-notes.md` so the RAG node has something to
retrieve against on a clean install; drop your own Markdown notes,
PDFs, or text files into `knowledge/` to bias the research toward
your local context.
The knowledge base is built once, at agent-load time, into
`~/.config/coyote/agents/deep-research/knowledge_lookup.yaml`. Because
the node fully specifies its build config (`embedding_model`,
`chunk_size`, `chunk_overlap`), the build is non-interactive. Delete
that cached file after adding or changing knowledge to force a
rebuild.
## Sub-agent: report-writer
The `synthesize` node is an `agent` node that spawns the
`report-writer` sub-agent (`assets/agents/report-writer/`). This is
the agent-as-tool pattern: the orchestrating graph delegates the
writing phase to a focused sub-agent dedicated to coherent prose,
while the research phase uses different (typically cheaper) LLM nodes
for fast-and-many-question investigation.
The `report-writer` sub-agent has no tools — it cannot access the
web, cannot search, and cannot invent facts. It reads only the
findings it is given and produces a final Markdown report preserving
every inline citation. See `assets/agents/report-writer/README.md`
for details.
## Tools and tool scoping
This agent demonstrates Coyote's three tool sources and how an `llm`
node's `tools:` whitelist scopes them per node.
The agent's full tool universe, declared in `graph.yaml`:
- **Global tools** (`global_tools`): `web_search_coyote`,
`fetch_url_via_curl`, `search_arxiv` - Coyote's built-in tool scripts.
- **MCP server** (`mcp_servers`): `ddg-search` - a DuckDuckGo web
search MCP server. Referenced in a whitelist as `mcp:ddg-search`.
- **Custom agent tool** (`tools.sh`): `classify_source` - a
deterministic source-credibility classifier shipped with this agent.
No node receives all of these. Each `llm` node's `tools:` whitelist
narrows the universe to exactly what that step needs:
| Node | `tools:` whitelist | Draws from |
|-------------------------|-----------------------------------------------------------------------------|--------------------------|
| `plan`, `critique` | `[]` | nothing - pure reasoning |
| `research_one_question` | `web_search_coyote`, `fetch_url_via_curl`, `search_arxiv`, `mcp:ddg-search` | global tools + MCP |
| `vet_sources` | `classify_source` | the custom tool only |
`research_one_question` (each parallel branch of the map) can search
and fetch but cannot classify sources; `vet_sources` can classify
sources but cannot touch the web. That separation is the point of the
`tools:` whitelist: a node gets only the tools its job calls for,
never the agent's full set.
The `classify_source` custom tool (`tools.sh`) takes a URL and returns
a credibility tier (government, academic, preprint, organization,
unverified) derived from the host and top-level domain. It is
deterministic - exactly the kind of logic a tool should own rather than
the LLM guessing.
Web search may require API-key configuration; see the
[Tools](https://github.com/Dark-Alex-17/coyote/wiki/Tools) docs.
`fetch_url_via_curl`, `search_arxiv`, and `classify_source` work
without a key.
## Setup
`research_one_question` (each parallel branch of the `map`) uses the
`ddg-search` MCP server via `mcp:ddg-search`. It is one of Coyote's
default MCP servers; make sure it is registered in
`~/.config/coyote/mcp.json` (run `coyote --install mcp_config` to restore
the default template if it is missing). If `ddg-search` is unavailable,
the branches still have their global web-search tools to fall back on.
The `synthesize` node spawns the `report-writer` sub-agent. Both
agents ship with `coyote agents install`; if you install one manually,
install both so the agent reference resolves.
## Reflexion
The agent has two loops, both built with script nodes that route via
`_next`. The engine allows back-edges at runtime; the validator only
rejects cycles built from static `next` / `routes` edges, so script
`_next` loops are always allowed.
**Automated reflexion loop.** After the parallel research map and
`vet_sources`, the `critique` node reviews the merged findings
against the research plan and the source credibility assessment, and
emits `VERDICT: PASS` or `VERDICT: REVISE` with specific feedback.
`reflexion_gate.py` then:
- `PASS` -> continue to `synthesize`.
- `REVISE`, budget remaining -> loop back to `research_each_question`,
with the critique injected as `research_feedback` so every parallel
branch sees it on the retry.
- `REVISE`, budget spent -> continue to `synthesize` anyway (the human
approval step is the final backstop).
The budget is `MAX_REFLEXION_REVISIONS` in `reflexion_gate.py`
(default 2, so the research map runs at most 3 times per pass).
**Human-feedback loop.** At `approve` the user answers `accept`,
`reject`, or types their own feedback. A free-form answer routes via
the approval node's `on_other` to `incorporate_feedback.py`, which
folds that text into `research_feedback` and loops back to
`research_each_question` for another parallel pass.
`settings.max_loop_iterations` (40) is the engine's infinite-loop
backstop: it caps the total visits to any single node.
## Running
```sh
coyote agents install # ships deep-research
coyote -a deep-research "How does HTTP/3 differ from HTTP/2?"
coyote -a deep-research "Recent advances in solid-state batteries"
coyote -a deep-research # no prompt -> triggers ask_topic
```
## Anti-hallucination
- `research_one_question` (each map branch) is instructed to back
every claim with a real retrieved source and never to fabricate
URLs, titles, or DOIs.
- `vet_sources` classifies every cited source so weak sources are
visible to the critique step.
- `critique` independently reviews the merged findings and sends weak
or uncited work back for another parallel research pass.
- `synthesize` (the `report-writer` sub-agent) is grounded: it may use
only the gathered findings and must keep each claim's inline source.
It has no tools and cannot browse the web.
- `verify_sources` probes every cited URL / DOI with an HTTP HEAD
request and reports which are unreachable, so the human reviewer
sees broken citations before approving.
## Customizing
- **Loop budget.** `MAX_REFLEXION_REVISIONS` in `reflexion_gate.py`.
- **Map concurrency.** The `research_each_question` node's
`max_concurrency: 3` caps simultaneous web-research branches.
Raise to investigate more questions in parallel; lower to be gentle
on rate-limited providers.
- **Per-node model.** Add `model: anthropic:...` to any `llm` node.
Cheap models work well for `plan` / `critique` / `vet_sources`; the
heavy intelligence is needed in `research_one_question` and the
`report-writer` sub-agent.
- **Tool scope.** Narrow the `research_one_question` node's `tools:`
list to constrain where each branch looks (for example, drop
`web_search_coyote` and `mcp:ddg-search` to force arXiv-only
research).
- **Local knowledge.** Drop files into `knowledge/` to bias every
research branch toward your local context (see the *Local
knowledge corpus* section above).
- **Different writer.** Replace `agent: report-writer` on the
`synthesize` node with the name of any other agent. The
orchestrator does not care what kind of agent the writer is.
- **Skip approval.** Point both `approve` routes at `end_accepted`,
or wire `verify_sources` straight to an `end` node.
## Files
```
assets/agents/deep-research/
graph.yaml - agent config + 17-node workflow
tools.sh - classify_source custom tool
README.md - this file
knowledge/
README.md - corpus-format notes
research-style-notes.md - starter knowledge file (replace with your notes)
scripts/
parse_request.py - _next: bootstrap_research, or ask_topic if no topic
bootstrap_research.py - fan-out source: next [plan, knowledge_lookup]
combine_findings.py - joins map output (question_findings) into findings
reflexion_gate.py - _next: research_each_question (revise) or synthesize
verify_sources.py - HTTP HEAD on cited URLs / DOIs
incorporate_feedback.py - _next: research_each_question, with user feedback
```
See also `assets/agents/report-writer/` — the sub-agent the
`synthesize` node spawns.
+291
View File
@@ -0,0 +1,291 @@
name: deep-research
description: |
Deep web research workflow. Plans an investigation, decomposes it
into sub-questions researched in parallel, grounds the work in a
local knowledge corpus, vets the credibility of cited sources, runs
a reflexion self-critique loop to revise weak or incomplete findings,
delegates the final write-up to a focused sub-agent, checks that the
cited sources are reachable, and gates the result behind human
approval. A reviewer's free-form feedback at the approval step feeds
back into another research pass.
This is the canonical Coyote graph-agent reference: it exercises every
node type (script, llm, rag, map, agent, input, approval, end) and
both static fan-out and dynamic map fan-out.
version: "1.0"
global_tools:
- web_search_coyote.sh
- fetch_url_via_curl.sh
- search_arxiv.sh
mcp_servers:
- ddg-search
conversation_starters:
- "How does HTTP/3 differ from HTTP/2?"
- "Summarize recent advances in solid-state battery chemistry"
settings:
max_loop_iterations: 40
log_state_snapshots: false
validate_before_run: true
max_concurrency: 4
initial_state:
research_feedback: ""
research_attempts: 0
local_context: ""
local_sources: ""
start: parse_request
nodes:
parse_request:
id: parse_request
type: script
script: scripts/parse_request.py
next: bootstrap_research
ask_topic:
id: ask_topic
type: input
question: "What would you like me to research?"
validation: "len(input) > 0"
state_updates:
topic: "{{input}}"
next: bootstrap_research
bootstrap_research:
id: bootstrap_research
type: script
script: scripts/bootstrap_research.py
next: [plan, knowledge_lookup]
plan:
id: plan
type: llm
instructions: |
You are a research planner. Given a topic, produce a focused
research plan and decompose it into 3-5 specific sub-questions
that can each be researched independently in parallel.
The plan is a short narrative naming the key questions and the
kinds of sources that would be authoritative. The sub-questions
are precise, self-contained queries (each one is sent on its own
to a separate research worker, so they must be answerable
without each other's context).
prompt: "Research topic: {{topic}}"
tools: []
output_schema:
type: object
properties:
research_plan:
type: string
description: A short plan narrative.
questions:
type: array
items: { type: string }
minItems: 1
maxItems: 6
description: 3-5 specific, self-contained sub-questions.
required: [research_plan, questions]
next: research_each_question
knowledge_lookup:
id: knowledge_lookup
type: rag
documents:
- ./knowledge/
query: "{{topic}}"
top_k: 6
chunk_size: 1000
chunk_overlap: 100
state_updates:
local_context: "{{output.context}}"
local_sources: "{{output.sources}}"
next: research_each_question
research_each_question:
id: research_each_question
type: map
over: "{{questions}}"
as: question
branch: research_one_question
collect_into: question_findings
max_concurrency: 3
next: combine_findings
research_one_question:
id: research_one_question
type: llm
instructions: |
You are a web research assistant. Investigate the SINGLE question
given to you using your tools: search the web, fetch and read
pages, and search arXiv for academic sources.
Rules:
- Every factual claim must be backed by a real source you
actually retrieved. Never fabricate URLs, page titles,
authors, or DOIs.
- Prefer primary and authoritative sources over aggregators.
- Where sources disagree, report the disagreement rather than
papering over it.
- Put the URL (or DOI) inline next to each claim it supports.
Return organized findings in plain text. Do not include
meta-commentary about the process.
prompt: |
Research question: {{question}}
Local context that may help:
{{local_context}}
{{research_feedback}}
tools:
- web_search_coyote
- fetch_url_via_curl
- search_arxiv
- mcp:ddg-search
max_iterations: 10
max_attempts: 2
temperature: 0.1
combine_findings:
id: combine_findings
type: script
script: scripts/combine_findings.py
next: vet_sources
vet_sources:
id: vet_sources
type: llm
instructions: |
You assess the credibility of the sources cited in a set of
research findings. For every distinct source URL in the findings,
call the `classify_source` tool to get its credibility tier. Then
summarize: which claims rest on HIGH-credibility sources, and
which rest on PREPRINT or UNVERIFIED sources and so need
corroboration. Do NOT do any new research -- assess only what is
already cited.
prompt: |
Findings to assess:
{{findings}}
tools:
- classify_source
max_iterations: 15
state_updates:
source_assessment: "{{output}}"
next: critique
critique:
id: critique
type: llm
instructions: |
You are a meticulous research reviewer. Judge whether the
findings below are good enough to synthesize a complete,
well-supported report that answers the research plan.
Mark the findings REVISE if ANY of these hold:
- A research-plan question is unanswered or only weakly
addressed.
- A factual claim has no source, or cites a source that looks
fabricated.
- The findings lean on a single source where corroboration is
needed.
- A key claim rests only on a PREPRINT or UNVERIFIED source,
per the source credibility assessment below.
- An obvious counter-perspective or recent development is
missing.
Otherwise mark them PASS.
Respond in EXACTLY this format, nothing else:
VERDICT: <PASS or REVISE>
FEEDBACK: <if REVISE, be specific and actionable -- name the gaps
and what kind of source would close them; if PASS, write "none">
prompt: |
Research plan:
{{research_plan}}
Findings under review:
{{findings}}
Source credibility assessment:
{{source_assessment}}
tools: []
state_updates:
critique: "{{output}}"
next: reflexion_gate
reflexion_gate:
id: reflexion_gate
type: script
script: scripts/reflexion_gate.py
next: synthesize
synthesize:
id: synthesize
type: agent
agent: report-writer
prompt: |
Research topic: {{topic}}
Findings (organized by sub-question, with inline citations):
{{findings}}
Source credibility assessment:
{{source_assessment}}
Produce the final report following your instructions.
timeout: 300
state_updates:
report: "{{output}}"
next: verify_sources
verify_sources:
id: verify_sources
type: script
script: scripts/verify_sources.py
next: approve
approve:
id: approve
type: approval
question: |
Research report on: {{topic}}
{{report}}
----
{{source_check}}
----
Accept this report? Pick "accept" or "reject", or type specific
feedback to send the research back for another pass.
options:
- "accept"
- "reject"
routes:
"accept": end_accepted
"reject": end_rejected
on_other: incorporate_feedback
state_updates:
decision: "{{choice}}"
incorporate_feedback:
id: incorporate_feedback
type: script
script: scripts/incorporate_feedback.py
end_accepted:
id: end_accepted
type: end
output: "{{report}}"
end_rejected:
id: end_rejected
type: end
output: "Research on '{{topic}}' was rejected and discarded."
@@ -0,0 +1,23 @@
# Local knowledge corpus for deep-research
The `knowledge_lookup` node in `graph.yaml` is a `rag` node that runs
hybrid (vector + keyword) retrieval over every file in this directory.
Drop your own notes, papers (PDFs), Markdown docs, or text files here
and they will be indexed into a per-agent knowledge base on first run.
Coyote supports common file types out of the box: `.md`, `.txt`, `.pdf`,
`.html`, and others. Subdirectories are walked recursively.
A small starter file (`research-style-notes.md`) ships so the RAG
node has something non-empty to retrieve against on a clean install.
Replace or extend it with your own materials to bias the research
phase toward your local context.
To force the knowledge base to rebuild after you add or change files,
delete the cached index:
```sh
rm ~/.config/coyote/agents/deep-research/knowledge_lookup.yaml
```
The next run will rebuild from the current contents of this directory.
@@ -0,0 +1,49 @@
# Research style notes
These are general principles the `deep-research` agent should keep in
mind regardless of topic. Replace this file with your own notes if you
want to bias retrieval toward your local context.
## What "good research" means here
- **Every factual claim cites a source you actually retrieved.** Never
fabricate URLs, page titles, authors, or DOIs.
- **Primary sources beat aggregators.** Prefer the original paper, the
RFC, the standards body, or the manufacturer over a blog summarizing
them.
- **Corroboration matters where stakes are high.** If a single source
makes a strong claim, look for a second independent source before
taking it as established.
- **Disagreement is information, not noise.** If two credible sources
disagree, report the disagreement and the reasoning on each side.
- **Old does not mean wrong.** A 2014 RFC is still authoritative if no
newer one has obsoleted it; check before assuming a source is stale.
## Source-tier heuristics
The `vet_sources` node uses these rough tiers to weigh credibility.
The custom tool `classify_source` (see `tools.sh`) implements this
deterministically by hostname / TLD.
- **HIGH:** government domains (`.gov`, `.mil`), academic institutions
(`.edu`, university subdomains), peer-reviewed journals, standards
bodies (IETF/RFCs, W3C, ISO, IEEE, NIST), and primary documents from
the entities being researched (e.g. a vendor's official spec page).
- **PREPRINT:** arXiv, bioRxiv, medRxiv, SSRN. Useful but not yet
peer-reviewed; treat numeric claims with extra caution.
- **ORGANIZATION:** established nonprofits, standards-adjacent groups,
industry consortia. Reliable for their stated mission but may have a
perspective.
- **UNVERIFIED:** general web pages, blogs, news aggregators, social
media. Useful for leads but should not be the only source for a
factual claim.
## Common pitfalls to flag in critique
- A claim cited only to a PREPRINT or UNVERIFIED source on a numeric
or contested point.
- A research-plan question that the findings address only obliquely.
- "Findings" that paraphrase a single source three times rather than
triangulating.
- Citation collisions where two sources are listed but turn out to
be the same study reported via different aggregators.
@@ -0,0 +1,18 @@
#!/usr/bin/env python3
"""Fan-out source for context loading.
Has no logic of its own. Exists so the static `next: [plan, knowledge_lookup]`
list on this node fans out into two parallel branches (the LLM planner and
the RAG knowledge lookup) as a single super-step. The validator requires
declared parallel-branch script outputs, so we emit an empty JSON object
explicitly here.
"""
import json
def main():
print(json.dumps({}))
if __name__ == "__main__":
main()
@@ -0,0 +1,39 @@
#!/usr/bin/env python3
"""Join the per-question map outputs into a single `findings` string.
The `research_each_question` map writes `question_findings` (an array,
one entry per sub-question, in input order). Downstream nodes
(`vet_sources`, `critique`, `synthesize`) read `{{findings}}` as a
single block, so this script renders the array as a Markdown document
with one section per question.
"""
import json
import os
def load_state():
path = os.environ.get("GRAPH_STATE_FILE")
if path:
with open(path) as f:
return json.load(f)
return json.loads(os.environ.get("GRAPH_STATE", "{}"))
def main():
state = load_state()
questions = state.get("questions") or []
per_question = state.get("question_findings") or []
sections = []
for idx, q in enumerate(questions):
body = per_question[idx] if idx < len(per_question) else ""
if isinstance(body, dict) or isinstance(body, list):
body = json.dumps(body, indent=2)
sections.append(f"## {q}\n\n{body}")
findings = "\n\n".join(sections) if sections else "No findings gathered."
print(json.dumps({"findings": findings}))
if __name__ == "__main__":
main()
@@ -0,0 +1,41 @@
#!/usr/bin/env python3
"""Fold a reviewer's free-form feedback back into the research loop.
Runs when the user answers the approval step with their own text
instead of "accept" or "reject". That text (saved by the approval node
as `decision`) becomes `research_feedback`, and the graph loops back to
`research_each_question` for another informed pass (each sub-question is
re-researched in parallel with the new feedback in context). The
reflexion counter is reset so the user-driven pass gets a fresh revision
budget.
Routing (`_next`): always research_each_question.
"""
import json
import os
def load_state():
path = os.environ.get("GRAPH_STATE_FILE")
if path:
with open(path) as f:
return json.load(f)
return json.loads(os.environ.get("GRAPH_STATE", "{}"))
def main():
state = load_state()
feedback = (state.get("decision") or "").strip()
output = {
"_next": "research_each_question",
"research_attempts": 0,
"research_feedback": (
"The user reviewed the report and asked for changes. Treat "
"this as the top priority for the next pass:\n\n" + feedback
),
}
print(json.dumps(output))
if __name__ == "__main__":
main()
@@ -0,0 +1,35 @@
#!/usr/bin/env python3
"""Entry router for deep-research.
Reads the caller's prompt from state. If it contains a usable research
topic, stores it as `topic` and falls through to the static `next`
(plan). If the prompt is empty, routes to `ask_topic` so the user can
supply one interactively.
Routing (`_next`):
- prompt present -> (no _next; static next: plan)
- prompt empty -> ask_topic
"""
import json
import os
def load_state():
path = os.environ.get("GRAPH_STATE_FILE")
if path:
with open(path) as f:
return json.load(f)
return json.loads(os.environ.get("GRAPH_STATE", "{}"))
def main():
state = load_state()
prompt = (state.get("initial_prompt") or "").strip()
if prompt:
print(json.dumps({"topic": prompt}))
else:
print(json.dumps({"_next": "ask_topic"}))
if __name__ == "__main__":
main()
@@ -0,0 +1,76 @@
#!/usr/bin/env python3
"""Reflexion gate for deep-research.
Runs after `critique` has reviewed the current research findings. If the
critique's verdict is REVISE and the reflexion budget is not spent,
loops back to `research` with the critique attached as
`research_feedback`, so the retry is informed rather than a blind
re-run. Otherwise it proceeds to `synthesize`.
Routing (`_next`):
- verdict PASS -> synthesize
- verdict REVISE, budget remaining -> research_each_question (+ research_feedback)
- verdict REVISE, budget spent -> synthesize
Reflexion is a best-effort quality booster, not a hard gate: once the
budget is spent the workflow proceeds anyway, and the human approval
step is the final backstop.
"""
import json
import os
import re
# Automated revision passes allowed. `research` runs at most
# MAX_REFLEXION_REVISIONS + 1 times per user pass. Bump to allow more.
MAX_REFLEXION_REVISIONS = 2
def load_state():
path = os.environ.get("GRAPH_STATE_FILE")
if path:
with open(path) as f:
return json.load(f)
return json.loads(os.environ.get("GRAPH_STATE", "{}"))
def as_int(value, default=0):
try:
return int(value)
except (TypeError, ValueError):
return default
def parse_verdict(critique):
"""Pull PASS/REVISE from the critique's `VERDICT:` line. Defaults to
PASS when no verdict line is found, so a malformed critique lets the
workflow proceed instead of burning the whole revision budget."""
match = re.search(r"VERDICT:\s*([A-Za-z]+)", critique, re.IGNORECASE)
if not match:
return "PASS"
return match.group(1).upper()
def main():
state = load_state()
critique = state.get("critique") or ""
verdict = parse_verdict(critique)
attempts = as_int(state.get("research_attempts"))
if verdict == "REVISE" and attempts < MAX_REFLEXION_REVISIONS:
feedback = (
"A reviewer judged the previous research pass incomplete. "
"Address every point in the critique below:\n\n" + critique
)
output = {
"_next": "research_each_question",
"research_attempts": attempts + 1,
"research_feedback": feedback,
}
else:
output = {"_next": "synthesize"}
print(json.dumps(output))
if __name__ == "__main__":
main()
@@ -0,0 +1,69 @@
#!/usr/bin/env python3
"""Check that the sources cited in the research report are reachable.
Scans the final report for URLs and DOIs, probes each with a HEAD
request, and writes a `source_check` summary into state so the human
reviewer sees broken citations at the approval step.
Times out per request so a slow source cannot stall the graph.
"""
import json
import os
import re
import urllib.error
import urllib.request
DOI_RE = re.compile(r"\b(10\.\d{4,9}/[-._;()/:A-Z0-9]+)", re.IGNORECASE)
URL_RE = re.compile(r"https?://[^\s)\]\}\"'>]+")
def load_state():
path = os.environ.get("GRAPH_STATE_FILE")
if path:
with open(path) as f:
return json.load(f)
return json.loads(os.environ.get("GRAPH_STATE", "{}"))
def reachable(url, timeout=5.0):
req = urllib.request.Request(url, method="HEAD")
try:
with urllib.request.urlopen(req, timeout=timeout) as resp:
return 200 <= resp.status < 400
except urllib.error.HTTPError as e:
return 200 <= e.code < 400
except Exception:
return False
def main():
state = load_state()
report = state.get("report") or ""
urls = sorted({u.rstrip(".,;)") for u in URL_RE.findall(report)})
dois = sorted(set(DOI_RE.findall(report)))
results = []
for url in urls:
ok = reachable(url)
results.append(f" {'OK' if ok else 'UNREACHABLE'} {url}")
for doi in dois:
url = f"https://doi.org/{doi}"
if url in urls:
continue
ok = reachable(url)
results.append(f" {'OK' if ok else 'UNREACHABLE'} DOI {doi} ({url})")
if not results:
summary = "No web sources were cited in the report."
else:
summary = (
f"Source reachability ({len(results)} checked):\n"
+ "\n".join(results)
)
print(json.dumps({"source_check": summary}))
if __name__ == "__main__":
main()
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
set -e
# @env LLM_OUTPUT=/dev/stdout The output path
# @cmd Classify the credibility tier of a web source from its URL.
# A deterministic check based on the host and top-level domain. Use it
# to weigh how much trust to place in a source before relying on it.
# @option --url! The full source URL to classify
classify_source() {
# shellcheck disable=SC2154
local url="$argc_url"
local host="${url#*://}"
host="${host%%/*}"
host="${host##*@}"
host="${host%%:*}"
host="$(printf '%s' "$host" | tr '[:upper:]' '[:lower:]')"
local tier
case "$host" in
'')
tier="UNKNOWN - no host could be parsed from the URL" ;;
*.gov | *.gov.* | *.mil)
tier="HIGH - government source" ;;
*.edu | *.edu.* | *.ac.*)
tier="HIGH - academic institution" ;;
arxiv.org | *.arxiv.org | biorxiv.org | *.biorxiv.org | medrxiv.org | *.medrxiv.org | ssrn.com | *.ssrn.com)
tier="PREPRINT - not yet peer reviewed, corroborate before citing" ;;
wikipedia.org | *.wikipedia.org)
tier="TERTIARY - encyclopedia, good for orientation not citation" ;;
*.org | *.org.*)
tier="MEDIUM - organization site, check for institutional bias" ;;
*)
tier="UNVERIFIED - general web source, corroborate before citing" ;;
esac
printf '%s: %s\n' "${host:-<none>}" "$tier" >> "$LLM_OUTPUT"
}
+1 -1
View File
@@ -2,6 +2,6 @@
This agent serves as a demo to guide agent development and showcase various agent capabilities.
To enable tools, Loki will look for the first `tools.py` or `tools.sh` file it finds in this directory.
To enable tools, Coyote will look for the first `tools.py` or `tools.sh` file it finds in this directory.
The base configuration using `tools.py`. To switch to using `tools.sh`, rename or remove `tools.py`.
+37
View File
@@ -0,0 +1,37 @@
# Explore
An AI agent specialized in exploring codebases, finding patterns, and understanding project structures.
This agent is designed to be delegated to by the **[Sisyphus](../sisyphus/README.md)** agent to gather information and context. Sisyphus
acts as the coordinator/architect, while Explore handles the research and discovery phase.
It can also be used as a standalone tool for understanding codebases and finding specific information.
## Features
- 🔍 Deep codebase exploration and pattern matching
- 📂 File system navigation and content analysis
- 🧠 Context gathering for complex tasks
- 🛡️ Read-only operations for safe investigation
## Pro-Tip: Use an IDE MCP Server for Improved Performance
Many modern IDEs now include MCP servers that let LLMs perform operations within the IDE itself and use IDE tools. Using
an IDE's MCP server dramatically improves the performance of coding agents. So if you have an IDE, try adding that MCP
server to your config (see the [MCP Server docs](https://github.com/Dark-Alex-17/loki/wiki/MCP-Servers) to see how to configure
them), and modify the agent definition to look like this:
```yaml
# ...
mcp_servers:
- jetbrains # The name of your configured IDE MCP server
global_tools:
- fs_read.sh
- fs_grep.sh
- fs_glob.sh
- fs_ls.sh
- web_search_coyote.sh
# ...
```
+124
View File
@@ -0,0 +1,124 @@
name: explore
description: Fast codebase exploration agent - finds patterns, structures, and relevant files. Designed to be fanned out in parallel by orchestrators — scale to the number of distinct search angles the task requires.
version: 3.1.0
skills_enabled: true
enabled_skills:
- ai-slop-remover
variables:
- name: project_dir
description: Project directory to explore
default: '.'
- name: auto_confirm
description: Auto-confirm command execution
default: '1'
mcp_servers:
- ddg-search
global_tools:
- web_search_coyote.sh
- ast_grep.sh
- fs_read.sh
- fs_cat.sh
- fs_grep.sh
- fs_glob.sh
- fs_ls.sh
instructions: |
You are a codebase explorer. Your job: Search, find, report. Nothing else.
## Step 0: Load your skills
At the start of every exploration, call `skill__load` for `ai-slop-remover`. Your findings go directly into the orchestrator's synthesis, so concise, slop-free output is the contract. Apply the skill's standards to your final findings block:
- No filler ("It's important to note that…", "Let me explain…"). Just the finding.
- No flattery, no padding, no status updates about your process.
- No multi-paragraph commentary — bullet points with code snippets are enough.
## You may be one of many parallel explorers
Orchestrators (like Sisyphus) fan out as many explore agents as the task warrants — one per distinct search angle, module boundary, or concern. You may be one of many running in parallel. Assume you are ONE narrow slice of a larger investigation. Stay strictly within YOUR slice as defined by the prompt — don't broaden scope to cover what other parallel explorers might be handling.
If the prompt says "find auth middleware", you find auth middleware. You do NOT also tour the routing layer, the error system, and the database connection pool. Narrow scope is the contract.
## Investigation methodology
Before searching, build a quick mental model. Then narrow in. Then read.
1. **Frame the question.** What kind of artifact am I looking for? Symbols (struct/class/function)? File patterns? Configuration? Implementation details? Tests? Different artifact kinds use different tools.
2. **Find first, read second.** Never `fs_read` a file without knowing why you're reading it.
3. **Build a directory mental model with `fs_ls` and `fs_glob`** — `fs_ls src/` to see what's there; `fs_glob '**/*.rs' src/` to see which files exist by name.
4. **Locate symbols with `fs_grep`** — for finding where things live across the codebase. `fs_grep --pattern "fn handle_request" --include "*.rs"` is faster than reading files.
4b. **Match code STRUCTURE with `ast_grep`** — when text grep is too noisy or formatting-dependent. It matches syntax trees: `ast_grep --pattern '$X.unwrap()' --lang rust` finds every unwrap call however it's formatted; `ast_grep --pattern 'fn $NAME($$$) { $$$ }' --lang rust --glob 'src/**'` finds function definitions; `ast_grep --pattern 'useEffect($$$)' --lang tsx` finds hook usages that a text grep for "useEffect" would bury in comments and strings. Meta-variables: `$NAME` = one AST node, `$$$` = zero or more. The pattern must be a COMPLETE, valid AST node for `--lang` — `fn $NAME($$$)` without a body parses as nothing and matches nothing. Use `fs_grep` for plain text, comments, strings, and config files; `ast_grep` for calls, definitions, and signatures. If ast-grep isn't installed the tool says so — fall back to fs_grep.
5. **Read targeted sections with `fs_read --offset/--limit`** — `fs_read --path "src/main.rs" --offset 50 --limit 30` reads lines 50-79 only. `fs_read` adds line numbers but TRUNCATES long lines (over 2000 chars) and caps output at 2000 lines by default.
6. **Use `fs_cat` only when you need the full untruncated file** — rare in exploration. If you reach for `fs_cat`, ask whether `fs_grep` + targeted `fs_read` would answer your question with less context spend.
7. **Never read entire large files** — for files 500+ lines, read the relevant section only.
## Available actions
- `fs_grep --pattern "struct User" --include "*.rs"` — find content across files in a directory tree
- `fs_grep --pattern "TODO" --path "src/main.rs"` — find content within a single file (--include is ignored in this mode)
- `ast_grep --pattern 'impl $TRAIT for $TYPE' --lang rust` — find code by STRUCTURE, not text (see 4b above)
- `fs_glob --pattern "*.rs" --path src/` — find files by name pattern
- `fs_read --path "src/main.rs"` — read a TRUNCATED view with line numbers (default 2000 lines, lines over 2000 chars cut off)
- `fs_read --path "src/main.rs" --offset 100 --limit 50` — read lines 100-149 only (line numbers; truncation rules still apply)
- `fs_cat --path "src/main.rs"` — read the FULL untruncated file (no line numbers); use only when you actually need every line
- `fs_ls --path "src/"` — list directory contents
## When to use the web (ddg-search MCP)
Rarely. You are a CODEBASE explorer, not a web researcher. Use the web only when the codebase references an external library/framework whose documented behavior is the answer to the question (e.g., "how does Tokio's #[tokio::main] expand"), and the answer isn't in the local code. For internal questions ("how does OUR auth work"), grep the codebase — never the web.
## Output format
Always end your response with a structured findings block. Sisyphus reads this verbatim and may paste sections directly into delegation prompts for a coder agent, so the structure matters:
```
FINDINGS:
- [One-line concrete fact about what you found]
- [Another one-line fact]
- Relevant files: [list of paths, no commentary]
Code patterns (paste actual lines):
- From `path/to/file.ext` lines N-M:
<5-20 lines of actual code that show the pattern>
- From `path/to/other.ext` lines N-M:
<another snippet>
Open questions (only if any):
- [Anything you couldn't determine and the orchestrator should clarify or delegate elsewhere]
EXPLORE_COMPLETE
```
Pasting actual code lines (5-20 per pattern) lets the orchestrator hand snippets directly to a coder agent without re-exploration. That is the entire point of your existence in a parallel research phase. File paths alone make downstream delegation impossible — the coder would have to re-do your work.
## Rules
1. **Be fast.** Don't read every file, read representative ones.
2. **Stay in your slice.** Narrow scope is the contract.
3. **Be concise.** Report findings, not your process. Apply the `ai-slop-remover` skill to your output.
4. **Never modify files.** You are read-only.
5. **Limit reads.** Target around 5 file reads per exploration; go higher only when the question genuinely requires it.
6. **Paste code snippets.** File paths alone make downstream delegation impossible.
7. **Report what you didn't find.** If the prompt asked for X and X doesn't exist in your slice, say so explicitly — don't pad your findings with adjacent material to hide the gap.
## Context
- Project: {{project_dir}}
- CWD: {{__cwd__}}
## Available tools:
{{__tools__}}
conversation_starters:
- 'Find how authentication is implemented'
- 'What patterns are used for API endpoints'
- 'Show me the project structure'
+175
View File
@@ -0,0 +1,175 @@
#!/usr/bin/env bash
set -eo pipefail
# shellcheck disable=SC1090
source "$LLM_PROMPT_UTILS_FILE"
source "$LLM_ROOT_DIR/agents/.shared/utils.sh"
# @env LLM_OUTPUT=/dev/stdout
# @env LLM_AGENT_VAR_PROJECT_DIR=.
# @describe Explore agent tools for codebase search and analysis
_project_dir() {
local dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}"
(cd "${dir}" 2>/dev/null && pwd) || echo "${dir}"
}
# Normalize a path to be relative to project root.
# Strips the project_dir prefix if the LLM passes an absolute path.
_normalize_path() {
local input_path="$1"
local project_dir
project_dir=$(_project_dir)
if [[ "${input_path}" == /* ]]; then
input_path="${input_path#"${project_dir}"/}"
fi
input_path="${input_path#./}"
echo "${input_path}"
}
# @cmd Get project structure and layout
get_structure() {
local project_dir
project_dir=$(_project_dir)
info "Project structure:" >> "$LLM_OUTPUT"
echo "" >> "$LLM_OUTPUT"
local project_info
project_info=$(detect_project "${project_dir}")
{
echo "Type: $(echo "${project_info}" | jq -r '.type')"
echo ""
get_tree "${project_dir}" 3
} >> "$LLM_OUTPUT"
}
# @cmd Search for files by name pattern
# @option --pattern! File name pattern (e.g., "*.rs", "config*", "*test*")
search_files() {
# shellcheck disable=SC2154
local pattern="${argc_pattern}"
local project_dir
project_dir=$(_project_dir)
info "Files matching: ${pattern}" >> "$LLM_OUTPUT"
echo "" >> "$LLM_OUTPUT"
local results
results=$(_search_files "${pattern}" "${project_dir}")
if [[ -n "${results}" ]]; then
echo "${results}" >> "$LLM_OUTPUT"
else
warn "No files found" >> "$LLM_OUTPUT"
fi
}
# @cmd Search for content in files
# @option --pattern! Text or regex pattern to search for
# @option --file-type Filter by file extension (e.g., "rs", "py", "ts")
search_content() {
local pattern="${argc_pattern}"
local file_type="${argc_file_type:-}"
local project_dir
project_dir=$(_project_dir)
info "Searching: ${pattern}" >> "$LLM_OUTPUT"
echo "" >> "$LLM_OUTPUT"
local include_arg=""
if [[ -n "${file_type}" ]]; then
include_arg="--include=*.${file_type}"
fi
local results
# shellcheck disable=SC2086
results=$(grep -rn ${include_arg} "${pattern}" "${project_dir}" 2>/dev/null | \
grep -v '/target/' | \
grep -v '/node_modules/' | \
grep -v '/.git/' | \
grep -v '/dist/' | \
sed "s|^${project_dir}/||" | \
head -30) || true
if [[ -n "${results}" ]]; then
echo "${results}" >> "$LLM_OUTPUT"
else
warn "No matches found" >> "$LLM_OUTPUT"
fi
}
# @cmd Read a file's contents
# @option --path! Path to the file (relative to project root)
# @option --lines Maximum lines to read (default: 200)
read_file() {
local file_path
# shellcheck disable=SC2154
file_path=$(_normalize_path "${argc_path}")
local max_lines="${argc_lines:-200}"
local project_dir
project_dir=$(_project_dir)
local full_path="${project_dir}/${file_path}"
if [[ ! -f "${full_path}" ]]; then
error "File not found: ${file_path}" >> "$LLM_OUTPUT"
return 1
fi
{
info "File: ${file_path}"
echo ""
} >> "$LLM_OUTPUT"
head -n "${max_lines}" "${full_path}" >> "$LLM_OUTPUT"
local total_lines
total_lines=$(wc -l < "${full_path}")
if [[ "${total_lines}" -gt "${max_lines}" ]]; then
echo "" >> "$LLM_OUTPUT"
warn "... truncated (${total_lines} total lines)" >> "$LLM_OUTPUT"
fi
}
# @cmd Find similar files to a given file (for pattern matching)
# @option --path! Path to the reference file
find_similar() {
local file_path
file_path=$(_normalize_path "${argc_path}")
local project_dir
project_dir=$(_project_dir)
local ext="${file_path##*.}"
local dir
dir=$(dirname "${file_path}")
info "Files similar to: ${file_path}" >> "$LLM_OUTPUT"
echo "" >> "$LLM_OUTPUT"
local results
results=$(find "${project_dir}/${dir}" -maxdepth 1 -type f -name "*.${ext}" \
! -name "$(basename "${file_path}")" \
! -name "*test*" \
! -name "*spec*" \
2>/dev/null | sed "s|^${project_dir}/||" | head -5)
if [[ -n "${results}" ]]; then
echo "${results}" >> "$LLM_OUTPUT"
else
results=$(find "${project_dir}" -type f -name "*.${ext}" \
! -name "$(basename "${file_path}")" \
! -name "*test*" \
-not -path '*/target/*' \
2>/dev/null | sed "s|^${project_dir}/||" | head -5)
if [[ -n "${results}" ]]; then
echo "${results}" >> "$LLM_OUTPUT"
else
warn "No similar files found" >> "$LLM_OUTPUT"
fi
fi
}
+35
View File
@@ -0,0 +1,35 @@
# File Reviewer
A specialized worker agent that reviews a single file's diff for bugs, style issues, and cross-cutting concerns.
This agent is designed to be spawned by the **[Code Reviewer](../code-reviewer/README.md)** agent. It focuses deeply on
one file while communicating with sibling agents to catch issues that span multiple files.
## Features
- 🔍 **Deep Analysis**: Focuses on bugs, logic errors, security issues, and style problems in a single file.
- 🗣️ **Teammate Communication**: Sends and receives alerts to/from sibling reviewers about interface or dependency
changes.
- 🎯 **Targeted Reading**: Reads only relevant context around changed lines to stay efficient.
- 🏷️ **Structured Findings**: Categorizes issues by severity (🔴 Critical, 🟡 Warning, 🟢 Suggestion, 💡 Nitpick).
## Pro-Tip: Use an IDE MCP Server for Improved Performance
Many modern IDEs now include MCP servers that let LLMs perform operations within the IDE itself and use IDE tools. Using
an IDE's MCP server dramatically improves the performance of coding agents. So if you have an IDE, try adding that MCP
server to your config (see the [MCP Server docs](https://github.com/Dark-Alex-17/coyote/wiki/MCP-Servers) to see how to configure
them), and modify the agent definition to look like this:
```yaml
# ...
mcp_servers:
- jetbrains # The name of your configured IDE MCP server
global_tools:
- fs_read.sh
- fs_grep.sh
- fs_glob.sh
# ...
```
+144
View File
@@ -0,0 +1,144 @@
name: file-reviewer
description: Reviews a single file's diff for bugs, style issues, and cross-cutting concerns
version: 2.3.0
skills_enabled: true
enabled_skills:
- code-review
- ai-slop-remover
- transactional-integrity
- logging-discipline
- rest-api-review
- cli-review
- library-review
- worker-review
- iac-review
- migration-review
- cicd-review
variables:
- name: project_dir
description: Project directory for context
default: '.'
- name: auto_confirm
description: Auto-confirm command execution
default: '1'
global_tools:
- fs_read.sh
- fs_grep.sh
- fs_glob.sh
- fs_cat.sh
- fs_ls.sh
instructions: |
You are a precise code reviewer. You review ONE file's diff and produce structured findings.
## Step 0: Load review skills
Before reading any code, call `skill__load` for `code-review` and `ai-slop-remover`. They carry your detailed review methodology — the categories to check (correctness, tests, clarity, coupling, footguns), the investigation workflow (how to use the fs tools to build context before reviewing), the slop checklist (useless comments, dishonest naming, defensive handling of impossible cases), and the standard for when to flag vs. skip.
Additionally load `transactional-integrity` when the diff touches state-changing code — database writes, transaction blocks, queue/webhook/job handlers, retry logic, or calls to external state-holding systems. It carries the atomicity/race/idempotency/dual-write checklist that generic correctness review misses. Skip it for pure reads, UI, and stateless computation.
Also load `logging-discipline` when the diff touches boundaries, error paths, background jobs, or state transitions. It carries the under-/over-logging checks (silent new failure paths, log-and-rethrow duplication, register mismatches, deleted log lines operators may grep for). Skip it for diffs with no operational surface.
Apply every loaded checklist in every review. Skill bodies are your source of truth for what to flag; this agent's instructions handle workflow and output shape.
## Your Mission
You receive a git diff for a single file. Your job:
1. Load the review skills (above).
2. Analyze the diff applying both skill checklists.
3. Read surrounding code for context using the skill's investigation workflow.
4. Check your inbox for cross-cutting alerts from sibling reviewers.
5. Send alerts to siblings if you spot cross-file issues.
6. Return structured findings in the format below.
## Input
You receive:
- The file path being reviewed
- The git diff for that file
- A sibling roster (other file-reviewers and which files they're reviewing)
## Cross-Cutting Alerts (Teammate Pattern)
After analyzing your file, check if changes might affect sibling files:
- **Interface changes**: If a function signature changed, alert siblings reviewing callers
- **Type changes**: If a type/struct changed, alert siblings reviewing files that use it
- **Import changes**: If exports changed, alert siblings reviewing importers
- **Config changes**: Alert all siblings if config format changed
To alert a sibling:
```
agent__send_message --to <sibling_agent_id> --message "ALERT: <description of cross-file concern>"
```
Check your inbox periodically for alerts from siblings:
```
agent__check_inbox
```
If you receive an alert, incorporate it into your findings under a "Cross-File Concerns" section.
## File Reading Limits
The `code-review` skill teaches the investigation workflow. Apply these per-review caps on top:
- **Max 5 fs_read calls per review.** Be deliberate about which files you read.
- **`fs_read` returns a TRUNCATED view** with line numbers (long lines cut at 2000 chars, output capped at 2000 lines by default). Use `--offset` and `--limit` (default 50 lines of context) to target specific sections. Never read entire large files.
- **Use `fs_cat` only when you genuinely need the full untruncated file** — for a diff review this should be rare; `fs_grep` + targeted `fs_read` usually answers the question with less context.
- **Focus on the diff.** Read surrounding code only when needed to evaluate the change; do not audit unrelated code in the same file.
## Output Format
Structure your response EXACTLY as:
```
## File: <file_path>
### Summary
<1-2 sentence summary of the changes>
### Findings
#### <finding_title>
- **Severity**: 🔴 CRITICAL | 🟡 WARNING | 🟢 SUGGESTION | 💡 NITPICK
- **Lines**: <start_line>-<end_line>
- **Description**: <clear explanation of the issue>
- **Suggestion**: <how to fix it>
#### <next_finding_title>
...
### Cross-File Concerns
<any issues received from siblings or that you alerted siblings about>
<"None" if no cross-file concerns>
REVIEW_COMPLETE
```
## Severity Tag Mapping
Translate the skill's category findings to the output severity:
- **🔴 CRITICAL** — Correctness bugs, security vulnerabilities, data loss risks, crashes
- **🟡 WARNING** — Logic errors, race conditions, missing error handling, performance issues with user-visible impact
- **🟢 SUGGESTION** — Clarity, coupling, naming, footgun mitigations, missing tests for the change
- **💡 NITPICK** — Style if no formatter enforces it, minor naming, slop-remover findings on prose-style comments
### The `[convention]` / `[correctness]` marker
Finding titles may optionally carry a `[convention]` or `[correctness]` marker (e.g. `#### [convention] Collection endpoint without pagination`). Emit a marker only when a loaded skill instructs you to: `[convention]` tags contract/convention-adherence findings, `[correctness]` tags contract-breaking findings such as a semver violation or an exit-code inversion. The marker rides in the title verbatim and changes nothing about how you assign severity — folding and rejection semantics live downstream in the orchestrators, not here. The severity mapping above is unchanged.
## Rules
1. **Be specific.** Reference exact line numbers and code.
2. **Be actionable.** Every finding must have a suggestion.
3. **Never modify files.** You are read-only.
4. **Always end with REVIEW_COMPLETE.**
## Context
- Project: {{project_dir}}
- CWD: {{__cwd__}}
## Available Tools:
{{__tools__}}
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env bash
set -eo pipefail
# shellcheck disable=SC1090
source "$LLM_PROMPT_UTILS_FILE"
source "$LLM_ROOT_DIR/agents/.shared/utils.sh"
# @env LLM_OUTPUT=/dev/stdout
# @env LLM_AGENT_VAR_PROJECT_DIR=.
# @describe File reviewer tools for single-file code review
_project_dir() {
local dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}"
(cd "${dir}" 2>/dev/null && pwd) || echo "${dir}"
}
# @cmd Get project structure to understand codebase layout
get_structure() {
local project_dir
project_dir=$(_project_dir)
info "Project structure:" >> "$LLM_OUTPUT"
echo "" >> "$LLM_OUTPUT"
local project_info
project_info=$(detect_project "${project_dir}")
{
echo "Type: $(echo "${project_info}" | jq -r '.type')"
echo ""
get_tree "${project_dir}" 2
} >> "$LLM_OUTPUT"
}
+77
View File
@@ -0,0 +1,77 @@
# Gatekeeper
A **plan self-containedness gate**. Audits a plan against the "sealed container" standard before it
is finalized:
> A context-free LLM implementer must be able to execute the plan using ONLY what is on the page —
> every question it will hit mid-implementation is either **answered inline** or **delegated via a
> verified pointer** to the exact code/docs where the answer lives.
Where [`plan-review`](../../skills/plan-review/SKILL.md) (via `oracle`) judges the *approach*
(executability, verifiability, ordering), `gatekeeper` audits the *context*: does the implementer
know where infrastructure code goes, what DB tech to use (RDS vs in-cluster Postgres), which
directory layout to mirror, what commands verify the work — or at least where to look?
## The three review gates
| Gate | Agent | Question | When |
|------|-------|----------|------|
| Self-containedness | `gatekeeper` | "Can a context-free LLM implement from this file alone?" | Before the plan is finalized |
| Executability | `oracle` + `plan-review` | "Is the approach sound, verifiable, correctly ordered?" | Before the plan is promoted |
| Conformance | [`adversary`](../adversary/README.md) | "Is the built code what the plan asked for?" | After implementation |
## How it audits
Driven by the [`plan-gatekeeping`](../../skills/plan-gatekeeping/SKILL.md) skill:
1. Walks a 10-category manifest: code placement, infrastructure, data layer, interfaces/contracts,
conventions/tooling, testing/verification, dependencies/ordering, config/secrets, scope
boundaries, settled decisions.
2. For each category: answered inline, delegated via pointer, or **missing**.
3. **Verifies every pointer** with read-only tools — the path exists AND actually covers the claimed
topic. A pointer to a file that never mentions the topic is a leak wearing a pointer costume.
4. Phrases each gap as the question the implementer would actually ask, tagged **BLOCKING** (will
guess wrong) or **FRICTION** (will waste time rediscovering).
## Verdict (blocking)
```
PLAN_GATE: SEALED
Categories audited: N applicable, all answered or pointed.
```
```
PLAN_GATE: LEAKY
Missing questions (N):
1. [infrastructure] Where do I put the Terraform for the new service DB — infra/rds/ or a separate repo? — BLOCKING — plan says "provision a database" with no target — add inline: "RDS via infra/rds/, mirror rate_cards.tf"
Broken pointers (if any):
- "see docs/db.md for conventions" — path missing
```
`LEAKY` blocks finalization. The caller (typically `architect`) answers the questions — by exploring
the code repos, reading docs, or asking the user — amends the plan, and re-submits to the SAME
gatekeeper session until it seals.
## Usage
Spawned by `architect` during design-doc decomposition (Phase B/C), before the `oracle` plan-review:
```sh
agent__spawn --agent gatekeeper --prompt "Audit this plan for self-containedness. Return SEALED/LEAKY.
Plan: <plans_dir>/PLAN-<slug>.md
Target project: <project_dir>"
```
Ad-hoc use against any plan file:
```sh
coyote -a gatekeeper --agent-variable project_dir ~/code/my-service \
"Audit plans/PLAN-my-feature.md for self-containedness"
```
## Related
- [`plan-gatekeeping`](../../skills/plan-gatekeeping/SKILL.md) — the manifest + methodology it runs on.
- [`architect`](../architect/README.md) — the orchestrator that gates plans through it.
- [`adversary`](../adversary/README.md) — the post-implementation conformance counterpart.
+102
View File
@@ -0,0 +1,102 @@
name: gatekeeper
description: Plan self-containedness gate - audits a plan against the "sealed container" standard (every implementer question answered inline or via a verified pointer to code/docs) and returns a blocking PLAN_GATE SEALED/LEAKY verdict with the missing questions. Designed to be delegated to by architect before plans are finalized.
version: 2.0.0
auto_continue: true
max_auto_continues: 15
inject_todo_instructions: true
skills_enabled: true
enabled_skills:
- plan-gatekeeping
variables:
- name: project_dir
description: Absolute path to the project the plan targets - the ground truth for pointer verification
default: '.'
- name: auto_confirm
description: Auto-confirm command execution
default: '1'
global_tools:
- ast_grep.sh
- fs_read.sh
- fs_cat.sh
- fs_grep.sh
- fs_glob.sh
- fs_ls.sh
instructions: |
You are the plan gatekeeper. You audit ONE plan for **self-containedness** before it is finalized:
the "sealed container" test. A context-free LLM implementer must be able to execute the plan using
ONLY what is on the page — every question it will hit mid-implementation must be answered inline or
delegated via a verified pointer to the exact code/docs where the answer lives. Your output is the
list of questions the plan FAILS to answer, and a blocking verdict.
You are NOT the approach reviewer (`plan-review` judges executability/verifiability of the design).
You audit completeness of CONTEXT. A brilliant approach with no answer to "where does the infra
code go?" or "managed RDS or an in-cluster Postgres container?" fails your gate.
## Step 0: Load the skill
Before anything else, `skill__load` `plan-gatekeeping`. It carries your methodology: the
answer-or-pointer rule, the 10-category manifest (code placement, infrastructure, data layer,
interfaces, conventions, testing, dependencies, config/secrets, scope, settled decisions), pointer
verification, severity tagging, and the exact verdict format. The skill body is your source of
truth; these instructions handle workflow and I/O.
## Input (the spawn prompt IS your entire context)
You are given a plan to audit — pasted inline or as a path to read. You may also be told which
project the plan targets; default ground truth is {{project_dir}}. Any other local repos/docs the
plan points into are readable for pointer verification.
If no plan is provided, STOP and say so.
## Workflow
1. Load `plan-gatekeeping`.
2. Read the plan in full (`fs_cat` for the whole file — do not audit a truncated view).
3. Walk EVERY manifest category. For each: answered inline, delegated via pointer, or MISSING.
Mark inapplicable categories explicitly.
4. Verify every pointer with the read-only tools: the path exists AND the target actually covers
the claimed topic. Check "mirror the layout of X" claims against X itself.
5. Phrase each gap as the QUESTION the implementer would actually ask, tag it BLOCKING or
FRICTION, and suggest the fix — an inline answer or a pointer you have VERIFIED resolves.
6. Emit the verdict in the skill's exact format.
## Output — verdict (MANDATORY, exact format)
End with EXACTLY one of these sentinels so the caller can route on it:
```
PLAN_GATE: SEALED
Categories audited: N applicable, all answered or pointed.
```
```
PLAN_GATE: LEAKY
Missing questions (N):
1. [category] <implementer's actual question> — [BLOCKING|FRICTION] — <why they get stuck> — <suggested fix>
Broken pointers (if any):
- <pointer> — <path missing | doesn't cover topic>
```
## Rules
1. **You are read-only.** Never modify the plan. You produce questions; the author owns the fixes.
2. **Questions, not complaints.** "Infra section is thin" is noise. "Where do I put the Terraform
for the new database — {{project_dir}}/infra/ or a separate repo?" is signal.
3. **Verify every pointer you check AND every pointer you suggest.** Recommending an unverified
pointer is the same leak you exist to catch.
4. **BLOCKING findings always mean LEAKY.** Only-FRICTION findings: note the caller may seal at
their discretion.
5. **Do not re-litigate the approach.** Coherent-but-underdocumented means the fix is context.
6. Be terse and decisive. Three BLOCKING questions beat fifteen nitpicks.
## Context
- Project (ground truth): {{project_dir}}
- CWD: {{__cwd__}}
## Available Tools
{{__tools__}}
-32
View File
@@ -1,32 +0,0 @@
# Jira AI Agent
## Overview
The Jira AI Agent is designed to assist with managing tasks within Jira projects, providing capabilities such as creating, searching, updating, assigning, linking, and commenting on issues. Its primary purpose is to help software engineers seamlessly integrate Jira into their workflows through an AI-driven interface.
## Configuration
### Variables
This agent accepts the following variables:
- **config**: Specifies the configuration file for the Jira CLI. This configuration should be located at `~/.config/.jira/<config_name>.yml`. Example: `work`.
- **project**: The Jira project key where operations are executed. Example: `PAN`.
### Customization
#### For a User's Specific Jira Instance
1. **Config File Setup**:
- Users must ensure there is a configuration file for their Jira instance located at `~/.config/.jira/`. The filename should match the `config` variable value provided to the agent (e.g., for `config` set to `work`, ensure a `work.yml` exists).
2. **State, Issue Type, and Priority Customization**:
- Modify the functions `_issue_type_choice` and `_issue_state_choice` in `tools.sh` to reflect the specific issue types and states used in your Jira instance.
- The `priority` for new issues can be modified directly through the `create_issue()` function in `tools.sh` with options set to the values available in your Jira instance (e.g., Medium, Highest, etc.).
## How the Agent Works
The agent works by utilizing provided variables to interact with Jira CLI commands through `tools.sh`. The `config` variable links directly to a `.yml` configuration file that contains connections settings for a Jira instance, enabling the agent to perform operations such as issue creation or status updates.
- **Configuration Linkage**: The `config` parameters specified during the execution must have a corresponding `.yml` configuration file at `~/.config/.jira/`, which contains the required Jira server details like login credentials and server URL.
- **Jira Command Execution**: The agent uses predefined functions within `tools.sh` to execute Jira operations. These functions rely on the configuration and project variable inputs to construct and execute the appropriate Jira CLI commands.
-25
View File
@@ -1,25 +0,0 @@
name: Jira Agent
description: An AI agent that can assist with Jira tasks such as creating issues, searching for issues, and updating issues.
version: 0.1.0
agent_session: temp
instructions: |
You are a AI agent designed to assist with managing Jira tasks and helping software engineers
utilize and integrate Jira into their workflows. You can create, search, update, assign, link, and comment on issues in Jira.
When you create issues, the general format of the issues is broken into two sections: Description, and User Acceptance Criteria. The Description section gives context and details about the issue, and the User Acceptance Criteria section provides bullet points that function like a checklist of all the things that must be completed in order for the issue to be considered done.
Create issues under the {{project}} Jira project.
Available tools:
{{__tools__}}
variables:
- name: config
description: The configuration to use for the Jira CLI; e.g. work
- name: project
description: The Jira project to operate on; e.g. PAN
conversation_starters:
- What are the latest issues in my Jira project?
- Can you create a new Jira issue for me?
- What are my open Jira issues?
- Can you search for issues with the label "bug" in my Jira project?
-259
View File
@@ -1,259 +0,0 @@
#!/usr/bin/env bash
# shellcheck disable=SC2154
# shellcheck disable=SC2046
set -e
# @meta require-tools jira
# @env LLM_OUTPUT=/dev/stdout The output path
# @env LLM_AGENT_VAR_CONFIG! The configuration to use for the Jira CLI; e.g. work
# @env LLM_AGENT_VAR_PROJECT! The Jira project to operate on; e.g. PAN
# @cmd Fetch my Jira username
get_jira_username() {
declare config_file="$HOME/.config/.jira/${LLM_AGENT_VAR_CONFIG}.yml"
jira me -c "$config_file" >> "$LLM_OUTPUT"
}
# @cmd Query for jira issues using a Jira Query Language (JQL) query
# @option --jql-query! The Jira Query Language query to execute
# @option --project! $LLM_AGENT_VAR_PROJECT <PROJECT> Jira project to operate on; e.g. PAN
query_jira_issues() {
declare config_file="$HOME"/.config/.jira/"${LLM_AGENT_VAR_CONFIG}".yml
jira issue ls \
--project "$argc_project" \
-q "$argc_jql_query" \
--plain \
-c "$config_file" >> "$LLM_OUTPUT"
}
# @cmd Assign a Jira issue to the specified user
# @option --issue-key! The Jira issue key, e.g. ISSUE-1
# @option --assignee! The email or display name of the user to assign the issue to
# @option --project! $LLM_AGENT_VAR_PROJECT <PROJECT> Jira project to operate on; e.g. PAN
assign_jira_issue() {
declare config_file="$HOME"/.config/.jira/"${LLM_AGENT_VAR_CONFIG}".yml
jira issue assign \
--project "$argc_project" \
"$argc_issue_key" "$argc_assignee" \
-c "$config_file" >> "$LLM_OUTPUT"
}
# @cmd View a Jira issue
# @option --issue-key! The Jira issue key, e.g. ISSUE-1
# @option --project! $LLM_AGENT_VAR_PROJECT <PROJECT> Jira project to operate on; e.g. PAN
view_issue() {
declare config_file="$HOME"/.config/.jira/"${LLM_AGENT_VAR_CONFIG}".yml
jira issue view \
"$argc_issue_key" \
--project "$argc_project" \
--comments 20 \
--plain \
-c "$config_file" >> "$LLM_OUTPUT"
}
# @cmd Transition a Jira issue to a different state
# @option --issue-key! The Jira issue key, e.g. ISSUE-1
# @option --state![`_issue_state_choice`] The Jira state of the issue
# @option --comment Add a comment to the issue
# @option --resolution Set resolution
# @option --project! $LLM_AGENT_VAR_PROJECT <PROJECT> Jira project to operate on; e.g. PAN
transition_issue() {
declare config_file="$HOME"/.config/.jira/"${LLM_AGENT_VAR_CONFIG}".yml
declare -a flags=()
if [[ -n $argc_comment ]]; then
flags+=("--comment '${argc_comment}'")
fi
if [[ -n $argc_resolution ]]; then
flags+=("--resolution ${argc_resolution}")
fi
jira issue move \
--project "$argc_project" \
"$argc_issue_key" "$argc_state" "$(echo "${flags[*]}" | xargs)" \
-c "$config_file" >> "$LLM_OUTPUT"
}
# @cmd Create a new Jira issue
# @option --type![`_issue_type_choice`]
# @option --summary! Issue summary or title
# @option --description! Issue description
# @option --parent-issue-key Parent issue key can be used to attach epic to an issue. And, this field is mandatory when creating a sub-task
# @option --assignee Issue assignee (username, email or display name)
# @option --fix-version* String array of Release info (fixVersions); for example: `--fix-version 'some fix version 1' --fix-version 'version 2'`
# @option --affects-version* String array of Release info (affectsVersions); for example: `--affects-version 'the first affected version' --affects-version 'v1.2.3'`
# @option --label* String array of issue labels; for example: `--label backend --label custom`
# @option --component* String array of issue components; for example: `--component backend --component core`
# @option --original-estimate The original estimate of the issue
# @option --priority[=Medium|Highest|High|Low|Lowest] The priority of the issue
# @option --project! $LLM_AGENT_VAR_PROJECT <PROJECT> Jira project to operate on; e.g. PAN
create_issue() {
declare config_file="$HOME"/.config/.jira/"${LLM_AGENT_VAR_CONFIG}".yml
declare -a flags=()
if [[ -n $argc_assignee ]]; then
flags+=("--assignee $argc_assignee")
fi
if [[ -n $argc_original_estimate ]]; then
flags+=("--original-estimate $argc_original_estimate")
fi
if [[ -n $argc_priority ]]; then
flags+=("--priority $argc_priority")
fi
if [[ -n $argc_fix_version ]]; then
for version in "${argc_fix_version[@]}"; do
flags+=("--fix-version '$version'")
done
fi
if [[ -n $argc_affects_version ]]; then
for version in "${argc_affects_version[@]}"; do
flags+=("--affects-version '$version'")
done
fi
if [[ -n $argc_components ]]; then
for component in "${argc_components[@]}"; do
flags+=("--affects-version '$component'")
done
fi
jira issue create \
--project "$argc_project" \
--type "$argc_type" \
--summary "$argc_summary" \
--body "$argc_description" \
--parent "$argc_parent_issue_key" \
-c "$config_file" \
--no-input $(echo "${flags[*]}" | xargs) >> "$LLM_OUTPUT"
}
# @cmd Link two issues together
# @option --inward-issue-key! Issue key of the source issue, eg: ISSUE-1
# @option --outward-issue-key! Issue key of the target issue, eg: ISSUE-2
# @option --issue-link-type! Relationship between two issues, eg: Duplicates, Blocks etc.
# @option --project! $LLM_AGENT_VAR_PROJECT <PROJECT> Jira project to operate on; e.g. PAN
link_issues() {
declare config_file="$HOME"/.config/.jira/"${LLM_AGENT_VAR_CONFIG}".yml
jira issue link \
--project "$argc_project" \
"${argc_inward_issue_key}" "${argc_outward_issue_key}" "${argc_issue_link_type}" \
-c "$config_file" >> "$LLM_OUTPUT"
}
# @cmd Unlink or disconnect two issues from each other, if already connected.
# @option --inward-issue-key! Issue key of the source issue, eg: ISSUE-1
# @option --outward-issue-key! Issue key of the target issue, eg: ISSUE-2.
# @option --project! $LLM_AGENT_VAR_PROJECT <PROJECT> Jira project to operate on; e.g. PAN
unlink_issues() {
declare config_file="$HOME"/.config/.jira/"${LLM_AGENT_VAR_CONFIG}".yml
jira issue unlink \
--project "$argc_project" \
"${argc_inward_issue_key}" "${argc_outward_issue_key}" \
-c "$config_file" >> "$LLM_OUTPUT"
}
# @cmd Add a comment to an issue
# @option --issue-key! Issue key of the source issue, eg: ISSUE-1
# @option --comment-body! Body of the comment you want to add
# @option --project! $LLM_AGENT_VAR_PROJECT <PROJECT> Jira project to operate on; e.g. PAN
add_comment_to_issue() {
declare config_file="$HOME"/.config/.jira/"${LLM_AGENT_VAR_CONFIG}".yml
jira issue comment add \
--project "$argc_project" \
"${argc_issue_key}" "${argc_comment_body}" \
--no-input \
-c "$config_file" >> "$LLM_OUTPUT"
}
# @cmd Edit an existing Jira issue
# @option --issue-key! The Jira issue key, e.g. ISSUE-1
# @option --parent Link to a parent key
# @option --summary Edit summary or title
# @option --description Edit description
# @option --priority Edit priority
# @option --assignee Edit assignee (email or display name)
# @option --label Append labels
# @option --project! $LLM_AGENT_VAR_PROJECT <PROJECT> Jira project to operate on; e.g. PAN
edit_issue() {
declare config_file="$HOME"/.config/.jira/"${LLM_AGENT_VAR_CONFIG}".yml
declare -a flags=()
if [[ -n $argc_parent ]]; then
flags+=("--parent $argc_parent")
fi
if [[ -n $argc_summary ]]; then
flags+=("--summary $argc_summary")
fi
if [[ -n $argc_description ]]; then
flags+=("--body $argc_description")
fi
if [[ -n $argc_priority ]]; then
flags+=("--priority $argc_priority")
fi
if [[ -n $argc_assignee ]]; then
flags+=("--assignee $argc_assignee")
fi
if [[ -n $argc_label ]]; then
flags+=("--label $argc_label")
fi
jira issue edit \
--project "$argc_project" \
"$argc_issue_key" $(echo "${flags[*]}" | xargs) \
--no-input \
-c "$config_file" >> "$LLM_OUTPUT"
}
_issue_type_choice() {
if [[ $LLM_AGENT_VAR_CONFIG == "work" ]]; then
echo "Story"
echo "Task"
echo "Bug"
echo "Technical Debt"
echo "Sub-task"
elif [[ $LLM_AGENT_VAR_CONFIG == "sideproject" ]]; then
echo "Task"
echo "Story"
echo "Bug"
echo "Epic"
fi
}
_issue_state_choice() {
if [[ $LLM_AGENT_VAR_CONFIG == "work" ]]; then
echo "Ready for Dev"
echo "CODE REVIEW"
echo "IN PROGRESS"
echo "Backlog"
echo "Done"
echo "TESTING"
elif [[ $LLM_AGENT_VAR_CONFIG == "sideproject" ]]; then
echo "IN CLARIFICATION"
echo "NEED TO CLARIFY"
echo "READY TO WORK"
echo "RELEASE BACKLOG"
echo "REOPEN"
echo "CODE REVIEW"
echo "IN PROGRESS"
echo "IN TESTING"
echo "TO TEST"
echo "DONE"
fi
}
+74
View File
@@ -0,0 +1,74 @@
# Librarian
The "external grep" sibling of [Explore](../explore/README.md). Searches the web
for authoritative external references (official docs, production OSS,
specifications), fetches them, and synthesizes findings with inline citations.
Designed to be delegated to by **[Sisyphus](../sisyphus/README.md)** — typically
fanned out 1-3 in parallel alongside `explore` agents whenever an unfamiliar
library, API, or framework is involved.
## Workflow
```mermaid
flowchart TD
triage["triage<br/>llm"] --> search
triage --> search_oss
triage -.->|"fallback"| end_failure
search["search<br/>llm + ddg-search MCP"] --> synthesize
search_oss["search_oss<br/>llm + personal-github MCP"] --> synthesize
synthesize["synthesize<br/>llm + fetch_url_via_curl"] --> final_format
final_format{"final_format<br/>script"} --> end_success
end_success(["end_success<br/>LIBRARIAN_COMPLETE"])
end_failure(["end_failure<br/>LIBRARIAN_FAILED"])
```
`triage` parses the prompt into language / doc-domain / query hints, then fans
out to `search` (authoritative docs via `ddg-search`) and `search_oss`
(production OSS examples via the `personal-github` MCP) in parallel. Both feed
into `synthesize`, which fetches each URL and produces a citation-backed
findings block. `final_format` (script) trims any LLM preamble before the
`LIBRARIAN_COMPLETE` sentinel is emitted.
Iteration 1 (this) is the happy-path MVP: single search pass, single synthesis
pass, no quality-check loop. Future iterations may add:
- `quality_check` LLM node + back-edge to `search` with a refined query if
the initial findings are thin or off-topic
- `gh` CLI / GitHub MCP integration for first-class OSS-example retrieval
- Reranking the search results before synthesis
- Cache of recently-fetched URLs across invocations
## Trigger phrases (when sisyphus should spawn it)
- "How do I use [library]?"
- "What's the best practice for [framework feature]?"
- "Why does [external dependency] behave this way?"
- "Find examples of [library] usage"
- Any unfamiliar npm/pip/cargo/crate package surfaced by the user
## Source priority
1. Official documentation (docs.X.org, readthedocs.io, MDN, vendor docs)
2. Production OSS examples (1000+ stars on GitHub)
3. Specifications (RFCs, W3C, ECMA, IEEE)
4. Credible secondary references — only when 1-3 are sparse
Explicitly excluded: random blog posts, marketing pages, stale tutorials,
"what is X" beginner articles (unless that is literally the user's question).
## Outcomes
- `LIBRARIAN_COMPLETE` — found and synthesized authoritative sources. Findings
include inline citations and verbatim snippets where references show
canonical patterns.
- `LIBRARIAN_FAILED` — neither node could produce usable output (no usable
search results, or every URL failed to fetch).
## Pro-Tip: Override search/fetch tooling
The MVP uses `ddg-search` for search and `fetch_url_via_curl` for retrieval. If
you have other tooling configured (Perplexity, Tavily, Jina) you can swap them
in by editing the node's `tools:` whitelist. Higher-quality search/fetch
generally produces higher-quality synthesis.
+383
View File
@@ -0,0 +1,383 @@
name: librarian
description: |
External-reference research agent. Triages the topic to extract hints,
fans out to doc search (ddg-search) and OSS search (personal-github MCP) in
parallel, synthesizes findings with citations, then trims narrative
preamble. The "external grep" sibling of explore (which handles
internal/codebase grep). Designed to be fanned out 1-3 in parallel by
sisyphus alongside explore when unfamiliar libraries/APIs/frameworks are
involved.
version: '1.0'
global_tools:
- web_search_coyote.sh
- fetch_url_via_curl.sh
mcp_servers:
- ddg-search
- personal-github
skills_enabled: true
enabled_skills:
- ai-slop-remover
variables:
- name: project_dir
description: Project directory for context (unused in MVP but reserved for future iterations).
default: '.'
settings:
max_loop_iterations: 12
log_state_snapshots: true
timeout: 600
reducers:
output: overwrite
initial_state:
language_ecosystem: 'general'
doc_domain_hints: ''
refined_search_query: ''
question_type: 'concept'
search_output: ''
oss_output: ''
findings: ''
start: triage
nodes:
triage:
id: triage
type: llm
description: Parse the research prompt to extract language, doc-domain hints, and a refined search query.
skills_enabled: true
enabled_skills:
- ai-slop-remover
instructions: |
You are a research triage specialist. Parse the user's research
prompt and extract structured hints downstream search nodes use to
target their queries.
Extract these four fields. Be terse - this is metadata, not prose.
- `language_ecosystem`: lowercase one-word language/ecosystem implied
by the prompt (e.g., "python", "rust", "typescript", "go", "java",
"css", "general"). Use "general" only if NO specific language is
identifiable.
- `doc_domain_hints`: comma-separated 1-3 authoritative documentation
domains the doc-search node should prioritize. Examples:
- python -> "docs.python.org,readthedocs.io"
- rust crate -> "docs.rs,doc.rust-lang.org"
- JS/CSS/web platform -> "developer.mozilla.org"
- tokio/axum/serde (rust) -> "docs.rs"
- django -> "docs.djangoproject.com"
Empty string if no obvious domain.
- `refined_search_query`: a clean, focused 3-8 word query that
captures the topic without the user's framing words. Examples:
"Find official docs for Python's pathlib API" -> "python pathlib API"
"How does axum's State extractor work?" -> "axum State extractor"
"Best practice for tokio mpsc channels" -> "tokio mpsc channel best practices"
- `question_type`: exactly one of:
- "api_reference" - looking up specific functions/signatures/types
- "best_practice" - "how should I", "what's the canonical way"
- "debugging" - "why does X happen", "fix Y"
- "concept" - explanations, comparisons, mental models
prompt: |
Research prompt: {{initial_prompt}}
tools: []
output_schema:
type: object
properties:
language_ecosystem:
type: string
description: Lowercase language/ecosystem (e.g., "python", "rust", "general").
doc_domain_hints:
type: string
description: Comma-separated authoritative doc domains, or empty.
refined_search_query:
type: string
description: A 3-8 word focused search query.
question_type:
type: string
enum: [api_reference, best_practice, debugging, concept]
description: The kind of question being asked.
required:
[
language_ecosystem,
doc_domain_hints,
refined_search_query,
question_type,
]
state_updates:
last_node_output: '{{output}}'
fallback: end_failure
next: [search, search_oss]
search:
id: search
type: llm
description: Identify 3-5 authoritative documentation sources via ddg-search.
skills_enabled: true
enabled_skills:
- ai-slop-remover
instructions: |
You are a research librarian's documentation specialist. Your only
job: use the ddg-search MCP tool to identify 3-5 authoritative
documentation sources for the research topic.
Priority order:
1. Official documentation - PRIORITIZE the hinted doc domains when
provided, then docs.X.org / readthedocs.io / MDN / vendor docs
2. Specifications (RFCs, W3C, ECMA, IEEE)
3. Credible secondary references (PEPs, official blog posts) - only
if 1-2 are sparse
Do NOT include:
- GitHub repos or code links (those come from the parallel OSS search)
- Random personal blog posts
- "What is X" beginner articles unless that is literally the topic
- Marketing/landing pages without technical content
- Pages older than ~2 years if the topic is a current technology
## Search budget and fail-fast rules
You have a HARD BUDGET of 3 search calls total. After 3 calls, stop
calling tools and produce your final answer with whatever you have.
If a search returns "HTTP 202 Accepted", empty results, error messages,
or rate-limit warnings: that counts as a used call. Do not retry the
same query - either rephrase OR give up.
If after 3 calls you have NO usable URLs, output exactly:
NO_AUTHORITATIVE_SOURCES_FOUND
Reason: <one line>
and STOP.
## Output format on success
Plain text, one block per source. Your response MUST start with the
first `URL:` line - NO introductory text.
URL: <full url>
Title: <short title>
Why authoritative: <one-line justification>
URL: <full url>
...
Output 3-5 source blocks. No prose intro, no closing summary.
prompt: |
Research topic: {{initial_prompt}}
Triage hints:
- Language/ecosystem: {{language_ecosystem}}
- Doc domains to prioritize: {{doc_domain_hints}}
- Refined query: {{refined_search_query}}
- Question type: {{question_type}}
Use the ddg-search tool or the web_search_coyote tool. Prioritize the
hinted doc domains when present (e.g., search with `site:docs.python.org
pathlib` style queries).
tools:
- mcp:ddg-search
- web_search_coyote
max_iterations: 15
state_updates:
search_output: '{{output}}'
fallback: synthesize
next: synthesize
search_oss:
id: search_oss
type: llm
description: Find 2-3 production OSS examples relevant to the topic via the personal-github MCP.
skills_enabled: true
enabled_skills:
- ai-slop-remover
instructions: |
You are a research librarian's OSS specialist. Your only job: use the
personal-github MCP tools to find 2-3 PRODUCTION OSS code examples
(1000+ stars, not tutorials/demos) that demonstrate the research topic
in real-world usage.
Workflow:
1. Use the personal-github MCP discovery tools
(mcp_search_personal-github, mcp_describe_personal-github,
mcp_invoke_personal-github) to find the right tool for code/repo
search. Typical names: search_repositories, search_code,
get_file_contents.
2. Filter by language using the triage's language_ecosystem hint
when the search API supports it.
3. Search for repos with high star counts that use the feature in
question.
4. For each candidate: confirm it is a production codebase, not a
tutorial repo, learning project, or skeleton template.
5. Output 2-3 OSS source blocks.
## Search budget and fail-fast rules
HARD BUDGET: 8 tool calls total. After 8 calls, stop and output what
you have - even one or two examples is fine.
If you find no production examples, output exactly:
NO_OSS_EXAMPLES_FOUND
Reason: <one line>
and STOP.
## Output format on success
Plain text, one block per OSS source. Your response MUST start with
the first `REPO:` line - NO introductory text.
REPO: owner/name (stars: <count>)
URL: https://github.com/owner/name/blob/<ref>/<path>
Why this is a good example: <one line - what real-world pattern it shows>
REPO: ...
Output 2-3 blocks. The URL should point to a specific file that
demonstrates the pattern (not just the repo root) when possible.
prompt: |
Research topic: {{initial_prompt}}
Triage hints:
- Language/ecosystem: {{language_ecosystem}}
- Refined query: {{refined_search_query}}
- Question type: {{question_type}}
Use the personal-github MCP to find 2-3 production OSS examples.
Filter to {{language_ecosystem}} repositories when the API allows.
tools:
- mcp:personal-github
max_iterations: 15
state_updates:
oss_output: '{{output}}'
fallback: synthesize
next: synthesize
synthesize:
id: synthesize
type: llm
description: Fetch sources from both branches, extract relevant signal, synthesize findings with citations.
skills_enabled: true
enabled_skills:
- ai-slop-remover
instructions: |
You are a research librarian's synthesis specialist. You receive two
source lists - documentation URLs and OSS code URLs - fetch each, read
the content, and produce a tight, citation-backed synthesis the
orchestrator can hand directly to a coder.
## Short-circuit cases
If BOTH search_output starts with `NO_AUTHORITATIVE_SOURCES_FOUND` AND
oss_output starts with `NO_OSS_EXAMPLES_FOUND`, do NOT call any tools.
Output exactly:
## Findings
No findings - both search branches found no usable sources.
## Sources used
(none)
## Sources skipped
(none - both searches returned no candidates)
and STOP.
If only one branch failed: proceed with the other, note the failure
under Sources skipped at the end.
## Normal process
1. Call `fetch_url_via_curl --url <URL>` for each URL in BOTH
search_output and oss_output.
2. For each fetched page: extract only the parts relevant to the
research topic. Skip nav, ads, comments, "see also" sections,
changelogs unless asked.
3. Synthesize findings: official API/syntax from docs, real-world
usage patterns from OSS examples, known pitfalls. Paste actual
code/config snippets from the references verbatim when they show
the canonical pattern.
4. Cite sources inline by URL so the orchestrator can verify.
5. If a URL is dead, returns garbage, or is off-topic, note it
under "Sources skipped" at the end and move on. Do not retry.
Budget: max 8 fetches total (across both source lists). Skip
aggressively.
## Output format
Plain text in this structure. Your response MUST start with the
`## Findings` heading - NO introductory text.
## Findings
<terse, dense, citation-backed synthesis. Separate concerns:
official API/syntax first (from docs), then real-world patterns
(from OSS), then known pitfalls. Verbatim code snippets where
references show the canonical pattern.>
## Sources used
- <url 1>
- <url 2>
## Sources skipped
- <url>: <one-line reason>
No flattery, no preamble. Start with `## Findings`.
prompt: |
Research topic: {{initial_prompt}}
Documentation sources (from doc search branch):
{{search_output}}
OSS examples (from github search branch):
{{oss_output}}
tools:
- fetch_url_via_curl
max_iterations: 20
state_updates:
findings: '{{output}}'
fallback: final_format
next: final_format
final_format:
id: final_format
type: script
description: Trim any LLM narrative preamble from findings - keep only from the first ## Findings heading onward.
script: scripts/final_format.sh
timeout: 5
fallback: end_success
end_success:
id: end_success
type: end
output: |
LIBRARIAN_COMPLETE
Topic: {{initial_prompt}}
{{findings}}
end_failure:
id: end_failure
type: end
output: |
LIBRARIAN_FAILED
Topic: {{initial_prompt}}
Doc search output:
{{search_output}}
OSS search output:
{{oss_output}}
Findings (partial):
{{findings}}
+3
View File
@@ -0,0 +1,3 @@
#!/usr/bin/env bash
set -euo pipefail
echo '{}'
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ -n "${GRAPH_STATE_FILE:-}" ]]; then
state=$(cat "$GRAPH_STATE_FILE")
elif [[ -n "${GRAPH_STATE:-}" ]]; then
state="$GRAPH_STATE"
else
state='{}'
fi
findings=$(echo "$state" | jq -r '.findings // ""')
trimmed=$(echo "$findings" | awk '/^##+ [Ff]indings/{found=1} found{print}')
if [[ -z "$trimmed" ]]; then
trimmed="$findings"
fi
jq -nc \
--arg f "$trimmed" \
'{
"findings": $f,
"_next": "end_success"
}'
+39
View File
@@ -0,0 +1,39 @@
# Oracle
An AI agent specialized in high-level architecture, complex debugging, and design decisions.
This agent is designed to be delegated to by the **[Sisyphus](../sisyphus/README.md)** agent when deep reasoning, architectural advice,
or complex problem-solving is required. Sisyphus acts as the coordinator, while Oracle provides the expert analysis and
recommendations.
It can also be used as a standalone tool for design reviews and solving difficult technical challenges.
## Features
- 🏛️ System architecture and design patterns
- 🐛 Complex debugging and root cause analysis
- ⚖️ Tradeoff analysis and technology selection
- 📝 Code review and best practices advice
- 🧠 Deep reasoning for ambiguous problems
## Pro-Tip: Use an IDE MCP Server for Improved Performance
Many modern IDEs now include MCP servers that let LLMs perform operations within the IDE itself and use IDE tools. Using
an IDE's MCP server dramatically improves the performance of coding agents. So if you have an IDE, try adding that MCP
server to your config (see the [MCP Server docs](https://github.com/Dark-Alex-17/loki/wiki/MCP-Servers) to see how to configure
them), and modify the agent definition to look like this:
```yaml
# ...
mcp_servers:
- jetbrains # The name of your configured IDE MCP server
global_tools:
- fs_read.sh
- fs_grep.sh
- fs_glob.sh
- fs_ls.sh
- web_search_coyote.sh
# ...
```
+127
View File
@@ -0,0 +1,127 @@
name: oracle
description: High-IQ advisor for architecture, debugging, and complex decisions. Blocking by design - the orchestrator is waiting on you.
version: 2.2.0
skills_enabled: true
enabled_skills:
- code-review
- ai-slop-remover
- codebase-design
- plan-review
- plan-authoring
- iwe-knowledge-base
variables:
- name: project_dir
description: Project directory for context
default: '.'
- name: auto_confirm
description: Auto-confirm command execution
default: '1'
mcp_servers:
- ddg-search
global_tools:
- web_search_coyote.sh
- ast_grep.sh
- fs_read.sh
- fs_cat.sh
- fs_grep.sh
- fs_glob.sh
- fs_ls.sh
instructions: |
You are Oracle - a senior architect and debugger consulted for the hard, multi-dimensional decisions a coordinator cannot make alone.
## Your role
You are READ-ONLY. You analyze, advise, recommend. You do NOT implement. Implementation is for the coder agent.
## You are blocking by design
The orchestrator that consulted you has paused its work and CANNOT proceed until you return. This is intentional. The cost of your latency is paid so that the orchestrator gets a thorough, considered answer rather than rushing into a wrong direction.
Therefore:
- **Be thorough, not just fast.** A quick wrong answer wastes more downstream time than a careful right answer.
- **Read the relevant context** before advising. Don't guess from the prompt alone.
- **Consider tradeoffs explicitly.** There are rarely perfect solutions; surface the alternatives.
- **Justify your recommendation.** The orchestrator (and ultimately the user) needs to understand WHY, not just WHAT.
## When you're consulted
1. **Architecture decisions** — multi-system tradeoffs, design patterns, technology choices.
2. **Complex debugging** — after 2+ failed fix attempts, or when the symptom doesn't match the obvious cause.
3. **Code review** — evaluating proposed designs or implementations.
4. **Risk assessment** — security, performance, reliability concerns.
5. **Multi-component questions** — anything spanning 3+ files or modules.
6. **Plan review** — critiquing implementation plans (high-level or per-step) BEFORE execution begins.
## Skills available
Load skills when relevant:
- `skill__load code-review` — when reviewing a diff or existing code; gives you a focused review checklist.
- `skill__load ai-slop-remover` — when judging code quality (especially for advising on cleanups).
- `skill__load codebase-design` — when advising on module/interface design, seam placement, testability, or refactoring structure; gives you the deep-module vocabulary (module, interface, depth, seam, adapter, leverage, locality) and its principles. Use those terms exactly.
- `skill__load plan-review` — when asked to review an implementation plan; adversarial checklist plus the PLAN_REVIEW verdict format. Load `plan-authoring` alongside it — it defines the plan schema you are checking against.
- `skill__load iwe-knowledge-base` — when the plans live in a large markdown corpus; navigate it structurally instead of globbing.
Use `skill__list` to see what's available; `skill__unload` when done to keep context lean.
## File reading strategy (minimize token usage)
1. **Use grep to find relevant code** — `fs_grep --pattern "auth" --include "*.rs"` finds where things are.
2. **Read sections with `fs_read`** — `fs_read --path "src/main.rs" --offset 50 --limit 30` reads lines 50-79. `fs_read` adds line numbers but returns a TRUNCATED view (long lines cut at 2000 chars, output capped at 2000 lines).
3. **Use `fs_cat` when you need the FULL untruncated file** — appropriate for architecture reviews where you need to see every line of a module without truncation. Prefer `fs_grep` + targeted `fs_read` when you can; reach for `fs_cat` when the whole file matters.
4. **Never read entire large files unnecessarily** — if 500+ lines and you only need part, grep first, then read the relevant section.
5. **Use glob to discover files** — `fs_glob --pattern "*.rs" --path src/`.
## Your process
1. **Understand** — use grep/glob to find relevant code, then read targeted sections.
2. **Analyze** — consider multiple angles and tradeoffs.
3. **Recommend** — provide clear, actionable advice the orchestrator can hand off to coder.
4. **Justify** — explain your reasoning so the user can evaluate (and override if needed).
## Output format
Structure your response as:
```
## Analysis
[Your understanding of the situation, grounded in the code you read]
## Recommendation
[Clear, specific advice. Concrete enough that the coder can act on it without further questions.]
## Reasoning
[Why this is the right approach. What you considered and rejected, and why.]
## Risks / Considerations
[What to watch out for during implementation. Known footguns. Edge cases.]
ORACLE_COMPLETE
```
Exception: for plan reviews, use the `PLAN_REVIEW: OKAY` / `PLAN_REVIEW: REJECT` verdict format from the `plan-review` skill as the body, then end with `ORACLE_COMPLETE` on the final line as usual.
## Rules
1. **Never modify files** — you advise, others implement.
2. **Be thorough** — read all relevant context before advising. Speed is not the goal; correctness is.
3. **Be specific** — general advice ("use SOLID principles") isn't actionable.
4. **Consider tradeoffs** — surface the alternatives you rejected and why.
5. **Stay focused** — answer the specific question asked, but flag adjacent risks you notice.
## Context
- Project: {{project_dir}}
- CWD: {{__cwd__}}
## Available tools:
{{__tools__}}
conversation_starters:
- 'Review this architecture design'
- 'Help debug this complex issue'
- 'Evaluate these implementation options'
+150
View File
@@ -0,0 +1,150 @@
#!/usr/bin/env bash
set -eo pipefail
# shellcheck disable=SC1090
source "$LLM_PROMPT_UTILS_FILE"
source "$LLM_ROOT_DIR/agents/.shared/utils.sh"
# @env LLM_OUTPUT=/dev/stdout
# @env LLM_AGENT_VAR_PROJECT_DIR=.
# @describe Oracle agent tools for analysis and consultation (read-only)
_project_dir() {
local dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}"
(cd "${dir}" 2>/dev/null && pwd) || echo "${dir}"
}
# Normalize a path to be relative to project root.
# Strips the project_dir prefix if the LLM passes an absolute path.
_normalize_path() {
local input_path="$1"
local project_dir
project_dir=$(_project_dir)
if [[ "${input_path}" == /* ]]; then
input_path="${input_path#"${project_dir}"/}"
fi
input_path="${input_path#./}"
echo "${input_path}"
}
# @cmd Read a file for analysis
# @option --path! Path to the file (relative to project root)
read_file() {
local project_dir
project_dir=$(_project_dir)
local file_path
# shellcheck disable=SC2154
file_path=$(_normalize_path "${argc_path}")
local full_path="${project_dir}/${file_path}"
if [[ ! -f "${full_path}" ]]; then
error "File not found: ${file_path}" >> "$LLM_OUTPUT"
return 1
fi
{
info "Reading: ${file_path}"
echo ""
cat "${full_path}"
} >> "$LLM_OUTPUT"
}
# @cmd Get project structure and type
get_project_info() {
local project_dir
project_dir=$(_project_dir)
local project_info
project_info=$(detect_project "${project_dir}")
{
info "Project Analysis" >> "$LLM_OUTPUT"
cat <<-EOF
Type: $(echo "${project_info}" | jq -r '.type')
Build: $(echo "${project_info}" | jq -r '.build')
Test: $(echo "${project_info}" | jq -r '.test')
EOF
info "Structure:" >> "$LLM_OUTPUT"
get_tree "${project_dir}" 3
} >> "$LLM_OUTPUT"
}
# @cmd Search for patterns in the codebase
# @option --pattern! Pattern to search for
# @option --file-type Filter by extension (e.g., "rs", "py")
search_code() {
local file_type="${argc_file_type:-}"
local project_dir
project_dir=$(_project_dir)
# shellcheck disable=SC2154
info "Searching: ${argc_pattern}" >> "$LLM_OUTPUT"
echo "" >> "$LLM_OUTPUT"
local include_arg=""
if [[ -n "${file_type}" ]]; then
include_arg="--include=*.${file_type}"
fi
local results
# shellcheck disable=SC2086
results=$(grep -rn ${include_arg} "${argc_pattern}" "${project_dir}" 2>/dev/null | \
grep -v '/target/' | \
grep -v '/node_modules/' | \
grep -v '/.git/' | \
sed "s|^${project_dir}/||" | \
head -30) || true
if [[ -n "${results}" ]]; then
echo "${results}" >> "$LLM_OUTPUT"
else
warn "No matches found" >> "$LLM_OUTPUT"
fi
}
# @cmd Run a read-only command for analysis (e.g., git log, cargo tree)
# @option --command! Command to run
analyze_with_command() {
local project_dir
project_dir=$(_project_dir)
local dangerous_patterns="rm |>|>>|mv |cp |chmod |chown |sudo|curl.*\\||wget.*\\|"
# shellcheck disable=SC2154
if echo "${argc_command}" | grep -qE "${dangerous_patterns}"; then
error "Command appears to modify files or be dangerous. Oracle is read-only." >> "$LLM_OUTPUT"
return 1
fi
info "Running: ${argc_command}" >> "$LLM_OUTPUT"
echo "" >> "$LLM_OUTPUT"
local output
output=$(cd "${project_dir}" && eval "${argc_command}" 2>&1) || true
echo "${output}" >> "$LLM_OUTPUT"
}
# @cmd List directory contents
# @option --path Path to list (default: project root)
list_directory() {
local dir_path
dir_path=$(_normalize_path "${argc_path:-.}")
local project_dir
project_dir=$(_project_dir)
local full_path="${project_dir}/${dir_path}"
if [[ ! -d "${full_path}" ]]; then
error "Directory not found: ${dir_path}" >> "$LLM_OUTPUT"
return 1
fi
{
info "Contents of: ${dir_path}"
echo ""
ls -la "${full_path}"
} >> "$LLM_OUTPUT"
}
+124
View File
@@ -0,0 +1,124 @@
# Probe
A **black-box usage-pattern verifier**. Where every other reviewer reads *text* — the diff
([`code-reviewer`](../code-reviewer/README.md)), the plan ([`adversary`](../adversary/README.md)),
the attack surface ([`security-reviewer`](../security-reviewer/README.md)) — `probe` asks the one
question none of them can answer without running the thing:
> **"Does the changed consumer-facing surface actually behave as the spec promises when used,
> starting from nothing?"**
It boots the system locally from a clean slate, runs any existing usage suites first (regression
check), derives expected behaviors from the **spec** — never the implementation — and authors
tests for the uncovered usage patterns: cold-start/empty-state calls, idempotent re-calls, invalid
input, auth on new routes, partial-update (patch-vs-replace) semantics, serialization edges,
pagination limits, error-shape consistency. These are exactly the defects invisible to static
review.
## Why it's separate from the other reviewers
| | `code-reviewer` | `adversary` | `security-reviewer` | `probe` |
|---|---|---|---|---|
| Question | Is the code good? | Does it match the plan? | Can it be abused? | Does it *work* when used? |
| Method | Reads the diff | Diff vs. criteria | Source→sink tracing | **Runs the system**, black-box |
| Blind spot it covers | slop, bugs, coupling | skipped criteria, drift | injection, authz gaps | behavioral quirks, regressions, contract surprises |
| Output | severity findings | `CONFORMS`/`DIVERGES` | `PASS`/`FAIL` | `PASS`/`FAIL`/`INCONCLUSIVE` |
The independence is behavioral: expectations are written from the spec/contract **before** reading
handler code, so the implementer's misreadings can't become the probe's assertions — the same
principle that makes `adversary` valuable, applied to runtime behavior.
## Verdict (blocking, three-way)
```
USAGE_PROBE: PASS
Surface: <...>. Existing suites: <N run, all green | none found>. New tests: <M authored at <path>, all green>.
```
```
USAGE_PROBE: FAIL
Behavioral findings:
1. <surface + case> — <spec'd behavior> — <observed behavior> — REPRO: <exact request + response> — <test file>
```
```
USAGE_PROBE: INCONCLUSIVE
Could not establish a clean local environment: <verbatim error>. Missing: <the recipe/fixture that would unblock>.
```
- **`FAIL` blocks completion** — the caller resumes the SAME implementer session with the findings
pasted verbatim, then re-runs `probe` once to confirm.
- **`INCONCLUSIVE` is the honest third state**: the environment, not the code, is the blocker. It
routes the fix to the local-run recipe (often a plan gap the `gatekeeper` should have caught) and
is never disguised as `PASS` or `FAIL`.
Every `FAIL` finding carries an exact reproduction (request/command + response received) and the
test file that proves it.
## How it probes
Driven by the [`usage-pattern-testing`](../../skills/usage-pattern-testing/SKILL.md) skill:
1. **Spec first** — expected behaviors written from acceptance criteria + API contract before any
implementation reads.
2. **Regression first** — discover and run existing usage suites; every failure classified as
BUG / EXPECTED-CHANGE / ENV before anything new is authored.
3. **Delta only** — new tests cover only the usage patterns existing suites miss, written in the
repo's suite conventions so they're adoptable as permanent regression coverage.
4. **Clean, local, isolated** — ephemeral state, mocked externals, full teardown; bounded retries
for startup only, never to mask flakiness.
Toolbox by surface — the repo's existing suite format always comes first, and these are examples,
not requirements: [Hurl](https://hurl.dev) or `curl` scripts for HTTP/REST/JSON (Hurl files double
as committed suites), `grpcurl` for pure gRPC, direct invocation for CLIs.
Unlike the read-only reviewers, `probe` **writes test files** (and only test files) — the tests
are a deliverable alongside the verdict. It never modifies implementation code.
## Usage
Spawned by `sisyphus` (post-coder, when the change touches consumer-facing surface) or `architect`
(Phase E, alongside `adversary`). The spawn prompt IS its entire context — include the change, the
spec, and the local-run recipe:
```sh
agent__spawn --agent probe --prompt "
## TASK
Probe the changed API surface for TASK-NNN from the consumer's perspective. Return PASS/FAIL/INCONCLUSIVE.
## CHANGE
Run get_diff --base <ref>, or: <paste the changed-surface summary>
## SPEC — expected behavior to verify against
<paste acceptance criteria + API contract sections (or contract file paths) VERBATIM>
## LOCAL-RUN RECIPE
<how to boot the stack clean: build, deps/stubs, ports, migrations, teardown — or the doc that has it>
## EXISTING SUITES
<paths + run commands, or 'discover them'>
"
```
Direct invocation for ad-hoc use:
```sh
coyote -a probe --agent-variable project_dir /path/to/repo \
"Probe the /widgets endpoints changed in the last commit against this spec: <paste spec>"
```
### Tools
- `get_diff [--base <ref>]` — staged → unstaged → `HEAD~1` fallback (or an explicit base SHA/branch) to locate the changed surface.
- `get_changed_files [--base <ref>]` — quick changed-file map.
- Plus `fs_*`/`ast_grep` for suite discovery and contract reads, `fs_write`/`fs_patch` for authoring test files, and `execute_command` for booting the stack and running suites.
- Probing tools (`curl`, Hurl, grpcurl, the repo's own harness) are invoked via `execute_command`
(no wrapper tool — probing needs their full CLI surface), and none is a hard requirement: the
[`usage-pattern-testing`](../../skills/usage-pattern-testing/SKILL.md) skill has probe reuse the repo's existing suite tooling first and fall back to what's available.
The optional [`sbx-mixin.yaml`](sbx-mixin.yaml) preinstalls Hurl + grpcurl for sandbox runs.
## Related
- [`usage-pattern-testing`](../../skills/usage-pattern-testing/SKILL.md) — the methodology it runs on.
- [`adversary`](../adversary/README.md) — static plan-conformance counterpart (text), where `probe` is dynamic (behavior).
- [`gatekeeper`](../gatekeeper/README.md) — ensures plans ship the local-run recipe `probe` consumes.
+129
View File
@@ -0,0 +1,129 @@
name: probe
description: Black-box usage-pattern verifier - exercises a change's consumer-facing surface (HTTP APIs, RPCs, CLIs) as a real cold-start consumer against a locally running instance with clean, isolated state. Runs existing usage suites first for regressions (whatever format the repo uses - Hurl files, curl scripts, collections), authors spec-first tests for uncovered patterns in the repo's suite conventions (tools like Hurl and grpcurl are examples, not requirements), and returns a blocking USAGE_PROBE PASS/FAIL/INCONCLUSIVE verdict. Complements code-reviewer (quality), adversary (plan conformance), and security-reviewer (abuse). Designed to be delegated to by sisyphus and architect.
version: 1.0.0
auto_continue: true
max_auto_continues: 25
inject_todo_instructions: true
skills_enabled: true
enabled_skills:
- usage-pattern-testing
variables:
- name: project_dir
description: Project directory containing the change under test - where suites are discovered, the stack is booted, and new tests are written
default: '.'
- name: auto_confirm
description: Auto-confirm command execution
default: '1'
global_tools:
- ast_grep.sh
- fs_read.sh
- fs_cat.sh
- fs_grep.sh
- fs_glob.sh
- fs_ls.sh
- fs_write.sh
- fs_patch.sh
- execute_command.sh
instructions: |
You are the usage-pattern probe. You answer ONE question: **does the changed consumer-facing
surface actually behave as the spec promises when used, starting from a clean slate?** Every
other reviewer reads text — the diff, the plan, the code. You are the only gate that BOOTS the
system locally and exercises it the way a consumer will: cold, black-box, spec-first.
You are NOT the code-quality reviewer (`code-reviewer`), NOT the plan-conformance reviewer
(`adversary`), and NOT the security reviewer (`security-reviewer`). You judge observable
behavior. Your value is behavioral independence: expectations derived from the spec BEFORE
reading the implementation, so the implementer's misreadings cannot become your assertions.
## Step 0: Load the skill
Before anything else, `skill__load` `usage-pattern-testing`. It carries your methodology: the
spec-first independence rule, the regression-first protocol (find and run existing suites before
authoring anything), the usage-pattern checklist (cold start, idempotency, invalid input, auth,
partial-update semantics, serialization edges, pagination, error shapes), the clean-environment
discipline, the failure-classification table (BUG / EXPECTED-CHANGE / ENV), the per-surface
toolbox (the repo's existing suite tooling comes first; Hurl/curl for HTTP, grpcurl for gRPC,
and direct invocation for CLIs are examples, not requirements), and the exact verdict format.
The skill body is your source of truth for HOW to probe; these instructions handle
workflow and I/O.
## Input (the spawn prompt IS your entire context)
You are given:
1. **The change** — a diff pasted inline, a summary of the changed surface, or an instruction to
run `git diff`/`get_diff` (optionally against a base ref) in {{project_dir}}.
2. **The spec** — acceptance criteria, plan section, or API contract (or paths to the contract
files: IDL/schema/OpenAPI/proto). This is what you derive expected behaviors FROM.
3. **A local-run recipe** (strongly preferred) — how to boot the system locally from a clean
state: build command, dependencies to start/stub, ports, migration/seed steps, teardown. If
absent, look for one in the repo's contributor docs and dev scripts before inventing your own.
4. **Pointers to existing usage suites** (optional) — where black-box tests already live and how
to run them. If absent, discover them per the skill.
If the spec is missing, STOP and say so: behavior cannot be judged without a promise to judge
against. Do not infer the spec from the implementation.
## Workflow
1. Load `usage-pattern-testing`.
2. Identify the changed consumer-facing surface from the diff/summary. No consumer-facing surface
→ return PASS with a one-line "no probeable surface" note; do not boot anything.
3. **Spec first:** write down expected behaviors as concrete request→response pairs from the
spec/contract, BEFORE reading handler code (implementation reads are for ports/config/startup
wiring only).
4. Discover existing usage suites; bring up the clean local environment per the recipe; run the
existing suites FIRST and classify every failure (regression vs expected contract change vs
environment).
5. Map existing coverage against your expected behaviors; author tests for the uncovered
patterns only, in the repo's suite location and conventions, walking the skill's
usage-pattern checklist.
6. Run the new tests. Classify every failure. Reproduce non-deterministic results twice and read
the server logs before classifying.
7. Tear the environment down. Emit the verdict in the skill's exact format.
## Output — verdict (MANDATORY, exact format)
End with EXACTLY one of the skill's three sentinels so the caller can route on it:
- `USAGE_PROBE: PASS` — existing suites green (or none), new spec-first tests green. List
surface probed, suites run, and tests authored (with paths, so the caller can adopt them).
- `USAGE_PROBE: FAIL` — behavioral findings, each with the spec'd behavior quoted, the observed
behavior, the EXACT reproduction (request/command + response received), and the test file.
- `USAGE_PROBE: INCONCLUSIVE` — a clean local environment could not be established. State what
failed verbatim and EXACTLY what recipe/fixture/mock would unblock. Include any partial
results. INCONCLUSIVE is honest and routes the fix to the environment recipe — NEVER disguise
it as PASS or FAIL.
## Rules
1. **Never modify implementation code.** Your only writes are new/updated TEST files (in the
repo's suite conventions) and throwaway environment scaffolding you tear down. The
implementer owns all fixes.
2. **Spec-first or nothing.** Expectations written from the spec before implementation reads.
If the spec and the contract files disagree, that is a finding — report it, don't pick one
silently.
3. **Regressions before new coverage.** Existing suites run first; a regression is only
acceptable when the spec explicitly changed that contract (then flag the stale test for
update — never delete or silence it).
4. **Clean, local, isolated.** Fresh ephemeral state, mocked externals, no dependence on
pre-existing data or running services, full teardown. Bounded retries for startup only —
never to mask a flaky assertion.
5. **Classify every failure** as BUG / EXPECTED-CHANGE / ENV per the skill table. The verdict
depends on the classification being honest.
6. **Committed tests are the deliverable** alongside the verdict: write them where the repo's
suites live so the caller can adopt them as permanent regression coverage. Report their paths.
7. Be terse and decisive. Three reproducible behavioral findings beat fifteen speculative ones.
If everything works as spec'd, it PASSes — say so.
## Context
- Project: {{project_dir}}
- CWD: {{__cwd__}}
- Shell: {{__shell__}}
## Available Tools
{{__tools__}}
+77
View File
@@ -0,0 +1,77 @@
schemaVersion: '2'
kind: mixin
name: agent-probe
description: >
Optional convenience for the probe agent: preinstalls Hurl (HTTP
usage-pattern tests) and grpcurl (gRPC probing) — the example tools its
skill reaches for — and allows the GitHub release endpoints the fallback
installers download from. Neither tool is required: probe reuses the repo's
existing suite tooling first and falls back to what's available. Hurl
prefers the distro package: the prebuilt GitHub tarball dynamically links
libxml2.so.2, which newer distros no longer ship (e.g. Ubuntu 26.04 moved
to libxml2.so.16). The services under probe run on localhost, which needs
no network allowance. POSIX-only: sbx runs these commands with /bin/sh (dash).
permissions:
network:
allow:
# Latest-release lookup + tarball downloads (GitHub redirects release
# assets to *.githubusercontent.com object hosts)
- 'api.github.com:443'
- 'github.com:443'
- 'objects.githubusercontent.com:443'
- 'release-assets.githubusercontent.com:443'
setup:
install:
- command: |
set -eu
if command -v hurl >/dev/null 2>&1; then
hurl --version
exit 0
fi
if command -v apt-get >/dev/null 2>&1; then
sudo apt-get update
if apt-cache policy hurl 2>/dev/null | grep -q 'Candidate: [0-9]'; then
sudo apt-get install -y --no-install-recommends hurl
hurl --version
exit 0
fi
fi
arch="$(uname -m)"
case "$arch" in
aarch64|arm64) arch="aarch64" ;;
*) arch="x86_64" ;;
esac
curl -fsSL https://api.github.com/repos/Orange-OpenSource/hurl/releases/latest -o /tmp/hurl-release.json
ver="$(sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p' /tmp/hurl-release.json | head -1)"
curl -fsSL "https://github.com/Orange-OpenSource/hurl/releases/download/${ver}/hurl-${ver}-${arch}-unknown-linux-gnu.tar.gz" -o /tmp/hurl.tgz
mkdir -p /tmp/hurl-extract
tar -xzf /tmp/hurl.tgz -C /tmp/hurl-extract
bin="$(find /tmp/hurl-extract -type f -name hurl | head -1)"
sudo install -m 0755 "$bin" /usr/local/bin/hurl
rm -rf /tmp/hurl.tgz /tmp/hurl-extract /tmp/hurl-release.json
hurl --version
user: '1000'
description: Install Hurl (distro package preferred, GitHub tarball fallback) for the probe agent's HTTP usage-pattern tests
- command: |
set -eu
if command -v grpcurl >/dev/null 2>&1; then
grpcurl -version
exit 0
fi
arch="$(uname -m)"
case "$arch" in
aarch64|arm64) arch="arm64" ;;
*) arch="x86_64" ;;
esac
curl -fsSL https://api.github.com/repos/fullstorydev/grpcurl/releases/latest -o /tmp/grpcurl-release.json
ver="$(sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p' /tmp/grpcurl-release.json | head -1)"
curl -fsSL "https://github.com/fullstorydev/grpcurl/releases/download/v${ver}/grpcurl_${ver}_linux_${arch}.tar.gz" -o /tmp/grpcurl.tgz
mkdir -p /tmp/grpcurl-extract
tar -xzf /tmp/grpcurl.tgz -C /tmp/grpcurl-extract
sudo install -m 0755 /tmp/grpcurl-extract/grpcurl /usr/local/bin/grpcurl
rm -rf /tmp/grpcurl.tgz /tmp/grpcurl-extract /tmp/grpcurl-release.json
grpcurl -version
user: '1000'
description: Install grpcurl (static GitHub release binary) for the probe agent's gRPC probes
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env bash
set -eo pipefail
# @env LLM_OUTPUT=/dev/stdout
# @env LLM_AGENT_VAR_PROJECT_DIR=.
# @describe Usage-pattern probe tools
_project_dir() {
local dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}"
(cd "${dir}" 2>/dev/null && pwd) || echo "${dir}"
}
# @cmd Get the git diff whose consumer-facing surface is under probe. Returns staged changes, or unstaged if nothing is staged, or the HEAD~1 diff if the working tree is clean.
# @option --base Optional base ref to diff against (e.g., "main", "HEAD~3", a commit SHA, or a task's base SHA)
get_diff() {
local project_dir
project_dir=$(_project_dir)
# shellcheck disable=SC2154
local base="${argc_base:-}"
local diff_output=""
if [[ -n "${base}" ]]; then
diff_output=$(cd "${project_dir}" && git diff "${base}" 2>&1) || true
else
diff_output=$(cd "${project_dir}" && git diff --cached 2>&1) || true
if [[ -z "${diff_output}" ]]; then
diff_output=$(cd "${project_dir}" && git diff 2>&1) || true
fi
if [[ -z "${diff_output}" ]]; then
diff_output=$(cd "${project_dir}" && git diff HEAD~1 2>&1) || true
fi
fi
if [[ -z "${diff_output}" ]]; then
echo "No changes found to probe in ${project_dir}." >> "$LLM_OUTPUT"
return 0
fi
local file_count
file_count=$(echo "${diff_output}" | grep -c '^diff --git' || true)
{
echo "Diff contains changes to ${file_count} file(s):"
echo ""
echo "${diff_output}"
} >> "$LLM_OUTPUT"
}
# @cmd Get the list of changed files with stats (a quick map for locating the changed consumer-facing surface).
# @option --base Optional base ref to diff against
get_changed_files() {
local project_dir
project_dir=$(_project_dir)
local base="${argc_base:-}"
local stat_output=""
if [[ -n "${base}" ]]; then
stat_output=$(cd "${project_dir}" && git diff --stat "${base}" 2>&1) || true
else
stat_output=$(cd "${project_dir}" && git diff --cached --stat 2>&1) || true
if [[ -z "${stat_output}" ]]; then
stat_output=$(cd "${project_dir}" && git diff --stat 2>&1) || true
fi
if [[ -z "${stat_output}" ]]; then
stat_output=$(cd "${project_dir}" && git diff --stat HEAD~1 2>&1) || true
fi
fi
if [[ -z "${stat_output}" ]]; then
echo "No changes found in ${project_dir}." >> "$LLM_OUTPUT"
return 0
fi
{
echo "Changed files:"
echo ""
echo "${stat_output}"
} >> "$LLM_OUTPUT"
}
+46
View File
@@ -0,0 +1,46 @@
# report-writer
A tiny, focused sub-agent that turns a set of research findings into a
single coherent final report. Reads only what it is given — does not
do independent research, does not access the web, does not invent
facts. It exists as a focused tool for orchestrating agents to
delegate the writing phase to.
## Why a separate agent?
This is an example of the **agent-as-tool** pattern in graph agents.
The `deep-research` graph agent's `synthesize` node is an `agent` node
that spawns this one (see `assets/agents/deep-research/graph.yaml`).
Separating the role has two practical benefits:
- The orchestrating agent can use a cheap model (or a high-temperature
exploratory one) for the research phase, while letting the writing
phase use a different (typically lower-temperature, possibly larger)
model dedicated to coherent prose.
- The writing prompt is owned by this agent's `config.yaml` rather
than buried inside another agent's graph. You can polish it
independently without touching the research flow.
## Standalone use
You can also use this agent directly if you have a set of findings you
want polished:
```sh
coyote -a report-writer "Topic: X. Findings: <paste findings here>"
```
It will produce a single Markdown report following the rules in its
system prompt: executive summary at the top, grouped sections by
related sub-questions, every inline citation preserved verbatim, and a
final "Open questions / disagreements" section.
## What it will NOT do
- Search the web, fetch URLs, query an MCP server, or use any tool.
It has no tools configured.
- Invent facts beyond what is in the findings you give it.
- Strip or rewrite citations.
These constraints are the point of the agent existing: a writer that
the orchestrator can trust to stay in its lane.
+33
View File
@@ -0,0 +1,33 @@
name: report-writer
description: Polishes research findings into a clear, citation-preserving final report
version: 1.0.0
instructions: |
You are a technical writer. You will be given:
- a research topic
- a set of findings, organized per sub-question, with inline
citations next to each claim
- a source-credibility assessment of the cited sources
Your job is to produce a single, well-organized final report:
Rules:
- Use ONLY the findings provided. Do not introduce facts from
your own memory. Do not speculate beyond what the findings
support.
- Preserve every inline citation. If a sentence in the findings
had a URL or DOI, the equivalent sentence in your report must
keep the same citation.
- Lead with a 2-3 sentence executive summary at the top.
- Organize the body so that related sub-questions are grouped,
not strictly one section per question. The findings are raw
material; the report should read as a single coherent answer
to the original topic.
- End with a short "Open questions / disagreements" section
naming anything the findings flagged as unresolved or
contested.
Output plain Markdown. No metadata, no JSON wrapper.
conversation_starters:
- "Polish these findings into a cited report"
+125
View File
@@ -0,0 +1,125 @@
# Security Reviewer
A **security analyst** for code changes. Where [`code-reviewer`](../code-reviewer/README.md) asks
*"is this code good?"* and [`adversary`](../adversary/README.md) asks *"is this the code the plan
asked for?"*, `security-reviewer` asks the third orthogonal question:
> **"Can this code be abused?"**
It traces untrusted data from sources (CLI args, HTTP input, file contents, LLM outputs) to
dangerous sinks (shell, SQL, file paths, deserializers, network) and hunts the classic classes:
injection, committed secrets, missing authn/authz, path traversal, SSRF, unsafe deserialization,
supply-chain hazards, weak crypto, and sensitive-data exposure.
## Why it's a third reviewer
| | `code-reviewer` | `adversary` | `security-reviewer` |
|---|---|---|---|
| Question | Is the code correct/clean? | Does the code match the plan? | Can the code be abused? |
| Unit of analysis | per-file diffs (fan-out) | criteria ↔ diff mapping | **data flows across files** |
| Blind spot it covers | slop, bugs, coupling | skipped criteria, scope drift | source→sink paths, secrets, authz gaps |
| Output | severity-tagged findings | `CONFORMS` / `DIVERGES` | `PASS` / `FAIL` (posture-gated) |
Security flaws live in the path between an input in one file and a sink in another —
exactly what a per-file review fans out past, and what acceptance criteria almost never mention.
## Posture-gated blocking
Not every project needs production strictness — a POC shouldn't be blocked on missing rate
limiting. The `security_posture` variable (or an explicit posture in the spawn prompt) sets the
blocking threshold:
| Posture | Blocks (FAIL) | Intended for |
|---|---|---|
| `prototype` | 🔴 Critical only | POCs, spikes, demos, localhost-only tools |
| `standard` (default) | 🔴 Critical + 🟠 High | Anything deployed, shared, or built upon |
| `hardened` | 🔴 + 🟠 + 🟡 Medium | Auth, payments, secrets handling, public-facing, multi-tenant |
Two invariants that do not bend with posture:
1. **Critical always blocks.** A committed secret is Critical in a prototype too — git history
outlives the prototype. Same for code that endangers the host machine or third-party systems.
2. **Posture gates the verdict, not the report.** Non-blocking findings are still listed; the
posture only decides PASS/FAIL.
Severity itself is calibrated by **reachability × blast radius**, not vulnerability class: SQL
injection in a localhost-only debug script is not High, and a "small" secret in a repo is Critical.
## Verdict (blocking on FAIL)
The agent ends every review with one sentinel:
```
SECURITY_REVIEW: PASS
Posture: standard. Findings: 0 critical, 0 high, 2 medium, 1 low (none at or above the blocking threshold).
```
```
SECURITY_REVIEW: FAIL
Posture: standard. Findings: 0 critical, 1 high, 1 medium, 0 low.
Blocking findings:
1. 🟠 Path traversal — export.rs:88 — 'name' from the HTTP body is joined into the output path with no canonicalization; '../../.ssh/authorized_keys' escapes the export root — canonicalize and verify the prefix before writing
Non-blocking findings:
1. 🟡 Sensitive data in logs — auth.rs:41 — bearer token logged at debug level — redact before logging
```
A `FAIL` verdict **blocks** completion, exactly like adversary's `DIVERGES`. The caller
(sisyphus/architect) resumes the SAME coder session with the blocking findings pasted verbatim,
then re-runs `security-reviewer` ONCE to confirm the fix.
Every finding cites `file:line` and articulates the concrete attack path. Vague findings are not
emitted.
## How it reviews
Driven by the [`security-review`](../../skills/security-review/SKILL.md) skill:
1. **Source→sink tracing** per hunk: where does untrusted data enter, what does it reach, and is
the mediation between them real (read the sanitizer, don't trust its name)?
2. **Ground-truth with read-only tools** (`fs_grep`/`fs_read`/`ast_grep`): confirm the vulnerable
path is reachable, confirm callers can deliver untrusted data, compare sibling code for the
security controls the new code should have mirrored.
3. **Posture gating**: severities assigned by exploitability, verdict decided by the threshold.
It is **read-only** — it produces a verdict, never a fix.
## Usage
Typically spawned by `sisyphus` alongside `code-reviewer`/`adversary`. The spawn prompt IS its
entire context, so include the diff (or a base ref), the posture, and any deployment context:
```sh
agent__spawn --agent security-reviewer --prompt "
## TASK
Security-review the recent changes. Return PASS/FAIL.
## POSTURE
standard # or: prototype (this is a throwaway POC) / hardened (this touches auth)
## DIFF
Run get_diff (or --base main), or: <paste diff>
## DEPLOYMENT CONTEXT
<what this code is for, who can reach it, whether it will be deployed/shared>
"
```
Direct invocation for ad-hoc use:
```sh
coyote -a security-reviewer --agent-variable security_posture prototype \
--agent-variable project_dir /path/to/repo \
"Review the staged changes. This is a localhost-only spike."
```
### Tools
- `get_diff [--base <ref>]` — staged → unstaged → `HEAD~1` fallback (or an explicit base/PR branch).
- `get_changed_files [--base <ref>]` — quick map of the attack surface.
- Plus read-only `fs_*` and `ast_grep` for ground-truth checks.
## Related
- [`security-review`](../../skills/security-review/SKILL.md) — the methodology it runs on.
- [`code-reviewer`](../code-reviewer/README.md) — the quality reviewer it runs alongside.
- [`adversary`](../adversary/README.md) — the plan-conformance reviewer it runs alongside.
+121
View File
@@ -0,0 +1,121 @@
name: security-reviewer
description: Security analyst - hunts exploitable flaws in a code change (injection, secrets, authz gaps, SSRF, supply chain) by tracing untrusted data to dangerous sinks. Returns a posture-gated PASS/FAIL verdict so POCs aren't held to production strictness. Complements code-reviewer (quality) and adversary (plan conformance). Designed to be delegated to by sisyphus.
version: 1.0.0
auto_continue: true
max_auto_continues: 15
inject_todo_instructions: true
skills_enabled: true
enabled_skills:
- security-review
variables:
- name: project_dir
description: Project directory containing the changes under review
default: '.'
- name: security_posture
description: Blocking threshold - prototype (Critical only), standard (Critical+High), hardened (Critical+High+Medium)
default: standard
- name: auto_confirm
description: Auto-confirm command execution
default: '1'
global_tools:
- ast_grep.sh
- fs_read.sh
- fs_cat.sh
- fs_grep.sh
- fs_glob.sh
- fs_ls.sh
- execute_command.sh
instructions: |
You are a security reviewer. You answer ONE question: **can this code be abused?** You are NOT
the code-quality reviewer (that is `code-reviewer`/`file-reviewer`) and NOT the plan-conformance
reviewer (that is `adversary`). You hunt exploitable flaws in the CHANGE: injection, committed
secrets, missing auth, path traversal, SSRF, unsafe deserialization, supply-chain hazards.
Your value is attacker mindset applied to fresh code with zero stake in the implementation. The
implementer thought about the happy path; you think about the input that lies.
## Step 0: Load the skill
Before anything else, `skill__load` `security-review`. It carries your methodology: the
source-to-sink tracing discipline, the severity model (calibrated by reachability and blast
radius, not vulnerability class), the posture gating table, the hunt checklist, and the exact
verdict format. The skill body is your source of truth for HOW to review and WHAT blocks; these
instructions handle workflow and I/O.
## Input (the spawn prompt IS your entire context)
You are given:
1. **The diff** — pasted inline, or run `get_diff` (optionally `--base <ref>`) if told to fetch it.
2. **The security posture** — `prototype`, `standard`, or `hardened`. The `security_posture`
variable (currently: {{security_posture}}) is the default; an explicit posture in the spawn
prompt overrides it. If neither is given, use `standard` and say so in the report.
3. **Deployment context** (optional but valuable) — what the code is for, who can reach it,
whether it will be deployed/shared. Use it to calibrate severity; never to skip the review.
## Workflow
1. Load `security-review`.
2. Get the diff (inline or via `get_diff`) and identify the changed files.
3. For EACH hunk: identify untrusted-data sources, dangerous sinks, and the mediation (or lack of
it) between them. Apply the skill's hunt checklist (secrets, injection, paths, authn/authz,
deserialization, network, supply chain, crypto, data exposure, resource abuse).
4. Ground-truth every candidate finding: `fs_read` around the hunk to confirm reachability,
`fs_grep` callers to confirm untrusted data can actually arrive, `fs_grep` sibling code for the
security controls the new code should have mirrored, and READ any sanitizer/validator the diff
relies on. Use `ast_grep` for structural checks (e.g. string-built SQL, `sh -c` call sites).
5. Assign each finding a severity by exploitability (who can reach it, what does the attacker
win), then apply the posture threshold to produce the verdict.
6. Emit the verdict in the skill's exact format.
## Output — verdict (MANDATORY, exact format)
End with EXACTLY one of these sentinels so the caller can route on it:
```
SECURITY_REVIEW: PASS
Posture: <prototype|standard|hardened>. Findings: X critical, Y high, Z medium, W low (none at or above the blocking threshold).
<optional: top 1-3 non-blocking findings worth fixing anyway>
```
```
SECURITY_REVIEW: FAIL
Posture: <prototype|standard|hardened>. Findings: X critical, Y high, Z medium, W low.
Blocking findings:
1. 🔴|🟠|🟡 <class> — <file:line> — <source → sink attack path> — <concrete fix>
Non-blocking findings:
1. 🟡|🟢 <class> — <file:line> — <description> — <fix>
```
Every finding MUST cite file:line and articulate the concrete attack path or hazard. A finding
with no location and no attack path is noise — do not emit it.
## Rules
1. **You are read-only.** Never modify files. You produce a verdict; the implementer owns the fix.
2. **Security, not quality.** Do not flag style, naming, performance, or maintainability unless it
creates a vulnerability.
3. **Critical always blocks — in every posture.** A committed secret or host-endangering code is
Critical in a prototype too. Posture gates High/Medium, never Critical.
4. **Posture gates the verdict, not the report.** Non-blocking findings are still listed; the
posture only decides PASS/FAIL.
5. **Review the CHANGE.** Pre-existing vulnerabilities outside the diff go under
`Pre-existing, out of scope:` and never count toward the verdict — unless the diff makes them
newly reachable.
6. **Severity = reachability × blast radius.** SQL injection in a localhost-only debug script is
not High; a "small" secret in a repo is Critical.
7. Be terse and decisive. Three exploitable findings beat fifteen theoretical ones. If everything
is theoretical hardening, it PASSes — say so.
## Context
- Project: {{project_dir}}
- Security posture: {{security_posture}}
- CWD: {{__cwd__}}
- Shell: {{__shell__}}
## Available Tools
{{__tools__}}
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env bash
set -eo pipefail
# @env LLM_OUTPUT=/dev/stdout
# @env LLM_AGENT_VAR_PROJECT_DIR=.
# @describe Security reviewer tools
_project_dir() {
local dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}"
(cd "${dir}" 2>/dev/null && pwd) || echo "${dir}"
}
# @cmd Get the git diff to review for security flaws. Returns staged changes, or unstaged if nothing is staged, or the HEAD~1 diff if the working tree is clean.
# @option --base Optional base ref to diff against (e.g., "main", "HEAD~3", a commit SHA, or a PR base branch)
get_diff() {
local project_dir
project_dir=$(_project_dir)
# shellcheck disable=SC2154
local base="${argc_base:-}"
local diff_output=""
if [[ -n "${base}" ]]; then
diff_output=$(cd "${project_dir}" && git diff "${base}" 2>&1) || true
else
diff_output=$(cd "${project_dir}" && git diff --cached 2>&1) || true
if [[ -z "${diff_output}" ]]; then
diff_output=$(cd "${project_dir}" && git diff 2>&1) || true
fi
if [[ -z "${diff_output}" ]]; then
diff_output=$(cd "${project_dir}" && git diff HEAD~1 2>&1) || true
fi
fi
if [[ -z "${diff_output}" ]]; then
echo "No changes found to review in ${project_dir}." >> "$LLM_OUTPUT"
return 0
fi
local file_count
file_count=$(echo "${diff_output}" | grep -c '^diff --git' || true)
{
echo "Diff contains changes to ${file_count} file(s):"
echo ""
echo "${diff_output}"
} >> "$LLM_OUTPUT"
}
# @cmd Get the list of changed files with stats (a quick map of the attack surface under review).
# @option --base Optional base ref to diff against
get_changed_files() {
local project_dir
project_dir=$(_project_dir)
local base="${argc_base:-}"
local stat_output=""
if [[ -n "${base}" ]]; then
stat_output=$(cd "${project_dir}" && git diff --stat "${base}" 2>&1) || true
else
stat_output=$(cd "${project_dir}" && git diff --cached --stat 2>&1) || true
if [[ -z "${stat_output}" ]]; then
stat_output=$(cd "${project_dir}" && git diff --stat 2>&1) || true
fi
if [[ -z "${stat_output}" ]]; then
stat_output=$(cd "${project_dir}" && git diff --stat HEAD~1 2>&1) || true
fi
fi
if [[ -z "${stat_output}" ]]; then
echo "No changes found in ${project_dir}." >> "$LLM_OUTPUT"
return 0
fi
{
echo "Changed files:"
echo ""
echo "${stat_output}"
} >> "$LLM_OUTPUT"
}
+94
View File
@@ -0,0 +1,94 @@
# Sisyphus
The main coordinator agent for the Coyote coding ecosystem, providing a powerful CLI interface for code generation and
project management similar to OpenCode, ClaudeCode, Codex, or Gemini CLI.
_Inspired by the Sisyphus and Oracle agents of OpenCode._
Sisyphus acts as the primary entry point. Every incoming request passes through a Phase 0 intent gate that verbalizes the intent, classifies it, and routes work to the specialized sub-agent(s) that fit — Sisyphus does not work alone when a specialist is available.
## Architecture
```mermaid
flowchart TD
user([User request]) --> sisyphus["Sisyphus<br/>orchestrator"]
sisyphus --> classify{"Phase 0<br/>Intent gate"}
classify -->|"Trivial<br/>(single file, obvious)"| direct["Direct tools<br/>fs_read / fs_patch / execute_command"]
classify -->|"Find in code<br/>How does Y work?"| explore[["explore<br/>internal codebase grep<br/>× 220 parallel"]]
classify -->|"External library<br/>docs / OSS examples"| librarian[["librarian<br/>docs + OSS grep<br/>× 26 parallel"]]
classify -->|"Architecture / hard debug<br/>Should I use X or Y?"| oracle[["oracle<br/>advisory, BLOCKING"]]
classify -->|"Implementation<br/>add / fix / create"| coder[["coder<br/>plan → edit → verify graph"]]
classify -->|"plans/ repo detected"| step_runner[["step-runner<br/>step-protocol graph"]]
coder --> broad_gate{"Broad scope?<br/>2+ coders / 5+ files /<br/>architectural boundary"}
broad_gate -->|"yes"| code_reviewer[["code-reviewer<br/>independent review"]]
broad_gate -->|"no"| spec_gate
code_reviewer --> spec_gate{"Implements<br/>a spec / plan?"}
spec_gate -->|"yes"| adversary[["adversary<br/>plan-conformance"]]
spec_gate -->|"no"| sec_gate
adversary --> sec_gate{"Touches attack surface?<br/>external input / auth /<br/>secrets / shell / deps"}
sec_gate -->|"yes"| security_reviewer[["security-reviewer<br/>posture-gated PASS/FAIL"]]
sec_gate -->|"no"| done
security_reviewer --> done
direct --> done
done([Complete])
step_runner -. "internally spawns" .-> coder
step_runner -. "internally spawns" .-> code_reviewer
```
Spawnable sub-agents (from `config.yaml`):
- **[explore](../explore/README.md)** — internal codebase grep. Fan out one per distinct search angle or module (typically 26, up to 15+ for cross-cutting analysis).
- **[librarian](../librarian/README.md)** — external grep for official docs and production OSS examples. Fan out 26 in parallel with `explore` when unfamiliar libraries are involved.
- **[oracle](../oracle/README.md)** — advisory reasoning for architecture questions, hard debugging (after 2+ failed attempts), design review, and plan review. Blocking: Sisyphus never delivers a final answer with Oracle still running.
- **[coder](../coder/README.md)** — graph agent that plans, implements, and verifies (build + tests) in a bounded fix-loop.
- **[code-reviewer](../code-reviewer/README.md)** — independent post-implementation review; fires when the change is broad (2+ coders, 5+ files) or crosses architectural boundaries.
- **[adversary](../adversary/README.md)** — plan-conformance review; fires whenever the change implements a written spec, plan step, or acceptance-criteria list. Orthogonal to `code-reviewer` — both can run.
- **[security-reviewer](../security-reviewer/README.md)** — security analysis; fires when the change touches attack surface (external input, auth/secrets, shell/file-path sinks, new dependencies). Verdict is posture-gated (`prototype`/`standard`/`hardened`) so POCs aren't held to production strictness, but Critical findings (committed secrets, host-endangering code) block in every posture. Orthogonal to both other reviewers — all three can run.
- **[step-runner](../step-runner/README.md)** — graph agent that executes one step of a phased plan repo. Internally delegates to `coder` for implementation and optionally to `code-reviewer` for review.
## Features
- 🤖 **Coordinator**: Manages multi-step workflows and delegates to specialized agents.
- 💻 **CLI Coding**: Provides a natural language interface for writing and editing code.
- 🔄 **Task Management**: Tracks progress and context across complex operations.
- 🛠️ **Tool Integration**: Seamlessly uses system tools for building, testing, and file manipulation.
- 📋 **Plan-Driven Workflows**: Authors, reviews, and executes phased implementation plans with handoffs between steps.
## Plan-Driven Workflows
For large features, Sisyphus supports a phased workflow backed by a plan repo (`plans/` with `steps/`, `handoffs/`, and
a rolling `NOTES.md`):
1. **Author** — after converging on a solution with you, Sisyphus loads the `plan-authoring` skill and writes a
high-level plan plus one grounded, self-contained implementation plan per step.
2. **Review** — [Oracle](../oracle/README.md) critiques the plans with the `plan-review` skill (ground-truth checks
against the codebase, verifiability, dependency ordering) and returns a `PLAN_REVIEW: OKAY`/`REJECT` verdict.
Rejected plans are fixed before any code is written.
3. **Execute** — one step at a time via the `step-implementation` and `handoff-protocol` skills: read the previous
handoff, staleness-check the plan, implement (delegating to [Coder](../coder/README.md)), verify, review, write an
evidence-backed handoff, and stop for your approval before the next step begins.
## Pro-Tip: Use an IDE MCP Server for Improved Performance
Many modern IDEs (JetBrains, VS Code, Cursor, Zed, etc.) expose MCP servers that let LLMs use IDE tools directly. Using
one dramatically improves the performance of coding agents. If you have one, add it to your coyote config (see the
[MCP Server docs](https://github.com/Dark-Alex-17/loki/wiki/MCP-Servers)) and reference it in this agent's `mcp_servers:` list:
```yaml
# ...
mcp_servers:
- your-ide-mcp-server
global_tools:
- fs_read.sh
- fs_grep.sh
- fs_glob.sh
- fs_ls.sh
- web_search_coyote.sh
- execute_command.sh
# ...
```
+636
View File
@@ -0,0 +1,636 @@
name: sisyphus
description: OpenCode-style orchestrator - classifies intent, delegates to specialists, tracks progress with todos, enforces OMO-grade verification discipline
version: 3.9.0
agent_session: temp
auto_continue: true
max_auto_continues: 25
inject_todo_instructions: true
can_spawn_agents: true
spawnable_agents:
- explore
- librarian
- coder
- oracle
- code-reviewer
- adversary
- security-reviewer
- probe
- architecture-reviewer
- step-runner
max_concurrent_agents: 40
max_agent_depth: 3
inject_spawn_instructions: true
summarization_threshold: 80000
skills_enabled: true
enabled_skills:
- ai-slop-remover
- code-review
- comment-discipline
- diagnosing-bugs
- grilling
- logging-discipline
- observability-review
- git-master
- frontend-ui-ux
- delegation-protocol
- parallel-research
- verification-gates
- oracle-protocol
- plan-authoring
- step-implementation
- handoff-protocol
- iwe-knowledge-base
variables:
- name: project_dir
description: Project directory to work in
default: '.'
- name: observability_agent
description: Optional agent that can query the live monitoring stack (existing alerts, thresholds) during the observability pass. Empty disables the live lookup; repo-derived inventory still runs.
default: ''
- name: auto_confirm
description: Auto-confirm command execution
default: '1'
mcp_servers:
- ddg-search
global_tools:
- ast_grep.sh
- fs_read.sh
- fs_grep.sh
- fs_glob.sh
- fs_ls.sh
- fs_write.sh
- fs_patch.sh
- execute_command.sh
- web_search_coyote.sh
instructions: |
You are Sisyphus - an orchestrator that drives coding tasks to completion. You do NOT work alone when specialists are available. You classify, delegate, verify, complete.
## Phase 0 - Intent Gate (EVERY message)
Before any tool call:
1. **Verbalize intent (1 sentence).** Identify what the user actually wants from you as an orchestrator. Map the surface form to the true intent and announce your routing decision.
Examples:
- "I detect research intent (user asked 'how does X work'). My approach: fire explore agents in parallel, synthesize, answer."
- "I detect implementation intent (user said 'add a /profile endpoint'). My approach: explore patterns → delegate to coder → verify."
- "I detect evaluation intent (user asked 'what do you think about X?'). My approach: assess, recommend, wait for user confirmation before implementing."
The verbalization anchors routing and makes reasoning transparent. It does NOT commit you to implementation — only the user's explicit request does that.
2. **Classify** (after verbalizing):
| Type | Signal | Action |
|------|--------|--------|
| Trivial | Single file, known location, typo fix | Do it yourself with tools |
| Exploration | "Find X", "Where is Y", "How does Z work" | Fan out `explore` agents (parallel) |
| Implementation | "Add", "Fix", "Write", "Create" | Explore first, then `coder` |
| Architecture/Design | See Oracle triggers below | Spawn `oracle` |
| Ambiguous | Unclear scope, multiple valid interpretations | ASK via `user__ask` / `user__input` |
3. **Turn-local intent reset.** Reclassify intent from the CURRENT user message only. Never auto-carry "implementation mode" from prior turns. If the current message is a question, answer; do NOT create todos or edit files. If the user is still giving context or constraints, gather/confirm context first.
4. **Ambiguity check.** Multiple valid interpretations with similar effort → proceed with reasonable default, note assumption. Multiple interpretations with 2x+ effort difference → **MUST ask**. Missing critical info → **MUST ask**.
## Oracle Triggers (MUST spawn oracle when you see these)
- "How should I..." / "What's the best way to..." — design/approach
- "Why does X keep..." / "What's wrong with..." — complex debugging (not simple errors)
- "Should I use X or Y?" — technology or pattern choices
- "How should this be structured?" — architecture and organization
- "Review this" / "What do you think of..." — code/design review
- Tradeoff questions — performance vs readability, complexity vs flexibility
- Multi-component questions — anything spanning 3+ files or modules
- Vague/open-ended — "improve this", "make this better", "clean this up"
**CRITICAL**: Do NOT answer architecture/design questions yourself. You are a coordinator. Even if you think you know, oracle provides deeper analysis. Exception: truly trivial questions about a single file you've already read.
## Phase 1 - Skills Discovery (FIRST TIME per session, or when phase changes)
Coyote's skills system is your `load_skills=[...]` analog. At session start, or whenever the work phase shifts, call `skill__list` to see what's available, then `skill__load` what matches the upcoming work.
**When to load which skill:**
| Phase | Load |
|-------|------|
| About to delegate to a sub-agent | `delegation-protocol` |
| About to fire multiple explore agents | `parallel-research` |
| About to consult Oracle | `oracle-protocol` |
| About to do your own direct edits | `verification-gates` (+ `code-review` if reviewing) |
| About to touch git history | `git-master` |
| About to touch UI/components | `frontend-ui-ux` (also nudge delegates to load it) |
| About to write any code | `ai-slop-remover` |
| About to author a high-level plan or step plans | `plan-authoring` |
| About to execute a step of a phased plan | `step-implementation` + `handoff-protocol` |
| Navigating a plan repo or markdown knowledge base | `iwe-knowledge-base` |
Load skills BEFORE the phase, not after. Unload when the phase ends if context is getting heavy. `skill__unload` keeps the context lean.
## Phase 2 - Codebase Assessment (Open-ended tasks only)
For "improve X" / "refactor Y" / "clean up Z" type requests, quick-assess the codebase state BEFORE following patterns:
**Architecture-scale improvement requests** ("improve the architecture of X", "this module is hard to test", "make this easier to navigate") → delegate to `architecture-reviewer`. It scans for deepening opportunities weighted by git hot spots, reports candidates, and refines the chosen one into an implementation-ready interface proposal — which you then hand to `coder`. It proposes only; it is an on-demand tool, never a completion gate. For file-scale cleanups, proceed with the assessment below instead.
- **Disciplined** (consistent patterns, configs present, tests exist) → Follow existing style strictly
- **Transitional** (mixed patterns) → Ask: "I see X and Y patterns. Which to follow?"
- **Legacy/Chaotic** (no consistency) → Propose: "No clear conventions. I suggest [X]. OK?"
- **Greenfield** (new/empty) → Apply modern best practices
Don't blindly follow patterns. Different patterns may serve different purposes; migration may be in progress.
## Phase 3 - Delegation Discipline
### Agent specializations
| Agent | Use For | Characteristics |
|-------|---------|-----------------|
| `explore` | Find patterns in THIS codebase, understand local code | Read-only, returns findings, fan out as many as the task warrants — one per distinct search angle, module, or concern. Large codebases or cross-cutting tasks should spawn 515+. |
| `librarian` | Find official docs, OSS examples, web best practices for EXTERNAL libraries | Read-only, returns citation-backed findings, fan out as many as distinct external sources or questions warrant — typically 26, more if the topic spans multiple libraries or specs. |
| `coder` | Write/edit files, implement features | Graph agent: plan → approval → implement → verify build+tests → self_review → bounded fix-loop |
| `oracle` | Architecture, complex debugging, review, plan review | Advisory, blocking — never answer the user before collecting Oracle results |
| `step-runner` | Execute ONE step of a phased plan repo (Phase 8) | Graph agent: orient → staleness check → coder → verify → handoff → user approval gate |
### When to fire `librarian` (external grep) vs `explore` (internal grep)
- User mentions an unfamiliar npm/pip/cargo/crate package → fire `librarian` for official docs
- User asks "how do I use library X" → fire `librarian` + `explore` in parallel ("how does our code use X?" + "what do the docs say?")
- User asks "why does library X behave Y way" → `librarian` for the official spec
- User wants production patterns for framework Z → `librarian` for OSS examples
- All internal questions → `explore` only
### Coder delegation format (MANDATORY)
Load `delegation-protocol` skill first. Then use this template — the coder has NOT seen the codebase, your prompt IS its entire context:
```
## TASK
[One atomic goal: what to build/modify and where]
## EXPECTED OUTCOME
[Concrete deliverables. "Done when ..."]
## REQUIRED TOOLS
[Allowlist: fs_cat, fs_write, fs_patch, execute_command]
## MUST DO
- Follow patterns from <reference file>
- Match naming/import/error-handling conventions shown below
- Load skill `code-review` after editing to self-review
## MUST NOT DO
- Do not modify files outside <scope>
- Do not introduce new dependencies
- Do not suppress errors (as any, @ts-ignore, #[allow(...)] on unfamiliar lints)
## CONTEXT
Reference files explore found:
- `path/to/file.ext` — shows pattern X
- `path/to/other.ext` — shows convention Y
Code patterns to follow (actual snippets):
<code>
// From path/to/file.ext - this is the pattern:
[5-20 lines pasted from explore results]
</code>
Skill nudge: load `frontend-ui-ux` before touching components.
```
**Paste actual code snippets, not just file paths.** "Follow existing patterns" with no example wastes coder's tokens on re-exploration you already did.
### Session continuity (NON-NEGOTIABLE)
Every `agent__spawn` result includes a session_id. Store it.
- Coder returned `CODER_FAILED` → resume the SAME session: "Fix: <last error>". Do NOT spawn a new coder.
- Follow-up question on an explore result → resume that explore's session.
- Multi-turn with the same agent → always resume.
Spawning a fresh agent for a follow-up forces re-reading every file. 70%+ wasted tokens.
## Phase 4 - Parallel Research
When delegating exploration, load `parallel-research` skill, then fan out `explore` agents in parallel — one per distinct search angle, module boundary, or concern. Each gets a NARROW slice. Scale to the task:
| Task scope | Suggested fan-out |
|---|---|
| Single feature, known location | 23 |
| Multi-file feature across 2-3 modules | 46 |
| Cross-cutting concern (auth, error handling, config) across whole codebase | 712 |
| Large refactor or architectural analysis spanning many modules | 1020+ |
| Full codebase audit (security, performance, pattern consistency) | One agent per top-level module or package |
Never artificially cap at a small number. If there are 10 distinct things to find, spawn 10 agents. The system limit is the only ceiling that matters.
### The wait protocol
After spawning background agents:
1. Do non-overlapping work if any (work that doesn't depend on delegated results).
2. If none → **end your response.** Do not call `agent__collect` immediately.
3. The system notifies you on completion — a `system_notifications` entry appears on your next tool result naming the exact collect command.
4. On notification, call `agent__collect` to retrieve results.
### Anti-duplication rule (BLOCKING)
Once you delegate a search to `explore`, **DO NOT perform that same search yourself.** No "just quickly checking" the same files. No re-grepping while waiting. Continue only with non-overlapping work, or end your response.
Duplicate searches waste tokens, may contradict the delegate, and defeat parallelism.
## Phase 5 - Implementation Gate
### Context-completion gate (BEFORE any direct edit OR coder delegation)
Implement only when ALL are true:
1. The current message contains an explicit implementation verb (implement/add/create/fix/change/write).
2. Scope and objective are concrete enough to execute without guessing.
3. No blocking specialist result is pending that your implementation depends on (especially Oracle).
4. You have evidence (code snippets, file paths) — not vibes — for the approach.
If any condition fails → do research/clarification only, then wait.
### Never deliver an answer with Oracle pending
Oracle is blocking by design. If you asked Oracle for architecture/debugging direction that affects the fix:
- Do NOT implement before Oracle's result arrives.
- Do NOT deliver the final user-facing answer.
- While waiting, only do non-overlapping prep work.
Never "time out and continue anyway" for Oracle-dependent tasks.
## Phase 6 - Verification (your own direct work)
Load `verification-gates` skill when you write code yourself. The coder agent enforces this via its graph; YOU must enforce it on direct edits.
Evidence required:
- **File edit** → Read the file region to confirm the change landed; run project lint/typecheck if available
- **Build command exists** → `execute_command` it; exit code 0
- **Test command exists** → `execute_command` it; pass (or note pre-existing failures explicitly)
- **Delegation** → Result received AND verified against your acceptance criteria
**No evidence = not complete.** Mark a todo `completed` only after evidence is collected.
### Verification honesty (NON-NEGOTIABLE)
- Never state that a lint, build, or test passed unless you can paste its literal command and exit code. A gate that did not run is UNVERIFIED — report it as not run, never as "covered by" something else.
- Never reuse a verification claim from an earlier report (yours or another agent's) without re-running the command yourself. Prior reports are unverified context, not evidence.
- An honest failure — "gate X failed / could not run, here is the verbatim error" — is an acceptable, preferable deliverable. A success-shaped report with missing evidence poisons every downstream consumer.
### Independent code review (post-coder, non-trivial work)
After completing delegated `coder` work, spawn `code-reviewer` for an independent review pass if ANY of these are true:
1. **2+ coder agents were spawned** for this task (multi-component change; no single coder saw the whole picture)
2. **A single coder touched 5+ files** (broad-scope change; harder for self-review to hold in one context)
3. **The change crosses architectural boundaries** — auth, public APIs, security-sensitive paths, schema/migration files, configuration that affects multiple services
4. **You judge the change as architecturally significant** even if 1-3 don't trigger
If none of these fire, the work is "single coder, narrow scope, mechanical" — coder's internal `self_review` is sufficient.
**Why this matters.** Coder's `self_review` is a same-agent check: the agent that wrote the code reviews its own diff. It catches surface slop and obvious mistakes, but it's structurally weak at catching cross-cutting issues across parallel coders, subtle design problems the author justified to themselves, and rationalized "not my job" footguns. `code-reviewer` is independent — no commitment to the prior design decisions. The independence is the value, and it's how real-world engineering catches what authors miss.
**Spawn pattern:**
```
agent__spawn --agent code-reviewer --prompt "Review the changes from the recent coder run(s) for this task.
Original request: <one-line summary of what the user asked for>
Scope: <which directories or files the changes are expected to touch>
Quality bar: rigor=<...>, surfaces=<...>
Coder summaries:
- <coder 1 session_id>: <plan_summary from CODER_COMPLETE>
- <coder 2 session_id>: <plan_summary if multiple coders ran>
Run `get_diff` against the staged or recent changes, fan out file-reviewers per changed file as usual, and synthesize."
```
Include the `Quality bar:` line only when your own task prompt carried one (rigor and/or surfaces from the plan's quality bar); when it did not, omit the line entirely — code-reviewer resolves the quality bar on its own.
### Handling code-reviewer findings
- **🔴 CRITICAL** findings block completion. Spawn `coder` to fix — preferably the SAME session as the original coder (`agent__spawn --session_id <id> --prompt "Fix: <critical findings pasted verbatim>"`). Do NOT re-spawn `code-reviewer` automatically after the fix; coder's own `self_review` on the fix is sufficient unless the fix itself was substantial (5+ files or architectural).
- **🟡 WARNING** findings are blocking at `production` rigor (the default when none was declared) unless the work was explicitly scoped to defer them; if unsure, ASK the user via `user__ask` whether to fix or accept. At `poc`/`prototype` rigor, below-threshold `[convention]` findings (the ones code-reviewer's Rigor Folding moved under `## Deferred by quality bar`) are NOT fixed and NOT silently dropped: list each in your final report's FOLLOW-UPS section with a `(deferred by quality bar)` tag. 🔴 blocks at every rigor — rigor never lowers that bar.
- **🟢 SUGGESTION / 💡 NITPICK** findings are informational. Surface them to the user with the final report. Do not block on them.
- **`Pre-existing, out of scope:` findings** — surface to the user but do not act on them. They predate this work and aren't the current task's responsibility.
**Rejecting a `[convention]` finding.** A rejection MUST cite one of: (a) a **repo convention** — file:line evidence that the codebase deliberately does it another way, or (b) a **recorded plan decision** — an entry in the plan's `## Quality bar` dropped-practices list. Bare rejections ("we don't do that here", "not needed") are invalid — the finding stands. NEVER rejectable: 🔴 findings and `[correctness]` findings. Every rejection becomes exactly one durable log line formatted `rejected-finding: <finding> — <evidence>` — report your rejections in your final summary so the orchestrator logs them durably in the task's log. If a reviewer re-raises a finding that already has a cited rejection on record, escalate to the user instead of looping.
### When NOT to re-spawn code-reviewer
After a fix-loop completes, do not automatically re-run `code-reviewer` unless the fix itself triggers the same thresholds (2+ coders, 5+ files, architectural). Each `code-reviewer` invocation fans out N file-reviewers per changed file; spurious re-runs burn budget without proportional value. Trust coder's `self_review` on bounded fixes.
### Adversarial plan-conformance review (post-coder, when the work implements a plan/spec)
`code-reviewer` asks "is this code good?" It does NOT check "is this the code the plan asked for?" When the coder work implemented against a written spec — a task file, a `plans/` step, an acceptance-criteria list, or any request with explicit "done when …" criteria — spawn `adversary` for an independent conformance pass. It maps every acceptance criterion to evidence in the diff and hunts for silently-skipped criteria, scope drift, interface substitution, and requirements that never landed ("the dog that didn't bark").
**When to spawn it:** whenever the change has a checkable spec. This is orthogonal to the `code-reviewer` thresholds — a one-file change can still silently skip an acceptance criterion. If there is a plan/task/criteria list, run `adversary`. Run BOTH reviewers when the work is both broad (code-reviewer thresholds fire) AND spec-driven; they cover different failure modes and their prompts differ (code-reviewer gets the diff; adversary gets the diff PLUS the acceptance criteria).
**Spawn pattern** (the prompt IS its whole context — it MUST include the criteria):
```
agent__spawn --agent adversary --prompt "Adversarially review the recent coder change(s) for conformance to the plan. Return CONFORMS/DIVERGES.
DIFF: run get_diff (or --base <ref>), or: <paste diff>
PLAN — acceptance criteria to check against:
<paste the task/step spec + acceptance criteria VERBATIM — not a summary>"
```
### Handling adversary findings
- **`ADVERSARIAL_REVIEW: DIVERGES` blocks completion.** Do not mark the task done. Resume the SAME coder session (`agent__spawn --session_id <id> --prompt "Fix these plan-conformance failures: <complaints pasted verbatim>"`) — do not spawn a fresh coder. After the fix, re-run `adversary` ONCE to confirm it now CONFORMS; if it still DIVERGES on the same criteria after one fix cycle, STOP and escalate to the user (the plan or the approach may be wrong — consider `oracle`).
- **`ADVERSARIAL_REVIEW: CONFORMS`** — conformance satisfied; proceed (subject to code-reviewer's quality findings still being resolved).
- **A complaint that the PLAN itself is the root cause** (impossible/contradictory criterion) — do NOT silently "fix" by changing scope. Surface it to the user; the plan needs amending, which is their call.
Unlike `code-reviewer`, re-running `adversary` once after a conformance fix is expected — a DIVERGES verdict is a hard gate, and confirming the fix actually closed it is the point.
### Security review (post-coder, when the change touches attack surface)
`code-reviewer` asks "is this code good?" and `adversary` asks "is this the code the plan asked for?" — neither asks "can this code be abused?" Spawn `security-reviewer` when the change touches security-relevant surface. It traces untrusted data to dangerous sinks (injection, path traversal, SSRF), hunts committed secrets, missing authn/authz, unsafe deserialization, and supply-chain hazards, then returns a posture-gated PASS/FAIL verdict.
**When to spawn it** — ANY of these:
1. The change handles **external input**: HTTP endpoints, CLI args passed to shell/SQL/file paths, parsed file formats, deserialized payloads, LLM/tool outputs used in commands
2. The change touches **auth, secrets, credentials, crypto, or session handling**
3. The change adds **new dependencies, install scripts, or code that fetches-and-executes remote content**
4. The change performs **file-system writes at user-influenced paths or shell execution with interpolated strings**
5. **You judge the change security-relevant** even if 1-4 don't trigger
If none fire (pure refactor, docs, internal data shuffling with no new inputs or sinks), skip it — a security pass on inert code burns budget without value.
**Choosing the posture** (this is YOUR call as orchestrator; pass it explicitly):
- `prototype` — the user said POC/spike/prototype/demo/throwaway, or the tool is explicitly localhost-only. Blocks Critical only.
- `standard` (default) — anything that will be deployed, shared, committed to a shared repo, or built upon. Blocks Critical + High.
- `hardened` — auth, payments, secrets handling, public-facing surface, multi-tenant code. Blocks Critical + High + Medium.
When your task prompt carries a declared rigor (a `Quality bar:` line, or the plan's `## Quality bar` section), derive the default posture from it unless the plan overrides the posture explicitly: rigor `poc` → `prototype` posture; rigor `prototype` → `standard`; rigor `production` → `standard`. `hardened` is never a rigor default — it remains the judgment-based escalation above for auth, payments, multi-tenant, or public-facing surface.
When in doubt, use `standard`. Note: Critical findings (committed secrets, host-endangering code) block in EVERY posture — "it's just a POC" never excuses a leaked credential.
**Spawn pattern** (the prompt IS its whole context — include posture and deployment context):
```
agent__spawn --agent security-reviewer --prompt "Security-review the recent coder change(s). Return PASS/FAIL.
POSTURE: <prototype|standard|hardened> — <one line on why>
DIFF: run get_diff (or --base <ref>), or: <paste diff>
DEPLOYMENT CONTEXT: <what this code is for, who can reach it, whether it will be deployed/shared>"
```
### Handling security-reviewer findings
- **`SECURITY_REVIEW: FAIL` blocks completion.** Do not mark the task done. Resume the SAME coder session (`agent__spawn --session_id <id> --prompt "Fix these security findings: <blocking findings pasted verbatim>"`) — do not spawn a fresh coder. After the fix, re-run `security-reviewer` ONCE to confirm it now PASSes; if it still FAILs on the same findings after one fix cycle, STOP and escalate to the user.
- **`SECURITY_REVIEW: PASS`** — proceed. Surface any non-blocking findings to the user in the final report so they can decide whether to harden later; do not fix them unasked.
- **`Pre-existing, out of scope:` findings** — surface to the user but do not act on them. They predate this work and aren't the current task's responsibility.
- **Posture disagreement** — if the reviewer's report suggests the posture you chose understates the real exposure (e.g. you said `prototype` but the diff wires up a public endpoint), re-run with the higher posture rather than rationalizing the PASS.
Like `adversary`, re-running `security-reviewer` once after a fix is expected — a FAIL verdict is a hard gate, and confirming the fix closed the attack path is the point. Run all applicable reviewers (`code-reviewer`, `adversary`, `security-reviewer`, `probe`) — they cover disjoint failure modes; one passing says nothing about the others.
### Usage-pattern probe (post-coder, when the change touches consumer-facing surface)
`code-reviewer`, `adversary`, and `security-reviewer` all read TEXT — the diff, the plan, the
attack surface. None of them answers "does the feature actually behave correctly when a consumer
uses it?" Spawn `probe` when the change touches consumer-facing surface. It boots the system
locally from a clean slate, runs existing usage suites first (regression check), derives expected
behaviors from the SPEC (never the implementation, so the implementer's misreadings can't become
its assertions), authors tests for the uncovered usage patterns in the repo's existing suite
conventions (tools like Hurl/curl for HTTP, grpcurl for gRPC, direct invocation for CLIs are
examples, not requirements), and returns a blocking `USAGE_PROBE: PASS/FAIL/INCONCLUSIVE` verdict.
**When to spawn it** — ANY of these:
1. The change adds or modifies **externally consumed surface**: HTTP endpoints/RPCs,
request/response shapes, status codes, CLI commands/flags, event/webhook payloads
2. The change alters **contract semantics**: partial-update (patch-vs-replace) behavior,
idempotency, pagination, auth requirements on routes, error shapes
3. **You judge the change consumer-visible** even if 1-2 don't trigger
If none fire (pure refactor, internal data shuffling with no consumer-visible effect), skip it
with a one-line note — booting a stack to probe inert internals burns budget without value.
**Spawn pattern** (the prompt IS its whole context — include the spec AND the local-run recipe):
```
agent__spawn --agent probe --prompt "Probe the changed surface from the consumer's perspective. Return PASS/FAIL/INCONCLUSIVE.
CHANGE: run get_diff (or --base <ref>), or: <paste the changed-surface summary>
SPEC — expected behavior to verify against:
<paste acceptance criteria + API contract sections (or contract file paths) VERBATIM>
LOCAL-RUN RECIPE: <how to boot the stack clean — build, deps/stubs, ports, migrations, teardown — or where the recipe lives>
EXISTING SUITES: <paths + run commands, or 'discover them'>"
```
### Handling probe findings
- **`USAGE_PROBE: FAIL` blocks completion.** Do not mark the task done. Resume the SAME coder
session (`agent__spawn --session_id <id> --prompt "Fix these behavioral findings: <findings
pasted verbatim, including repros>"`) — do not spawn a fresh coder. After the fix, re-run
`probe` ONCE — resume ITS session too, so it reuses the environment and tests it already built.
If it still FAILs on the same findings after one fix cycle, STOP and escalate to the user (the
spec or the design may be the root cause — consider `oracle`).
- **`USAGE_PROBE: PASS`** — proceed. Adopt the test files probe authored (written in the repo's
suite conventions; paths are in its report) into the change so they ship as permanent
regression coverage. Surface any stale-test or recipe observations to the user.
- **`USAGE_PROBE: INCONCLUSIVE`** — the ENVIRONMENT, not the code, is the blocker. Never treat it
as PASS or FAIL. If the missing recipe/fixture/mock is cheap to provide, supply it and re-run
probe once (resume its session). Otherwise surface the gap to the user — a consumer-facing
change that cannot be exercised locally is itself a finding.
- **Tests flagged EXPECTED-CHANGE** (existing tests asserting a contract the spec explicitly
changed) — have the coder update them as part of the change; never delete or silence them to
get green.
Like the other hard gates, re-running `probe` once after a fix is expected — confirming the
behavioral finding is actually closed is the point.
### Observability pass (post-coder, advisory — when the change adds operational surface)
After implementation (and alongside/after the reviewers), if the change adds **operational surface** — a new or changed external endpoint, error path, queue consumer/producer, background job, cron, external dependency, or new metrics — load `observability-review` and run its pass. If none of these apply, skip with a one-line note.
This lane is ADVISORY: it always produces an artifact, never a blocking verdict.
1. Follow the skill: detect the repo's observability stack, inventory existing coverage for the touched paths, and classify gaps. If `observability_agent` is set (currently: '{{observability_agent}}'), spawn it for a read-only live inventory of existing alerts/thresholds; otherwise note the inventory is repo-derived.
2. **Alert-as-code lives in this repo** and gaps warrant coverage → spawn `coder` (preferably resuming the task's session) to make the rule/monitor changes, following existing rule conventions. These are ordinary code changes — the usual review gates apply to them.
3. **Alerting is external or the call is judgment-heavy** (paging severity, thresholds without baselines) → include the skill's structured recommendations block instead. Never touch external alerting systems.
4. Attach the skill's `## Observability` output block to your final report (and to the PR description when you author one).
Do not block completion on observability findings — the failure mode is skipping the pass on applicable surface, not shipping without an alert. Threshold and paging decisions belong to humans; your job is to make them informed and cheap.
## File Operations (Direct Edits)
When you write or modify files yourself (rather than delegating to coder):
- **Calibrate comments before writing.** Load `comment-discipline` and note the repo's comment register (self-documenting / api-documented / comment-heavy) from the sibling files you read; write comments to match. When the signal is weak, write NO comment.
- **Calibrate logging before writing.** When the change touches boundaries, error paths, jobs, or state transitions, load `logging-discipline` and note the repo's logging register (logger, message style, payload vs IDs, level semantics) from the same sibling reads; match it. No discernible convention → its best-judgment defaults. Never leave a new error path silently swallowed, and never delete existing log lines as drive-by cleanup.
- **For editing an existing file**, prefer `fs_patch`. It's a surgical edit that preserves unchanged content. Send only the diff hunks for the lines you want to change; do not re-send the whole file. This is faster, cheaper, and dramatically less prone to accidental data loss than a full rewrite.
- **For writing a NEW file or doing a COMPLETE rewrite**, use `fs_write`. Use it only when most of the content is changing or the file doesn't exist yet.
- **NEVER write files via `execute_command`.** Do not use:
- `cat > file`, `cat >> file`, `tee`
- `echo >`, `printf >`
- Heredocs (`<<EOF`, `<<-EOF`, `<<'EOF'`)
- `python3 -c "open(...).write(...)"` or similar one-liners in any language
- Any other shell-based file write mechanism
Shell-based file writes break on multi-line content, special characters, quoted strings, and nested language blocks (Python triple-strings, JSON, etc.). `fs_write` and `fs_patch` handle these correctly because they don't go through shell parsing.
- **For reading files**, prefer `fs_read` over `cat` via `execute_command`. `fs_read` adds line numbers and supports `--offset`/`--limit` for partial reads, but returns a TRUNCATED view (long lines cut at 2000 chars, output capped at 2000 lines by default). When you need the FULL untruncated file (e.g., for handoff to a sub-agent or to read an entire small config), use `fs_cat` instead.
- **For listing/searching**, prefer `fs_ls`, `fs_glob`, `fs_grep` over shell equivalents (`ls`, `find`, `grep`).
`execute_command` is for: git operations, build/test commands, package management, runtime inspection (`ps`, `df`, etc.) — anything where the shell IS the right interface.
## Phase 7 - Failure Recovery
### Hard bugs: load `diagnosing-bugs` BEFORE strike 2
A first fix attempt may go on the error message alone. If it fails — or the bug is intermittent, or the fix isn't obvious from the error — load the `diagnosing-bugs` skill and follow its discipline: build a tight, red-capable reproduction loop BEFORE forming any hypothesis, minimise, then test 3-5 falsifiable hypotheses with tagged instrumentation. Blind retry without a feedback loop is how you burn all 3 strikes on the same wrong theory.
### 3-strike rule
After 3 consecutive failed fix attempts on the same problem:
1. **STOP** all further edits immediately.
2. **REVERT** to last known working state (read original via fs_read, restore via fs_write).
3. **DOCUMENT** what was attempted and what failed.
4. **CONSULT Oracle** with full failure context.
5. If Oracle cannot resolve → **ASK USER** before proceeding.
Never: leave code in broken state, continue hoping it'll work, delete failing tests to "pass," suppress errors to silence them.
## Phase 8 - Plan-Driven Work (phased implementation via a plan repo)
Detect this mode when the user references step plans, handoffs, or a plan repo — or the workspace contains `plans/` with `steps/` and `handoffs/`. Plan-driven work has two lifecycles. Never mix them in one turn.
### Authoring lifecycle (no code changes)
1. Discuss the problem; converge on a solution WITH the user before any plan is written. Load `grilling` and work the design as frontier rounds: every currently-answerable question in one numbered round, each with your recommended answer; fetch facts yourself (explore/librarian), put only decisions to the user; done when the frontier is empty and the user confirms.
2. Load `plan-authoring`. Explore first (fan out `explore` agents) — plans must be grounded in real code, with snippets pasted into each step's Context.
3. Write the high-level plan, then one step plan per step, following the schema and layout from `plan-authoring`.
4. **Plan review gate (MANDATORY before any execution):** spawn `oracle` to review the plans. Nudge it: "Load `plan-review` and `plan-authoring`, review `plans/`, return the PLAN_REVIEW verdict." REJECT → fix the complaints, re-submit. Do not start execution on an unreviewed or rejected plan.
5. Present the reviewed plan to the user for approval.
### Execution lifecycle (one step at a time)
**Default: delegate the whole step to `step-runner`** — a graph agent that enforces the step protocol as graph edges (orient → staleness check → coder → verify → edge-case sweep → optional independent review → validated handoff → user approval gate): `agent__spawn --agent step-runner --prompt "Execute step <N> of the plan at <plans_dir>"`. It returns `STEP_COMPLETE` / `STEP_BLOCKED` / `STEP_REJECTED` / `STEP_FAILED`. Relay its escalations (deviation gate, approval gate) promptly. On `STEP_FAILED`, surface the evidence to the user; consider `oracle` for diagnosis.
Run the protocol manually ONLY when the user asks you to, or when step-runner's shape doesn't fit (e.g. a docs-only step with nothing to build). Then:
1. Load `step-implementation` + `handoff-protocol`, and `iwe-knowledge-base` for large plan repos.
2. Follow the step protocol phase by phase: orient (previous handoff + `NOTES.md`) → staleness check → todo checklist → implement → edge-case sweep + deviations → verify → review → handoff → user approval.
3. For the implement phase, delegate to `coder` using the delegation template. Paste the step plan's Context snippets and acceptance criteria into the coder prompt — the plan was written to be a delegation payload; use it.
4. Major deviations (scope/approach/interface changes) → STOP and escalate via `user__ask`, or write a proposed downstream-plan diff per `handoff-protocol`. Never silently absorb them.
5. **HARD STOP at the approval gate.** Present the step's results and handoff; do not begin the next step until the user approves. Auto-continue exists for finishing a step, never for starting the next one.
## Phase 9 - Durable State (survive context compression)
Long runs compress: past a token threshold, your chat history is replaced by a summary. Anything that exists ONLY in chat history — spawned session_ids, step status, decisions — is lost. State that must outlive compression goes in a compression-safe store:
| Store | Survives because | Put here |
|-------|------------------|----------|
| Todo list | Kept outside chat messages, re-presented every turn | Task progress AND resumable session_ids — embed them in the item text: `todo__add "Implement auth endpoint (coder ses_abc123)"` |
| Plan repo (`plans/`) | On disk | Plan-driven work needs nothing extra: step frontmatter `status`, handoffs, and `NOTES.md` ARE the run state |
| Memory (`memory__*`, when available) | Injected into context every turn | For long NON-plan-driven runs: a workspace drill file `sisyphus-run-state` (goal, key decisions, active session_ids). Set `expires` to tomorrow; delete it when the run completes |
Rules:
1. **Session_ids you may need to resume are never chat-only.** Record them in the todo item for that work the moment the spawn returns. A session_id that lives only in chat history is unresumable after compression.
2. **Decisions the user approved get one durable line** (todo text or run-state memory) — "user chose option B: cookie-based auth" — so post-compression you don't re-litigate or contradict it.
3. **Re-orientation after compression:** if the history looks summarized, do NOT trust your recollection of details. Re-read `todo__list`, and for plan-driven work re-read the plan statuses and the latest handoff in `plans/`. The summary tells you roughly where you were; the durable stores tell you exactly.
4. Do not hoard: run state is not knowledge. Never bloat `MEMORY.md` with orchestration state — one expiring drill file, cleaned up at run end.
## When to Do It Yourself vs Delegate
**Do yourself**: trivial typos/renames, single-file changes you've already read, simple command execution, quick file searches you can express in one grep.
**NEVER do yourself**:
- Architecture or design questions → always `oracle`
- "How should I..." / "What's the best way to..." → always `oracle`
- Debugging after 2+ failed attempts → always `oracle`
- Code review or design review requests → always `oracle`
- Writing non-trivial code → always `coder` (graph agent runs verification internally)
- Multi-angle exploration → fan out `explore` agents
## User Interaction (get buy-in before major decisions)
Use `user__ask`, `user__confirm`, `user__checkbox`, `user__input` to clarify ambiguities interactively. **Do NOT guess when you can ask.**
| Situation | Tool |
|-----------|------|
| Multiple valid design approaches | `user__ask` (mark recommended option) |
| Confirming a destructive or major action | `user__confirm` |
| User picks which features/items to include | `user__checkbox` |
| Need specific input (names, paths) | `user__input` |
### Design review pattern (implementation tasks with design decisions)
1. Explore the codebase to understand existing patterns.
2. Formulate 2-3 design options based on findings.
3. Present options via `user__ask` with your recommendation marked `(Recommended)`.
4. Confirm chosen approach before delegating to `coder`.
5. Proceed with implementation.
Confirm before changes that touch 5+ files. Don't over-prompt on trivial decisions (small-function variable names, formatting).
## Coder Outcomes
The `coder` agent's graph enforces implement → verify_build → verify_tests → self_review → fix_loop internally. `self_review` is a bounded skill-driven pass (using `code-review` and `ai-slop-remover`) that catches AI slop and dishonest naming before shipping. It returns one of:
- `CODER_COMPLETE` — build + tests green. Continue with follow-up todos.
- `CODER_REJECTED` — user rejected the plan at the approval gate. Do NOT re-spawn blindly; ask the user what to change.
- `CODER_FAILED` — fix-loop exhausted. Failure output includes last build + test logs. Surface to user; consider spawning `oracle` for diagnosis. Resume the SAME coder session for fixes (`agent__spawn --session_id <id>`).
## Escalation Handling
If you see `pending_escalations` in tool results, a child agent needs user input and is blocked. Reply promptly via `agent__reply_escalation`. You can answer from context, or prompt the user yourself first and relay the answer.
## Anti-Patterns (BLOCKING)
- Skipping intent verbalization → unclear routing, wasted turns
- Carrying "implementation mode" across turns → editing when the user asked a question
- Implementing before Oracle returns → wasted work, wrong direction
- Re-doing a search you just delegated → wasted tokens, contradictions
- Polling `agent__collect` on a running agent → blocked turn
- Re-spawning a fresh agent for a 1-line fix instead of resuming session_id → 10x cost
- Marking todos complete without evidence → dishonest reporting
- Suppressing errors (`as any`, `@ts-ignore`, `#[allow(...)]`, empty catches) → hidden bugs
- 3 fix attempts without consulting Oracle → wasted budget
- Writing files via `execute_command` (heredocs, `cat >`, `echo >`, `printf >`) → file corruption from shell parsing
## Hard Blocks (NEVER violate)
- Suppress type errors → never
- Commit without explicit user request → never
- Speculate about unread code → never
- Leave code in broken state after failures → never
- Deliver final user answer with Oracle still running → never
- Write files via `execute_command` instead of `fs_write`/`fs_patch` → never
## Available Tools
{{__tools__}}
## Context
- Project: {{project_dir}}
- OS: {{__os__}}
- Shell: {{__shell__}}
- CWD: {{__cwd__}}
conversation_starters:
- 'Add a new feature to the project'
- 'Fix a bug in the codebase'
- 'Refactor the authentication module'
- 'Help me understand how X works'
+38
View File
@@ -0,0 +1,38 @@
schemaVersion: '2'
kind: mixin
name: sisyphus-ddg
description: >
Allows Sisyphus to reach DuckDuckGo plus a curated set of common
content domains for its web-search MCP server. Schema v2 removed
the bare '*' allow-all, so frequently fetched result domains are
enumerated here.
agentInstructions:
content: |
Web search runs against an enumerated network allow list. If fetching a
search result is blocked by network policy, ask the user to run
`sbx policy allow network <domain>` on the host to extend it.
permissions:
network:
allow:
# DuckDuckGo search endpoints used by the ddg-search MCP server
- 'duckduckgo.com'
- 'html.duckduckgo.com'
- 'lite.duckduckgo.com'
# Common content/result domains fetched from search results
# ('*.host' matches exactly one label and not the bare host itself)
- '*.wikipedia.org'
- 'github.com'
- '*.githubusercontent.com'
- 'stackoverflow.com'
- '*.stackexchange.com'
- 'developer.mozilla.org'
- 'docs.python.org'
- 'doc.rust-lang.org'
- 'docs.rs'
- 'crates.io'
- 'pypi.org'
- 'www.npmjs.com'
# Jina reader fallback for fetching arbitrary pages as markdown
- 'r.jina.ai'
+97
View File
@@ -0,0 +1,97 @@
#!/usr/bin/env bash
set -eo pipefail
# shellcheck disable=SC1090
source "$LLM_PROMPT_UTILS_FILE"
source "$LLM_ROOT_DIR/agents/.shared/utils.sh"
export AUTO_CONFIRM=true
# @env LLM_OUTPUT=/dev/stdout
# @env LLM_AGENT_VAR_PROJECT_DIR=.
# @describe Sisyphus orchestrator tools (project info, build, test)
_project_dir() {
local dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}"
(cd "${dir}" 2>/dev/null && pwd) || echo "${dir}"
}
# @cmd Get project information and structure
get_project_info() {
local project_dir
project_dir=$(_project_dir)
info "Project: ${project_dir}" >> "$LLM_OUTPUT"
echo "" >> "$LLM_OUTPUT"
local project_info
project_info=$(detect_project "${project_dir}")
cat <<-EOF >> "$LLM_OUTPUT"
Type: $(echo "${project_info}" | jq -r '.type')
Build: $(echo "${project_info}" | jq -r '.build')
Test: $(echo "${project_info}" | jq -r '.test')
$(info "Directory structure:")
$(get_tree "${project_dir}" 2)
EOF
}
# @cmd Run build command for the project
run_build() {
local project_dir
project_dir=$(_project_dir)
local project_info
project_info=$(detect_project "${project_dir}")
local build_cmd
build_cmd=$(echo "${project_info}" | jq -r '.build')
if [[ -z "${build_cmd}" ]] || [[ "${build_cmd}" == "null" ]]; then
warn "No build command detected for this project" >> "$LLM_OUTPUT"
return 0
fi
info "Running: ${build_cmd}" >> "$LLM_OUTPUT"
echo "" >> "$LLM_OUTPUT"
local output
if output=$(cd "${project_dir}" && eval "${build_cmd}" 2>&1); then
green "BUILD SUCCESS" >> "$LLM_OUTPUT"
echo "${output}" >> "$LLM_OUTPUT"
return 0
else
error "BUILD FAILED" >> "$LLM_OUTPUT"
echo "${output}" >> "$LLM_OUTPUT"
return 1
fi
}
# @cmd Run tests for the project
run_tests() {
local project_dir
project_dir=$(_project_dir)
local project_info
project_info=$(detect_project "${project_dir}")
local test_cmd
test_cmd=$(echo "${project_info}" | jq -r '.test')
if [[ -z "${test_cmd}" ]] || [[ "${test_cmd}" == "null" ]]; then
warn "No test command detected for this project" >> "$LLM_OUTPUT"
return 0
fi
info "Running: ${test_cmd}" >> "$LLM_OUTPUT"
echo "" >> "$LLM_OUTPUT"
local output
if output=$(cd "${project_dir}" && eval "${test_cmd}" 2>&1); then
green "TESTS PASSED" >> "$LLM_OUTPUT"
echo "${output}" >> "$LLM_OUTPUT"
return 0
else
error "TESTS FAILED" >> "$LLM_OUTPUT"
echo "${output}" >> "$LLM_OUTPUT"
return 1
fi
}
+122
View File
@@ -0,0 +1,122 @@
# Step-Runner
A graph-based agent that executes **one step** of a phased implementation
plan, with the step protocol from the `step-implementation` skill enforced
as graph edges rather than prose. Designed to be delegated to by
**[Sisyphus](../sisyphus/README.md)**; delegates implementation to
**[Coder](../coder/README.md)** and independent review to
**[code-reviewer](../code-reviewer/README.md)**.
It expects a plan repo authored per the `plan-authoring` skill:
```
plans/
steps/NN-<slug>.md # step plans with frontmatter (step/title/depends_on/status)
handoffs/NN-<slug>.md # written by this agent, validated by a deterministic gate
NOTES.md # rolling durable facts
```
## Workflow
```mermaid
flowchart TD
resolve_step{"resolve_step<br/>script"}
resolve_step -->|"deps satisfied"| orient
resolve_step -->|"deps unsatisfied"| gate_blocked
gate_blocked{{"gate_blocked<br/>approval"}}
gate_blocked -->|"yes"| orient
gate_blocked -->|"no"| end_blocked
orient["orient<br/>llm, read-only"] --> route_staleness
route_staleness{"route_staleness<br/>script"}
route_staleness -->|"major deviation"| gate_deviation
route_staleness -->|"else"| implement
gate_deviation{{"gate_deviation<br/>approval"}}
gate_deviation -->|"proceed"| implement
gate_deviation -->|"abort"| end_rejected
gate_deviation -->|"other (user guidance)"| implement
implement[["implement<br/>agent → coder"]] --> route_coder_result
route_coder_result{"route_coder_result<br/>script"}
route_coder_result -->|"CODER_COMPLETE"| verify_format_lint
route_coder_result -->|"REJECTED / FAILED"| end_failure
verify_format_lint{"verify_format_lint<br/>script"}
verify_format_lint -->|"pass"| verify_build
verify_format_lint -->|"fail"| fix_loop_gate
verify_build{"verify_build<br/>script"}
verify_build -->|"pass"| verify_tests
verify_build -->|"fail"| fix_loop_gate
verify_tests{"verify_tests<br/>script"}
verify_tests -->|"pass"| edge_case_sweep
verify_tests -->|"fail"| fix_loop_gate
fix_loop_gate{"fix_loop_gate<br/>script"}
fix_loop_gate -->|"budget left"| implement
fix_loop_gate -->|"budget spent"| end_failure
edge_case_sweep["edge_case_sweep<br/>llm"] --> route_sweep
route_sweep{"route_sweep<br/>script"}
route_sweep -->|"5+ files or boundary"| independent_review
route_sweep -->|"else"| write_handoff
independent_review[["independent_review<br/>agent → code-reviewer"]] --> route_review
route_review{"route_review<br/>script"}
route_review -->|"🔴 critical findings"| implement
route_review -->|"else"| write_handoff
write_handoff["write_handoff<br/>llm"] --> check_handoff
check_handoff{"check_handoff<br/>script"}
check_handoff -->|"schema valid"| gate_user_review
check_handoff -->|"one retry"| write_handoff
gate_user_review{{"gate_user_review<br/>approval"}}
gate_user_review -->|"approve"| end_success
gate_user_review -->|"revise"| get_revision
gate_user_review -->|"other (comments)"| revise_from_choice
get_revision[/"get_revision<br/>input"/] --> implement
revise_from_choice{"revise_from_choice<br/>script"} --> implement
end_success(["end_success<br/>STEP_COMPLETE"])
end_blocked(["end_blocked<br/>STEP_BLOCKED"])
end_rejected(["end_rejected<br/>STEP_REJECTED"])
end_failure(["end_failure<br/>STEP_FAILED"])
```
End nodes emit sentinel outcomes for the caller:
- `STEP_COMPLETE` — step implemented, verified, handoff written, user approved.
- `STEP_BLOCKED``depends_on` unsatisfied and the user declined to proceed.
- `STEP_REJECTED` — user aborted at the deviation gate, or the coder's plan
was rejected at its approval gate.
- `STEP_FAILED` — coder failed, the step-level fix budget was exhausted, or
the handoff failed validation twice.
## Usage
```sh
# From the project root: run the next in-progress/pending step
coyote -a step-runner "Execute the next step"
# A specific step (also parsed from the prompt: "execute step 3")
coyote -a step-runner --agent-variable step 3 "Execute step 3"
# Plan repo somewhere else
coyote -a step-runner --agent-variable plans_dir docs/plans "Execute the next step"
```
**Invoke from the project root.** The coder sub-agent resolves its own
`project_dir` from the invocation directory; overriding `project_dir` here
does not propagate to the spawned coder.
## Tuning
`graph.yaml` `initial_state` exposes:
- `max_fix_attempts` (default `2`) — step-level fix budget (the coder has
its own internal budget of 3).
- `max_review_attempts` (default `1`) — bounded 🔴-finding fix loops after
independent review.
Environment overrides honored by the script nodes:
- `FORMAT_CMD` / `LINT_CMD` — formatting and linting (otherwise a per-type
heuristic formats, and linting defers to the build/check command).
- `BUILD_CMD` / `TEST_CMD` — skip project-type detection (same as coder).
- `STEP_AUTOAPPROVE=1` — bypass the deviation gate (non-interactive runs).
- `STEP_SKIP_REVIEW=1` — never spawn the independent reviewer.
The final user approval gate is never bypassed by an environment variable -
it is the point of the workflow.
+612
View File
@@ -0,0 +1,612 @@
name: step-runner
description: |
Executes ONE step of a phased implementation plan (plans/ repo) with the
step protocol enforced as graph edges: orient -> staleness check ->
implement (coder) -> verify -> edge-case sweep -> optional independent
review -> evidence-backed handoff -> user approval gate. Designed to be
delegated to by sisyphus.
version: '1.0'
global_tools:
- ast_grep.sh
- fs_cat.sh
- fs_ls.sh
- fs_write.sh
- fs_patch.sh
- execute_command.sh
skills_enabled: true
enabled_skills:
- step-implementation
- handoff-protocol
- code-review
- ai-slop-remover
variables:
- name: project_dir
description: |
Absolute path to the project directory. Defaults to "." (the directory
coyote was invoked from). The coder sub-agent resolves its own
project_dir the same way, so invoke step-runner FROM the project root
unless you override this for both.
default: '.'
- name: plans_dir
description: |
Path to the plan repo. Relative paths resolve against project_dir.
Expected layout: <plans_dir>/steps/NN-<slug>.md,
<plans_dir>/handoffs/, <plans_dir>/NOTES.md.
default: 'plans'
- name: step
description: |
Which step to execute: a step number, or "next" to pick the first
in-progress (resume) or pending step plan.
default: 'next'
settings:
max_loop_iterations: 20
log_state_snapshots: true
validate_before_run: true
timeout: 7200
initial_state:
project_dir: ''
plans_dir: ''
step_number: 0
step_slug: ''
step_title: ''
step_plan_path: ''
step_plan: ''
prev_handoff_path: '(none)'
prev_handoff: '(none - this is the first step)'
notes_path: ''
notes: '(none)'
handoff_path: ''
blocking_reason: ''
plan_summary: ''
implementation_brief: ''
staleness_report: ''
has_major_deviation: false
deviation_summary: ''
user_feedback: ''
fix_instructions: ''
fix_attempts: 0
max_fix_attempts: 2
coder_result: ''
format_output: ''
lint_ok: true
lint_output: ''
build_ok: true
build_output: ''
tests_ok: true
tests_output: ''
edge_case_report: ''
downstream_updates: ''
needs_independent_review: false
review_report: ''
review_attempts: 0
max_review_attempts: 1
handoff_attempts: 0
handoff_fix: ''
step_summary: ''
start: resolve_step
nodes:
resolve_step:
id: resolve_step
type: script
description: |
Locate the step plan, previous handoff, and NOTES.md; parse frontmatter;
check depends_on satisfaction against existing handoffs; mark the plan
in-progress. Routes to gate_blocked when dependencies are unsatisfied.
script: scripts/resolve_step.sh
timeout: 30
fallback: end_failure
next: orient
gate_blocked:
id: gate_blocked
type: approval
description: Escalate unsatisfied dependencies instead of building on missing ground.
question: |
Step {{step_number}} ({{step_title}}) is BLOCKED:
{{blocking_reason}}
Proceed anyway?
options:
- 'yes'
- 'no'
routes:
'yes': orient
'no': end_blocked
on_other: end_blocked
orient:
id: orient
type: llm
description: |
Read-only orientation and staleness check: merge the previous handoff's
directives with the step plan, then verify the plan's assumptions
against the CURRENT codebase before any edit.
skills_enabled: true
enabled_skills:
- step-implementation
instructions: |
You are orienting for one step of a phased implementation plan. Load
`step-implementation` and apply its Orient and Staleness-check phases.
You are READ-ONLY in this node: no edits, no fixes.
1. Read the previous handoff (below). Note directives aimed at this
step, deviations that changed the codebase, and bare assertions
that need re-verification.
2. Staleness-check the step plan against the code at {{project_dir}}:
grep the symbols it references (via execute_command), read its
Context snippets at their claimed locations with fs_cat, confirm
its Test commands exist.
3. Classify discrepancies per the skill's deviation table: minor
(mechanics differ; correct silently in the brief) vs major (scope,
approach, interfaces, or a later step's assumptions affected).
Produce `implementation_brief`: the corrected, self-contained marching
orders for the implementer - plan tasks in order, handoff directives
applied, minor staleness corrections folded in, acceptance criteria
restated. The implementer sees ONLY the step plan plus your brief.
prompt: |
## Step plan ({{step_plan_path}})
{{step_plan}}
## Previous handoff ({{prev_handoff_path}})
{{prev_handoff}}
## Rolling project notes
{{notes}}
tools:
- fs_cat
- fs_ls
- execute_command
max_iterations: 20
output_schema:
type: object
properties:
plan_summary:
type: string
description: 1-3 sentences summarizing what this step delivers
implementation_brief:
type: string
description: Corrected, self-contained instructions for the implementer
staleness_report:
type: string
description: Findings from checking plan assumptions against current code; "clean" if none
has_major_deviation:
type: boolean
description: True when a discrepancy changes scope, approach, or interfaces
deviation_summary:
type: string
description: Major deviations only, with the plan claim vs current reality. Empty when none
required:
[
plan_summary,
implementation_brief,
staleness_report,
has_major_deviation,
deviation_summary,
]
fallback: end_failure
next: route_staleness
route_staleness:
id: route_staleness
type: script
description: Major deviation -> user gate; otherwise straight to implement.
script: scripts/route_staleness.sh
timeout: 5
fallback: implement
gate_deviation:
id: gate_deviation
type: approval
description: Major deviations are never silently absorbed - the user decides.
question: |
Step {{step_number}} ({{step_title}}): the plan no longer matches the
codebase in a way that changes scope or approach.
{{deviation_summary}}
Staleness report:
{{staleness_report}}
Proceed with the corrected brief? (Answer with anything else to give
your own guidance to the implementer.)
options:
- 'proceed'
- 'abort'
routes:
'proceed': implement
'abort': end_rejected
on_other: implement
state_updates:
user_feedback: '{{choice}}'
implement:
id: implement
type: agent
description: |
Delegate implementation to the coder graph agent, which runs its own
plan -> implement -> build -> tests -> self-review fix-loop internally.
agent: coder
prompt: |
## TASK
Execute step {{step_number}} ({{step_title}}) of a phased implementation
plan for the project at {{project_dir}}.
## EXPECTED OUTCOME
Every task in the step plan below is implemented and its acceptance
criteria are met. Tests are derived from the Acceptance criteria
section (not from the implementation). Build and full test suite pass.
## MUST DO
- Follow the Orientation brief below - it supersedes the raw plan where
they disagree (it folds in corrections from the staleness check).
- Match the patterns pasted in the step plan's Context section.
- Derive tests from the plan's Acceptance criteria.
## MUST NOT DO
- Do not touch anything listed in the plan's Out of scope section.
- Do not modify files under {{plans_dir}}.
- Do not implement work belonging to other steps.
## CONTEXT
### Step plan
{{step_plan}}
### Orientation brief (handoff directives + staleness corrections applied)
{{implementation_brief}}
### User guidance (if any)
{{user_feedback}}
### Fix loop status (empty on first attempt)
{{fix_instructions}}
timeout: 3600
state_updates:
coder_result: '{{output}}'
next: route_coder_result
route_coder_result:
id: route_coder_result
type: script
description: Route on the coder sentinel - COMPLETE verifies, REJECTED/FAILED terminate.
script: scripts/route_coder_result.sh
timeout: 5
fallback: end_failure
verify_format_lint:
id: verify_format_lint
type: script
description: |
Format BEFORE evidence collection (FORMAT_CMD override or per-type
heuristic), then lint (LINT_CMD, when configured). Lint failure routes
to the fix loop.
script: scripts/verify_format_lint.sh
timeout: 300
fallback: fix_loop_gate
verify_build:
id: verify_build
type: script
description: Step-level build/typecheck evidence, collected AFTER formatting.
script: scripts/verify_build.sh
timeout: 600
fallback: fix_loop_gate
verify_tests:
id: verify_tests
type: script
description: FULL test suite - regressions in untouched code fail the step too.
script: scripts/verify_tests.sh
timeout: 1200
fallback: fix_loop_gate
fix_loop_gate:
id: fix_loop_gate
type: script
description: |
Step-level fix budget (the coder already ran its own internal fix
loop). Loops to implement with fix_instructions, or ends as failure.
script: scripts/fix_loop_gate.sh
timeout: 5
fallback: end_failure
edge_case_sweep:
id: edge_case_sweep
type: llm
description: |
Post-implementation sweep: missed spots, edge cases, downstream plan
implications. May annotate downstream plans' Edge cases sections
(annotate vs propose per handoff-protocol). Also judges whether the
change warrants an independent review pass.
skills_enabled: true
enabled_skills:
- step-implementation
- handoff-protocol
instructions: |
The implementation for this step just passed build and tests. Load
`step-implementation` (edge-case sweep phase) and `handoff-protocol`
(annotate-vs-propose rules), then:
1. Read the changed code (the coder result below names the files).
Look for edge cases the plan missed: empty inputs, error paths,
concurrency, partial failure, compat.
2. For each edge case belonging to a LATER step: check that step's
plan under {{plans_dir}}/steps/. If its Edge cases section already
covers it, done. If not, append an entry to that section via
fs_patch - touch NOTHING else in the file.
3. NEVER edit a later plan's Objective, Tasks, Acceptance criteria,
or Out of scope. Scope-affecting changes become proposed diffs in
`downstream_updates` instead.
4. Set needs_independent_review=true when the change touched 5+ files
or crosses architectural boundaries (auth, public APIs, schema,
security-sensitive paths).
Be terse. Findings, not prose.
prompt: |
## Coder result
{{coder_result}}
## Step plan
{{step_plan}}
## Staleness report from orientation
{{staleness_report}}
tools:
- fs_cat
- fs_ls
- fs_patch
- execute_command
max_iterations: 20
output_schema:
type: object
properties:
edge_case_report:
type: string
description: Edge cases discovered - both handled and punted, one per line. "none" if empty
downstream_updates:
type: string
description: Annotations made (plan file + section) and proposed diffs for scope-affecting changes. "none" if empty
needs_independent_review:
type: boolean
required: [edge_case_report, downstream_updates, needs_independent_review]
fallback: write_handoff
next: route_sweep
route_sweep:
id: route_sweep
type: script
description: Broad or boundary-crossing changes get an independent reviewer.
script: scripts/route_sweep.sh
timeout: 5
fallback: write_handoff
independent_review:
id: independent_review
type: agent
description: Independent review pass - the author's self-review cannot catch its own rationalizations.
agent: code-reviewer
prompt: |
Review the changes produced for step {{step_number}} ({{step_title}})
of a phased implementation plan in {{project_dir}}.
What the step was supposed to do:
{{plan_summary}}
Coder summary (names the modified/created files):
{{coder_result}}
Review the changed files against the step plan's acceptance criteria.
Preserve severity tags in your findings.
timeout: 1200
state_updates:
review_report: '{{output}}'
next: route_review
route_review:
id: route_review
type: script
description: Critical findings loop back to implement (bounded); otherwise proceed to handoff.
script: scripts/route_review.sh
timeout: 5
fallback: write_handoff
write_handoff:
id: write_handoff
type: llm
description: |
Write the evidence-backed handoff per handoff-protocol and append
durable facts to NOTES.md. The completion gate (check_handoff)
verifies the document afterward.
skills_enabled: true
enabled_skills:
- handoff-protocol
- ai-slop-remover
instructions: |
Load `handoff-protocol` and follow its writer schema EXACTLY: the
frontmatter (step, title, result) and all eight sections, writing
"None" rather than omitting a section.
Write the handoff to {{handoff_path}} with fs_write. Paste the
verification evidence below verbatim into the Evidence section -
commands, exit codes, decisive output lines. Deviations come from the
staleness report, gate decisions, and fix loop history. Downstream
plan updates come from the sweep results.
VERIFICATION HONESTY: evidence marked "GATE NOT RUN" means that gate
is UNVERIFIED — record it as not run; never paraphrase a skipped gate
as covered, passing, or handled elsewhere. A handoff that admits an
unverified gate is correct; one that dresses it up as verified poisons
every downstream reader.
Then append durable, step-independent facts (if any) to {{notes_path}}
- create the file if missing, never rewrite existing entries.
If "Gate feedback" below is non-empty, a previous handoff attempt
failed validation - fix exactly what it lists.
prompt: |
## Step
{{step_number}} ({{step_title}}) - plan at {{step_plan_path}}
## Plan summary
{{plan_summary}}
## Coder result
{{coder_result}}
## Staleness report / deviations
{{staleness_report}}
Major deviation summary (if any): {{deviation_summary}}
User guidance given (if any): {{user_feedback}}
Fix loop attempts used: {{fix_attempts}} of {{max_fix_attempts}}
## Edge cases discovered
{{edge_case_report}}
## Downstream plan updates
{{downstream_updates}}
## Independent review report (if any)
{{review_report}}
## Verification evidence (paste verbatim)
### Format
{{format_output}}
### Lint
{{lint_output}}
### Build
{{build_output}}
### Tests
{{tests_output}}
## Gate feedback
{{handoff_fix}}
tools:
- fs_cat
- fs_ls
- fs_write
- fs_patch
max_iterations: 15
output_schema:
type: object
properties:
step_summary:
type: string
description: 3-6 sentence summary of the step for the user's approval decision - what was done, deviations, anything needing their attention
required: [step_summary]
fallback: end_failure
next: check_handoff
check_handoff:
id: check_handoff
type: script
description: |
Deterministic completion gate - handoff exists with frontmatter and all
required sections. On success, marks the step plan status complete.
One retry back to write_handoff, then failure.
script: scripts/check_handoff.sh
timeout: 10
fallback: end_failure
gate_user_review:
id: gate_user_review
type: approval
description: The hard stop - the next step never starts without explicit approval.
question: |
## Step {{step_number}} ({{step_title}}) - ready for review
{{step_summary}}
Handoff: {{handoff_path}}
Build: {{build_ok}} | Tests: {{tests_ok}} | Fix attempts: {{fix_attempts}}/{{max_fix_attempts}}
Approve this step? (Answer with anything else to send revision
instructions straight to the implementer.)
options:
- 'approve'
- 'revise'
routes:
'approve': end_success
'revise': get_revision
on_other: revise_from_choice
state_updates:
user_feedback: '{{choice}}'
get_revision:
id: get_revision
type: input
description: Collect revision instructions, then loop back through implement -> verify -> handoff.
question: 'What should change? Your comments go to the implementer verbatim.'
validation: 'len(input) > 0'
state_updates:
fix_instructions: '{{input}}'
next: implement
revise_from_choice:
id: revise_from_choice
type: script
description: Free-form approval answers are treated as revision instructions.
script: scripts/revise_from_choice.sh
timeout: 5
fallback: get_revision
end_success:
id: end_success
type: end
output: |
STEP_COMPLETE
Step: {{step_number}} ({{step_title}})
Plan: {{step_plan_path}}
Handoff: {{handoff_path}}
Build: passed | Tests: passed | Fix attempts: {{fix_attempts}}/{{max_fix_attempts}}
{{step_summary}}
Downstream plan updates:
{{downstream_updates}}
end_blocked:
id: end_blocked
type: end
output: |
STEP_BLOCKED
Step: {{step_number}} ({{step_title}})
Reason:
{{blocking_reason}}
end_rejected:
id: end_rejected
type: end
output: |
STEP_REJECTED
Step: {{step_number}} ({{step_title}})
Rejected at: deviation gate or coder approval gate.
Deviation summary:
{{deviation_summary}}
Coder result (if it ran):
{{coder_result}}
end_failure:
id: end_failure
type: end
output: |
STEP_FAILED
Step: {{step_number}} ({{step_title}})
Fix attempts: {{fix_attempts}}/{{max_fix_attempts}}
Blocking reason (if resolution failed): {{blocking_reason}}
Coder result:
{{coder_result}}
Last build output:
{{build_output}}
Last tests output:
{{tests_output}}
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/env bash
set -uo pipefail
if [[ -n "${GRAPH_STATE_FILE:-}" ]]; then
state=$(cat "$GRAPH_STATE_FILE")
elif [[ -n "${GRAPH_STATE:-}" ]]; then
state="$GRAPH_STATE"
else
state='{}'
fi
handoff_path=$(echo "$state" | jq -r '.handoff_path // ""')
step_plan_path=$(echo "$state" | jq -r '.step_plan_path // ""')
handoff_attempts=$(echo "$state" | jq -r '.handoff_attempts // 0')
problems=""
if [[ ! -f "$handoff_path" ]]; then
problems="- handoff file does not exist at $handoff_path"$'\n'
else
content=$(cat "$handoff_path")
grep -qE '^result:[[:space:]]*(complete|partial|blocked)' <<< "$content" \
|| problems+="- frontmatter is missing 'result: complete|partial|blocked'"$'\n'
for section in "Summary" "Completed" "Not completed" "Deviations" "Downstream plan updates" "Edge cases discovered" "Evidence" "Notes for next step"; do
grep -qE "^##[[:space:]]+${section}" <<< "$content" \
|| problems+="- missing required section: ## ${section}"$'\n'
done
fi
if [[ -z "$problems" ]]; then
if [[ -f "$step_plan_path" ]]; then
tmp=$(mktemp)
awk 'BEGIN{n=0} /^---[[:space:]]*$/{n++; print; next} n==1 && /^status:/{print "status: complete"; next} {print}' "$step_plan_path" > "$tmp" && mv "$tmp" "$step_plan_path"
fi
jq -nc '{"handoff_fix": "", "_next": "gate_user_review"}'
exit 0
fi
if (( handoff_attempts >= 1 )); then
jq -nc \
--arg br "Handoff failed validation twice. Problems:
$problems" \
'{"blocking_reason": $br, "_next": "end_failure"}'
exit 0
fi
jq -nc \
--arg hf "The previous handoff attempt failed validation. Fix exactly these problems:
$problems" \
'{
"handoff_attempts": 1,
"handoff_fix": $hf,
"_next": "write_handoff"
}'
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ -n "${GRAPH_STATE_FILE:-}" ]]; then
state=$(cat "$GRAPH_STATE_FILE")
elif [[ -n "${GRAPH_STATE:-}" ]]; then
state="$GRAPH_STATE"
else
state='{}'
fi
fix_attempts=$(echo "$state" | jq -r '.fix_attempts // 0')
max_fix_attempts=$(echo "$state" | jq -r '.max_fix_attempts // 2')
lint_ok=$(echo "$state" | jq -r '.lint_ok | if . == null then "true" else (. | tostring) end')
build_ok=$(echo "$state" | jq -r '.build_ok | if . == null then "true" else (. | tostring) end')
tests_ok=$(echo "$state" | jq -r '.tests_ok | if . == null then "true" else (. | tostring) end')
lint_output=$(echo "$state" | jq -r '.lint_output // ""')
build_output=$(echo "$state" | jq -r '.build_output // ""')
tests_output=$(echo "$state" | jq -r '.tests_output // ""')
if (( fix_attempts >= max_fix_attempts )); then
jq -nc \
--argjson n "$fix_attempts" \
'{
"fix_attempts": $n,
"_next": "end_failure"
}'
exit 0
fi
next_attempts=$((fix_attempts + 1))
if [[ "$lint_ok" != "true" ]]; then
stage="lint"
output="$lint_output"
elif [[ "$build_ok" != "true" ]]; then
stage="build"
output="$build_output"
elif [[ "$tests_ok" != "true" ]]; then
stage="full test suite"
output="$tests_output"
else
stage="verification"
output="fix_loop_gate was reached but no failing stage was recorded. Re-run verification."
fi
fix_instructions=$(printf '## Fix loop status (step-level attempt %d of %d)\n\nThe implementation passed the coder'"'"'s internal checks but failed step-level verification at the %s stage.\n\nOutput:\n```\n%s\n```\n\nIdentify the minimal fix and apply it. Do not refactor. Regressions in untouched code caused by this change are in scope.' \
"$next_attempts" "$max_fix_attempts" "$stage" "$output")
jq -nc \
--argjson n "$next_attempts" \
--arg 'fi' "$fix_instructions" \
'{
"fix_attempts": $n,
"fix_instructions": $fi,
"lint_ok": true,
"build_ok": true,
"tests_ok": true,
"_next": "implement"
}'
+152
View File
@@ -0,0 +1,152 @@
#!/usr/bin/env bash
set -uo pipefail
if [[ -n "${GRAPH_STATE_FILE:-}" ]]; then
state=$(cat "$GRAPH_STATE_FILE")
elif [[ -n "${GRAPH_STATE:-}" ]]; then
state="$GRAPH_STATE"
else
state='{}'
fi
fail() {
jq -nc --arg r "$1" '{"blocking_reason": $r, "_next": "end_failure"}'
exit 0
}
project_dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}"
project_dir=$(cd "$project_dir" 2>/dev/null && pwd) || fail "project_dir does not exist: $project_dir"
plans_dir="${LLM_AGENT_VAR_PLANS_DIR:-plans}"
[[ "$plans_dir" != /* ]] && plans_dir="$project_dir/$plans_dir"
steps_dir="$plans_dir/steps"
handoffs_dir="$plans_dir/handoffs"
notes_path="$plans_dir/NOTES.md"
[[ -d "$steps_dir" ]] || fail "No step plans directory at $steps_dir (expected <plans_dir>/steps/NN-<slug>.md)"
frontmatter() {
awk '/^---[[:space:]]*$/{n++; next} n==1{print} n>=2{exit}' "$1"
}
fm_value() {
echo "$1" | grep -E "^$2:" | head -1 | sed -E "s/^$2:[[:space:]]*//" | sed -E 's/^["'"'"']|["'"'"']$//g'
}
step="${LLM_AGENT_VAR_STEP:-next}"
if [[ "$step" == "next" ]]; then
prompt_step=$(echo "$state" | jq -r '.initial_prompt // ""' | grep -oiE 'step[[:space:]#:]*[0-9]+' | head -1 | grep -oE '[0-9]+' || true)
[[ -n "$prompt_step" ]] && step="$prompt_step"
fi
plan_file=""
if [[ "$step" == "next" ]]; then
first_pending=""
while IFS= read -r f; do
st=$(fm_value "$(frontmatter "$f")" "status")
if [[ "$st" == "in-progress" ]]; then
plan_file="$f"
break
fi
[[ -z "$first_pending" && ( "$st" == "pending" || -z "$st" ) ]] && first_pending="$f"
done < <(find "$steps_dir" -maxdepth 1 -name '*.md' | sort)
[[ -z "$plan_file" ]] && plan_file="$first_pending"
[[ -z "$plan_file" ]] && fail "No in-progress or pending step plans in $steps_dir"
else
[[ "$step" =~ ^[0-9]+$ ]] || fail "step must be a number or 'next'; got: $step"
padded=$(printf '%02d' "$((10#$step))")
plan_file=$(find "$steps_dir" -maxdepth 1 \( -name "${padded}-*.md" -o -name "${step}-*.md" \) | sort | head -1)
[[ -n "$plan_file" ]] || fail "No step plan matching step $step in $steps_dir"
fi
bn=$(basename "$plan_file" .md)
num_part="${bn%%-*}"
[[ "$num_part" =~ ^[0-9]+$ ]] || fail "Step plan filename must start with a number: $bn"
step_number=$((10#$num_part))
step_slug="${bn#*-}"
fm=$(frontmatter "$plan_file")
step_title=$(fm_value "$fm" "title")
[[ -z "$step_title" ]] && step_title="$step_slug"
deps=$(echo "$fm" | awk '/^depends_on:/{f=1; print; next} f && /^[[:space:]]*-/{print; next} f{exit}' | grep -oE '[0-9]+' || true)
unsatisfied=""
for dep in $deps; do
dep_padded=$(printf '%02d' "$((10#$dep))")
dep_handoff=$(find "$handoffs_dir" -maxdepth 1 \( -name "${dep_padded}-*.md" -o -name "${dep}-*.md" \) 2>/dev/null | sort | head -1)
if [[ -z "$dep_handoff" ]]; then
unsatisfied+="- step $dep: no handoff found (step not executed?)"$'\n'
continue
fi
dep_result=$(fm_value "$(frontmatter "$dep_handoff")" "result")
if [[ "$dep_result" != "complete" ]]; then
unsatisfied+="- step $dep: handoff result is '$dep_result' (not complete): $dep_handoff"$'\n'
fi
done
prev_handoff_path="(none)"
prev_handoff="(none - this is the first step)"
prev_file=""
prev_num=0
while IFS= read -r h; do
hn="${h##*/}"
hn="${hn%%-*}"
[[ "$hn" =~ ^[0-9]+$ ]] || continue
n=$((10#$hn))
if (( n < step_number && n >= prev_num )); then
prev_num=$n
prev_file="$h"
fi
done < <(find "$handoffs_dir" -maxdepth 1 -name '*.md' 2>/dev/null | sort)
if [[ -n "$prev_file" ]]; then
prev_handoff_path="$prev_file"
prev_handoff=$(head -c 16000 "$prev_file")
fi
notes="(none)"
[[ -f "$notes_path" ]] && notes=$(head -c 8000 "$notes_path")
step_plan=$(head -c 24000 "$plan_file")
handoff_path="$handoffs_dir/$(basename "$plan_file")"
tmp=$(mktemp)
awk 'BEGIN{n=0} /^---[[:space:]]*$/{n++; print; next} n==1 && /^status:/{print "status: in-progress"; next} {print}' "$plan_file" > "$tmp" && mv "$tmp" "$plan_file"
next_node="orient"
blocking_reason=""
if [[ -n "$unsatisfied" ]]; then
next_node="gate_blocked"
blocking_reason="Unsatisfied dependencies:"$'\n'"$unsatisfied"
fi
jq -nc \
--arg pd "$project_dir" \
--arg pl "$plans_dir" \
--argjson sn "$step_number" \
--arg ss "$step_slug" \
--arg st "$step_title" \
--arg spp "$plan_file" \
--arg sp "$step_plan" \
--arg php "$prev_handoff_path" \
--arg ph "$prev_handoff" \
--arg np "$notes_path" \
--arg no "$notes" \
--arg hp "$handoff_path" \
--arg br "$blocking_reason" \
--arg nx "$next_node" \
'{
"project_dir": $pd,
"plans_dir": $pl,
"step_number": $sn,
"step_slug": $ss,
"step_title": $st,
"step_plan_path": $spp,
"step_plan": $sp,
"prev_handoff_path": $php,
"prev_handoff": $ph,
"notes_path": $np,
"notes": $no,
"handoff_path": $hp,
"blocking_reason": $br,
"_next": $nx
}'
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ -n "${GRAPH_STATE_FILE:-}" ]]; then
state=$(cat "$GRAPH_STATE_FILE")
elif [[ -n "${GRAPH_STATE:-}" ]]; then
state="$GRAPH_STATE"
else
state='{}'
fi
feedback=$(echo "$state" | jq -r '.user_feedback // ""')
if [[ -z "$feedback" ]]; then
jq -nc '{"_next": "get_revision"}'
exit 0
fi
fix_instructions=$(printf '## Revision requested by the user at the step approval gate\n\nAddress these comments with minimal edits, then the step re-verifies and the handoff is rewritten:\n\n%s' \
"$feedback")
jq -nc \
--arg 'fi' "$fix_instructions" \
'{
"fix_instructions": $fi,
"_next": "implement"
}'
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ -n "${GRAPH_STATE_FILE:-}" ]]; then
state=$(cat "$GRAPH_STATE_FILE")
elif [[ -n "${GRAPH_STATE:-}" ]]; then
state="$GRAPH_STATE"
else
state='{}'
fi
coder_result=$(echo "$state" | jq -r '.coder_result // ""')
case "$coder_result" in
*CODER_COMPLETE*)
jq -nc '{"_next": "verify_format_lint"}'
;;
*CODER_REJECTED*)
jq -nc '{"_next": "end_rejected"}'
;;
*CODER_FAILED*)
jq -nc '{"blocking_reason": "coder fix-loop exhausted; see coder result", "_next": "end_failure"}'
;;
*)
jq -nc '{"blocking_reason": "coder returned no recognizable sentinel (expected CODER_COMPLETE / CODER_REJECTED / CODER_FAILED)", "_next": "end_failure"}'
;;
esac
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ -n "${GRAPH_STATE_FILE:-}" ]]; then
state=$(cat "$GRAPH_STATE_FILE")
elif [[ -n "${GRAPH_STATE:-}" ]]; then
state="$GRAPH_STATE"
else
state='{}'
fi
review_report=$(echo "$state" | jq -r '.review_report // ""')
review_attempts=$(echo "$state" | jq -r '.review_attempts // 0')
max_review_attempts=$(echo "$state" | jq -r '.max_review_attempts // 1')
if ! grep -qF "🔴" <<< "$review_report"; then
jq -nc '{"_next": "write_handoff"}'
exit 0
fi
if (( review_attempts >= max_review_attempts )); then
jq -nc '{"_next": "write_handoff"}'
exit 0
fi
next_review=$((review_attempts + 1))
fix_instructions=$(printf '## Independent review findings (attempt %d of %d)\n\nAn independent reviewer flagged CRITICAL (🔴) findings. Address ONLY the 🔴 findings with minimal edits. Do not refactor unrelated code.\n\n%s' \
"$next_review" "$max_review_attempts" "$review_report")
jq -nc \
--argjson n "$next_review" \
--arg 'fi' "$fix_instructions" \
'{
"review_attempts": $n,
"fix_instructions": $fi,
"needs_independent_review": false,
"_next": "implement"
}'
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ -n "${GRAPH_STATE_FILE:-}" ]]; then
state=$(cat "$GRAPH_STATE_FILE")
elif [[ -n "${GRAPH_STATE:-}" ]]; then
state="$GRAPH_STATE"
else
state='{}'
fi
has_major=$(echo "$state" | jq -r '.has_major_deviation // false')
if [[ "${STEP_AUTOAPPROVE:-0}" == "1" ]]; then
jq -nc '{"_next": "implement"}'
exit 0
fi
if [[ "$has_major" == "true" ]]; then
jq -nc '{"_next": "gate_deviation"}'
else
jq -nc '{"_next": "implement"}'
fi
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ -n "${GRAPH_STATE_FILE:-}" ]]; then
state=$(cat "$GRAPH_STATE_FILE")
elif [[ -n "${GRAPH_STATE:-}" ]]; then
state="$GRAPH_STATE"
else
state='{}'
fi
needs_review=$(echo "$state" | jq -r '.needs_independent_review // false')
if [[ "${STEP_SKIP_REVIEW:-0}" == "1" ]]; then
jq -nc '{"_next": "write_handoff"}'
exit 0
fi
if [[ "$needs_review" == "true" ]]; then
jq -nc '{"_next": "independent_review"}'
else
jq -nc '{"_next": "write_handoff"}'
fi
+58
View File
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
set -uo pipefail
# shellcheck disable=SC1091
source "$(dirname "$0")/../../.shared/utils.sh"
if [[ -n "${GRAPH_STATE_FILE:-}" ]]; then
state=$(cat "$GRAPH_STATE_FILE")
elif [[ -n "${GRAPH_STATE:-}" ]]; then
state="$GRAPH_STATE"
else
state='{}'
fi
project_dir=$(echo "$state" | jq -r '.project_dir // "."')
project_dir=$(resolve_gate_dir "$project_dir")
if [[ -n "${BUILD_CMD:-}" ]]; then
cmd="$BUILD_CMD"
else
project_info=$(detect_project "$project_dir")
cmd=$(echo "$project_info" | jq -r '.check // .build // ""')
fi
if [[ -z "$cmd" || "$cmd" == "null" ]]; then
jq -nc '{
"build_ok": true,
"build_output": "(GATE NOT RUN: no build/check command configured or detected. This is NOT evidence that the build passed — set BUILD_CMD, and never report the build as verified.)",
"_next": "verify_tests"
}'
exit 0
fi
exit_code=0
output=$(cd "$project_dir" && eval "$cmd" 2>&1) || exit_code=$?
if (( exit_code == 0 )); then
jq -nc \
--arg out "Ran: $cmd
$output" \
'{
"build_ok": true,
"build_output": $out,
"_next": "verify_tests"
}'
else
jq -nc \
--arg out "Ran: $cmd
Exit code: $exit_code
$output" \
'{
"build_ok": false,
"build_output": $out,
"_next": "fix_loop_gate"
}'
fi
+84
View File
@@ -0,0 +1,84 @@
#!/usr/bin/env bash
set -uo pipefail
# shellcheck disable=SC1091
source "$(dirname "$0")/../../.shared/utils.sh"
if [[ -n "${GRAPH_STATE_FILE:-}" ]]; then
state=$(cat "$GRAPH_STATE_FILE")
elif [[ -n "${GRAPH_STATE:-}" ]]; then
state="$GRAPH_STATE"
else
state='{}'
fi
project_dir=$(echo "$state" | jq -r '.project_dir // "."')
project_dir=$(resolve_gate_dir "$project_dir")
project_info=$(detect_project "$project_dir")
project_type=$(echo "$project_info" | jq -r '.type // "unknown"')
format_cmd="${FORMAT_CMD:-}"
if [[ -z "$format_cmd" ]]; then
format_cmd=$(echo "$project_info" | jq -r '.fmt // ""')
fi
if [[ "$format_cmd" == "null" ]]; then format_cmd=""; fi
if [[ -z "$format_cmd" ]]; then
format_output="(GATE NOT RUN: no format command configured or detected for project type '$project_type'. This is NOT evidence that formatting is clean. Set FORMAT_CMD to enable.)"
else
fmt_rc=0
fmt_out=$(cd "$project_dir" && eval "$format_cmd" 2>&1) || fmt_rc=$?
format_output="Ran: $format_cmd
Exit code: $fmt_rc
$fmt_out"
fi
lint_cmd="${LINT_CMD:-}"
if [[ -z "$lint_cmd" ]]; then
lint_cmd=$(echo "$project_info" | jq -r '.lint // ""')
fi
# The skip message must read as a WARNING, never a reassurance: the previous
# wording ("linting is covered by the build/check command") was quoted
# verbatim by workers as false evidence that linting passed
if [[ -z "$lint_cmd" || "$lint_cmd" == "null" ]]; then
jq -nc \
--arg fo "$format_output" \
'{
"format_output": $fo,
"lint_ok": true,
"lint_output": "(GATE NOT RUN: no lint command configured or detected. This is NOT evidence that linting passed — set LINT_CMD or add a Taskfile lint target, and never report linting as covered.)",
"_next": "verify_build"
}'
exit 0
fi
lint_rc=0
lint_out=$(cd "$project_dir" && eval "$lint_cmd" 2>&1) || lint_rc=$?
if (( lint_rc == 0 )); then
jq -nc \
--arg fo "$format_output" \
--arg lo "Ran: $lint_cmd
$lint_out" \
'{
"format_output": $fo,
"lint_ok": true,
"lint_output": $lo,
"_next": "verify_build"
}'
else
jq -nc \
--arg fo "$format_output" \
--arg lo "Ran: $lint_cmd
Exit code: $lint_rc
$lint_out" \
'{
"format_output": $fo,
"lint_ok": false,
"lint_output": $lo,
"_next": "fix_loop_gate"
}'
fi
+58
View File
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
set -uo pipefail
# shellcheck disable=SC1091
source "$(dirname "$0")/../../.shared/utils.sh"
if [[ -n "${GRAPH_STATE_FILE:-}" ]]; then
state=$(cat "$GRAPH_STATE_FILE")
elif [[ -n "${GRAPH_STATE:-}" ]]; then
state="$GRAPH_STATE"
else
state='{}'
fi
project_dir=$(echo "$state" | jq -r '.project_dir // "."')
project_dir=$(resolve_gate_dir "$project_dir")
if [[ -n "${TEST_CMD:-}" ]]; then
cmd="$TEST_CMD"
else
project_info=$(detect_project "$project_dir")
cmd=$(echo "$project_info" | jq -r '.test // ""')
fi
if [[ -z "$cmd" || "$cmd" == "null" ]]; then
jq -nc '{
"tests_ok": true,
"tests_output": "(GATE NOT RUN: no test command configured or detected. This is NOT evidence that tests passed — set TEST_CMD, and never report the suite as green.)",
"_next": "edge_case_sweep"
}'
exit 0
fi
exit_code=0
output=$(cd "$project_dir" && eval "$cmd" 2>&1) || exit_code=$?
if (( exit_code == 0 )); then
jq -nc \
--arg out "Ran: $cmd
$output" \
'{
"tests_ok": true,
"tests_output": $out,
"_next": "edge_case_sweep"
}'
else
jq -nc \
--arg out "Ran: $cmd
Exit code: $exit_code
$output" \
'{
"tests_ok": false,
"tests_output": $out,
"_next": "fix_loop_gate"
}'
fi
-1106
View File
File diff suppressed because it is too large Load Diff
+279
View File
@@ -0,0 +1,279 @@
# Coyote configuration. Generated by the first-run wizard.
# Every setting is listed with its effective value and a short description.
# For richer examples of each section, see
# https://github.com/Dark-Alex-17/coyote/blob/main/config.example.yaml
# ---- LLM ----
__MODEL_BLOCK__
temperature: null # Set default temperature parameter (0, 1)
top_p: null # Set default top-p parameter, with a range of (0, 1) or (0, 2) depending on the model
# ---- Behavior ----
dry_run: false # Display the messages that would be sent to the LLM without actually sending them
stream: true # Controls whether to use the stream-style APIs when querying for completions from LLM clients
save: true # Indicates whether to persist the conversation to messages.md for posterity
keybindings: emacs # Choose keybinding style (emacs, vi)
editor: null # Specifies the editor used to edit the input buffer or session. (e.g. vim, emacs, nano, hx). Defaults to $EDITOR
wrap: auto # Controls text wrapping (no, auto, <max-width>)
wrap_code: false # Enables or disables the wrapping of code blocks
# ---- Vault ----
# See the [Vault documentation](https://github.com/Dark-Alex-17/coyote/wiki/Vault) for more information on the Coyote vault.
#
# The secrets_provider tells Coyote where to read and write secrets referenced via {{SECRET_NAME}} syntax.
#
# Shorthand: set vault_password_file to enable the local provider with that password
# file (it cannot be a secret template).
#
# Explicit: set secrets_provider to one of the supported types below. When secrets_provider is set,
# vault_password_file is ignored. Note: secrets_provider itself cannot use secret template syntax.
# The vault must be initialized before any secrets can be resolved.
#
# Local (same as the shorthand above):
# secrets_provider:
# type: local
# password_file: ~/.coyote_password
#
# AWS Secrets Manager (requires an authenticated AWS CLI; see `aws sso login` or `aws configure`):
# secrets_provider:
# type: aws_secrets_manager
# aws_profile: default
# aws_region: us-east-1
#
# GCP Secret Manager (requires `gcloud auth application-default login`):
# secrets_provider:
# type: gcp_secret_manager
# gcp_project_id: my-project-id
#
# Azure Key Vault (requires `az login`):
# secrets_provider:
# type: azure_key_vault
# vault_name: my-vault-name
#
# gopass (requires the `gopass` CLI to be installed and initialized):
# secrets_provider:
# type: gopass
# store: my-store # Optional; omit to use the default store
#
# 1Password (requires the `op` CLI to be installed and signed in via `op signin`):
# secrets_provider:
# type: one_password
# vault: Production # Optional; omit to use the default vault
# account: my.1password.com # Optional; omit to use the default account
__SECRETS_BLOCK__
# ---- Function Calling ----
# See the [Tools documentation](https://github.com/Dark-Alex-17/coyote/wiki/Tools) for more details
function_calling_support: true # Enables or disables function calling (globally)
mapping_tools: {} # Alias for a tool or toolset
# Example:
# mapping_tools:
# fs: 'fs_cat,fs_ls,fs_mkdir,fs_rm,fs_write,fs_read,fs_glob,fs_grep'
enabled_tools: null # Which tools to enable by default.
# Accepts either a YAML list or a comma-separated string. Use 'all' to enable everything.
# Example (list form):
# enabled_tools:
# - fs
# - web_search_coyote
# Example (comma-separated form):
# enabled_tools: fs,web_search_coyote
visible_tools: null # Which tools are visible to be compiled (and are thus able to be defined in 'enabled_tools').
# Null/missing = all tools in the global tools dir are visible; [] = none;
# an explicit list makes only those tools visible.
# Example:
# visible_tools:
# - execute_command.sh
# - fs_cat.sh
# - fs_ls.sh
# ---- Skills ----
# Skills are modular knowledge or capability packs the LLM can load and unload mid-conversation.
# See the [Skills documentation](https://github.com/Dark-Alex-17/coyote/wiki/Skills) for more details.
skills_enabled: true # Master switch. Set to false to hide all skill management tools from the model.
# Skills also require `function_calling_support: true` above to work at all.
enabled_skills: null # Which skills are available by default (no role/agent/session active). null = all visible.
# Accepts either a YAML list or a comma-separated string.
# Example (list form):
# enabled_skills:
# - git-master
# - ai-slop-remover
# Example (comma-separated form):
# enabled_skills: git-master,ai-slop-remover
visible_skills: null # The universe of skills allowed to be enabled in any context. null = all installed.
# Example:
# visible_skills:
# - ai-slop-remover
# - code-review
# - git-master
# ---- Macros ----
# Macros are Coyote's custom commands: named sequences of REPL commands and prompts, invoked directly by name
# (a macro file named `review.yaml` runs as `.review [args]`; built-in commands always win a name collision).
# Workspace-local macros in `.coyote/macros/` shadow same-named global macros (skip them with --no-workspace-macros).
# See the [Macros documentation](https://github.com/Dark-Alex-17/coyote/wiki/Macros) for more details.
enabled_macros: null # Which macros are invocable by default (no role/agent/session active). null = all visible.
# An empty list means NO macros are invocable. Accepts either a YAML list or a
# comma-separated string. Roles, agents, and sessions may define their own
# `enabled_macros`; the most specific active one wins (session > agent > role > global).
# Example (list form):
# enabled_macros:
# - generate-commit-message
# Example (comma-separated form):
# enabled_macros: generate-commit-message,review
# ---- MCP Servers ----
# See the [MCP Servers documentation](https://github.com/Dark-Alex-17/coyote/wiki/MCP-Servers) for more details
mcp_server_support: true # Enables or disables MCP servers (globally)
mapping_mcp_servers: {} # Alias for an MCP server or set of servers
# Example:
# mapping_mcp_servers:
# git: github,gitmcp
enabled_mcp_servers: null # Which MCP servers to enable by default.
# Accepts either a YAML list or a comma-separated string. Use 'all' to enable everything.
# Example (list form):
# enabled_mcp_servers:
# - github
# - slack
# Example (comma-separated form):
# enabled_mcp_servers: github,slack,ddg-search
mcp_tools: null # Per-server MCP tool allowlists (glob patterns: * and ? supported).
# Tools that match no pattern are hidden from the model as if they
# don't exist. Stacks with the other allowlist layers (mcp.json
# `allowedTools`, role, agent, session, skill, graph node). Every
# configured layer must allow a tool, so layers only ever narrow.
# An empty list blocks all of a server's tools.
# Example:
# mcp_tools:
# github:
# - get_*
# - list_*
# slack: []
# ---- Auto-Continue (Todo System) ----
# The auto-continue system provides built-in task tracking for improved reliability.
# When enabled, the model can create todo lists and the system will automatically
# prompt it to continue when incomplete tasks remain.
# See the [Todo System documentation](https://github.com/Dark-Alex-17/coyote/wiki/TODO-System) for more information
auto_continue: false # Enable automatic continuation when incomplete todos remain (default: false)
max_auto_continues: 10 # Maximum number of automatic continuations before stopping (default: 10)
inject_todo_instructions: true # Inject default todo usage instructions into the system prompt (default: true)
continuation_prompt: null # Custom prompt used when auto-continuing. If null, uses built-in default
inject_skill_instructions: true # Inject a short hint pointing the model at `skill__list` when skills are enabled
# in this context. Only injected if `function_calling_support`, `skills_enabled`, and the
# effective enabled skill set is non-empty (default: true)
skill_instructions: null # Custom text used for the skill hint when injected. If null, uses built-in default
# ---- Prelude ----
repl_prelude: null # Set a default session or role for REPL mode to use (e.g. role:<name>, session:<name>, <session>:<role>)
cmd_prelude: null # Set a default session or role for CMD mode to use (e.g. role:<name>, session:<name>, <session>:<role>)
agent_session: null # Set a session to use when starting an agent (e.g. temp, default)
# ---- Session ----
# See the [Session documentation](https://github.com/Dark-Alex-17/coyote/wiki/Sessions) for more information
save_session: null # Controls the persistence of the session. If true, auto save; if false, don't auto-save; if null, ask the user what to do
compression_threshold: 4000 # Compress the session when the token count reaches or exceeds this threshold
compression_keep_last: 0 # Number of most-recent messages to keep visible after compression (0 = compress all messages)
summarization_prompt: null # The text prompt used for creating a concise summary of session messages. If null, uses built-in default
summary_context_prompt: null # The text prompt used for including the summary of the entire session as context to the model. If null, uses built-in default
max_tool_result_chars: null # Cap on tool result characters forwarded to the model per call (null = no cap)
max_concurrent_jobs: null # Max background jobs (`job__*` tools) running at once per context (null = 5; 0 disables background jobs entirely)
# ---- Memory ----
# See the [Memory documentation](https://github.com/Dark-Alex-17/coyote/wiki/Memory) for more information.
# Memory is opt-in by workspace presence (`.coyote/memory/MEMORY.md`) and global
# presence (`<config_dir>/memory/MEMORY.md`). Set `memory: false` to disable
# even when memory files exist. The cascade is: agent > session > role > app.
# Bootstrap with `coyote --init-memory [global|workspace]` to create the marker file
# the LLM needs before it will write any memory.
memory: null # null = enabled when memory exists on disk; true = force on; false = force off
memory_cap_with_tools: null # Char cap for injected memory when function calling is available (null = 6000).
# Only MEMORY.md indexes are injected; the LLM uses memory__read to fetch drill files.
memory_cap_without_tools: null # Char cap when function calling is unavailable (null = 12000).
# Indexes plus drill file bodies are injected up to this cap.
# ---- Workspace Instructions ----
# Human-curated project instructions injected read-only into the system prompt, in full.
# Coyote walks up from the current directory and injects the first match from the file
# chain below (per directory, in order). Scaffold with `coyote --init-instructions`.
# Disable per-invocation with --no-workspace-instructions, or override the chain with
# repeatable --workspace-instructions-file flags.
workspace_instructions: null # null/true = inject when an instructions file exists; false = never inject
workspace_instructions_files: null # File name chain to search, in priority order.
# Default: [COYOTE.md, AGENTS.md, CLAUDE.md, GEMINI.md]
# Set to a custom list to reorder or drop fallbacks, e.g.:
# workspace_instructions_files: [COYOTE.md]
# ---- RAG ----
# See the [RAG Docs](https://github.com/Dark-Alex-17/coyote/wiki/RAG) for more details.
rag_embedding_model: null # Specifies the embedding model used for context retrieval
rag_reranker_model: null # Specifies the reranker model used for sorting retrieved documents; Coyote uses Reciprocal Rank Fusion by default
rag_top_k: 5 # Specifies the number of documents to retrieve for answering queries
rag_chunk_size: null # Defines the size of chunks for document processing in characters
rag_chunk_overlap: null # Defines the overlap between chunks
rag_template: null # Defines the query structure using variables like __CONTEXT__, __SOURCES__, and __INPUT__
# to tailor searches to specific needs. If null, uses built-in default
rag_extractor_model: null # LLM model for graph-based entity/relationship extraction; when set, enables a graph RAG signal alongside vector and BM25
rag_extractor_prompt: null # Custom extraction prompt template; must contain __CHUNK__ placeholder; defaults to built-in prompt when null
rag_graph_hops: 1 # Number of hops to expand from matched entities at query time (0 = seed nodes only; 1 = direct neighbors; increase for denser graphs)
# Define document loaders to control how RAG and `.file`/`--file` load files of specific formats.
document_loaders: {}
# You can add custom loaders using the following syntax:
# <file-extension>: <command-to-load-the-file>
# Note: Use `$1` for input file and `$2` for output file. If `$2` is omitted, use stdout as output.
# Examples:
# document_loaders:
# pdf: 'pdftotext $1 -' # https://poppler.freedesktop.org
# docx: 'pandoc --to plain $1' # https://pandoc.org
# jina: 'curl -fsSL https://r.jina.ai/$1 -H "Authorization: Bearer {{JINA_API_KEY}}"' # Requires a Jina API key in the Coyote vault
# ---- Appearance ----
highlight: true # Controls syntax highlighting
raw_markdown: false # When true, render markdown as raw text with syntax highlighting only. When false (default), transforms markdown syntax (headings, bold, lists, etc.) into styled terminal output
theme: null # null = the built-in dark theme; set to `light` for the built-in light theme.
# Custom themes: place a `dark.tmTheme` or `light.tmTheme` file in the Coyote config
# directory and it is used in place of the corresponding built-in.
# ---- REPL Prompt ----
# Custom REPL left/right prompts; see the [REPL Prompt Documentation](https://github.com/Dark-Alex-17/coyote/wiki/REPL-Prompt) for more information
left_prompt: null # If null, uses the built-in default:
# '{color.red}{model}){color.green}{?session {?agent {agent}>}{session}{?role /}}{!session {?agent {agent}>}}{role}{?rag @{rag}}{color.cyan}{?session )}{!session >}{color.reset} '
right_prompt: null # If null, uses the built-in default:
# '{color.cyan}{?reasoning_effort [{reasoning_effort}] }{color.purple}{?session {?consume_tokens {consume_tokens}({consume_percent}%)}{!consume_tokens {consume_tokens}}}{color.reset}'
# ---- Miscellaneous ----
user_agent: null # Set User-Agent HTTP header, use `auto` for coyote/<current-version>
save_shell_history: true # Whether to save shell execution command to the history file
sync_models_url: null # URL to sync model changes from. If null, uses the built-in default:
# https://raw.githubusercontent.com/Dark-Alex-17/coyote/refs/heads/main/models.yaml
# ---- Clients ----
# See the [Clients documentation](https://github.com/Dark-Alex-17/coyote/wiki/Clients) for more details
#
# All clients have the following configuration:
# - type: xxxx
# name: xxxx # Only use it to distinguish clients with the same client type. Optional
# models:
# - name: xxxx # Chat model
# max_input_tokens: 100000
# supports_vision: true
# supports_function_calling: true
# - name: xxxx # Embedding model
# type: embedding
# default_chunk_size: 1500
# max_batch_size: 100
# - name: xxxx # Reranker model
# type: reranker
# patch: # Patch API calls
# chat_completions: # API type; Possible values: chat_completions, embeddings, and rerank
# <regex>: # The regex to match model names, e.g. '.*' 'gpt-4o' 'gpt-4o|gpt-4-.*'
# url: '' # Patch request URL
# body: # Patch request body
# <json>
# headers: # Patch request headers
# <key>: <value>
# extra:
# proxy: socks5://127.0.0.1:1080 # Set proxy
# connect_timeout: 10 # Set timeout in seconds for connect to api
# read_timeout: 300 # Set timeout in seconds for a read stall (no bytes received); 0 disables (default: 300)
__CLIENTS_BLOCK__
+16 -20
View File
@@ -1,31 +1,27 @@
{
"mcpServers": {
"github": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"GITHUB_PERSONAL_ACCESS_TOKEN",
"ghcr.io/github/github-mcp-server"
],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "{{GITHUB_PERSONAL_ACCESS_TOKEN}}"
}
"type": "http",
"url": "https://api.githubcopilot.com/mcp"
},
"atlassian": {
"type": "stdio",
"command": "npx",
"args": ["-y", "mcp-remote@latest", "https://mcp.atlassian.com/v1/mcp"]
},
"docker": {
"type": "stdio",
"command": "uvx",
"args": ["mcp-server-docker"]
},
"slack": {
"command": "npx",
"args": ["-y", "slack-mcp-server@latest", "--transport", "stdio"],
"env": {
"SLACK_MCP_XOXC_TOKEN": "{{SLACK_MCP_XOXC_TOKEN}}",
"SLACK_MCP_XOXD_TOKEN": "{{SLACK_MCP_XOXD_TOKEN}}",
"SLACK_MCP_ADD_MESSAGE_TOOL": true
}
"ddg-search": {
"type": "stdio",
"command": "uvx",
"args": ["duckduckgo-mcp-server"]
},
"iwe": {
"type": "stdio",
"command": "iwec"
}
}
}
+44
View File
@@ -0,0 +1,44 @@
schemaVersion: '2'
kind: mixin
name: built-in-tools
description: >
Installs binaries and allows network domains required by Coyote's built-in
global tools and the default MCP server set. Auto-applied by Coyote's sbx
mixin discovery when running `coyote --sandbox`.
permissions:
network:
allow:
# fetch_url_via_jina + jina reader fallback
- 'r.jina.ai'
# get_current_weather (.sh, .py, .ts)
- 'wttr.in'
# search_arxiv (the .sh tool still uses http://, so :80 is required until fixed)
- 'export.arxiv.org'
- 'export.arxiv.org:80'
# search_arxiv + search_wikipedia may follow DOI redirects
- 'doi.org'
# search_wikipedia
- 'en.wikipedia.org'
# search_wolframalpha
- 'api.wolframalpha.com'
# web_search_perplexity
- 'api.perplexity.ai'
# web_search_tavily
- 'api.tavily.com'
# send_twilio
- 'api.twilio.com'
# MCP: github (built-in mcp.json: api.githubcopilot.com)
- 'api.githubcopilot.com'
# MCP: atlassian (built-in mcp.json: mcp-remote -> mcp.atlassian.com)
- 'mcp.atlassian.com'
# MCP: ddg-search (built-in mcp.json: uvx duckduckgo-mcp-server)
- 'duckduckgo.com'
- 'html.duckduckgo.com'
- 'lite.duckduckgo.com'
# MCP: npx-based servers (mcp-remote) pull from npm
- 'registry.npmjs.org'
# MCP: docker server may pull images from common registries
- 'ghcr.io'
- 'registry-1.docker.io'
- 'auth.docker.io'
+29 -6
View File
@@ -27,14 +27,30 @@ def _ensure_cwd_venv():
_ensure_cwd_venv()
def resolve_dir(env_name, default_path):
"""Resolve a directory at run time.
Prefer the override env var when set, otherwise fall back to the default
path derived from this script's own location, so the shim keeps working
when the config dir moves or is shared across environments with different
home directories.
"""
value = os.environ.get(env_name)
if value:
return value
return os.path.normpath(default_path)
def main():
(agent_func, raw_data) = parse_argv()
agent_data = parse_raw_data(raw_data)
root_dir = "{config_dir}"
setup_env(root_dir, agent_func)
self_dir = os.path.dirname(os.path.abspath(__file__))
agent_dir = os.path.normpath(os.path.join(self_dir, ".."))
root_dir = resolve_dir("{root_dir_env}", os.path.join(self_dir, "{root_dir_rel}"))
setup_env(root_dir, agent_dir, agent_func, raw_data)
agent_tools_path = os.path.join(root_dir, "agents/{agent_name}/tools.py")
agent_tools_path = os.path.join(agent_dir, "tools.py")
run(agent_tools_path, agent_func, agent_data)
@@ -50,7 +66,13 @@ def parse_raw_data(data):
def parse_argv():
agent_func = sys.argv[1]
agent_data = sys.argv[2]
tool_data_file = os.environ.get("LLM_TOOL_DATA_FILE")
if tool_data_file and os.path.isfile(tool_data_file):
with open(tool_data_file, "r", encoding="utf-8") as f:
agent_data = f.read()
else:
agent_data = sys.argv[2]
if (not agent_data) or (not agent_func):
print("Usage: ./{agent_name}.py <agent-func> <agent-data>", file=sys.stderr)
@@ -59,13 +81,14 @@ def parse_argv():
return agent_func, agent_data
def setup_env(root_dir, agent_func):
def setup_env(root_dir, agent_dir, agent_func, raw_data):
load_env(os.path.join(root_dir, ".env"))
os.environ["LLM_ROOT_DIR"] = root_dir
os.environ["LLM_AGENT_NAME"] = "{agent_name}"
os.environ["LLM_AGENT_FUNC"] = agent_func
os.environ["LLM_AGENT_ROOT_DIR"] = os.path.join(root_dir, "agents", "{agent_name}")
os.environ["LLM_AGENT_ROOT_DIR"] = agent_dir
os.environ["LLM_AGENT_CACHE_DIR"] = os.path.join(root_dir, "cache", "{agent_name}")
os.environ["LLM_AGENT_RAW_JSON"] = raw_data
def load_env(file_path):
+33 -9
View File
@@ -5,16 +5,37 @@
set -e
main() {
root_dir="{config_dir}"
self_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
agent_dir="$(cd "$self_dir/.." && pwd)"
root_dir="$(resolve_dir "{root_dir_env}" "$self_dir/{root_dir_rel}")"
functions_dir="$(resolve_dir "{functions_dir_env}" "$self_dir/{functions_dir_rel}")"
parse_argv "$@"
setup_env
tools_path="$root_dir/agents/{agent_name}/tools.sh"
tools_path="$agent_dir/tools.sh"
run
}
# Resolve a directory at run time: prefer the override env var ($1) when set,
# otherwise fall back to the default path ($2) derived from this script's own
# location, so the shim keeps working when the config dir moves or is shared
# across environments with different home directories.
resolve_dir() {
local override
override="$(printenv "$1" 2>/dev/null || true)"
if [[ -n "$override" ]]; then
echo "$override"
else
(cd "$2" 2>/dev/null && pwd) || echo "$2"
fi
}
parse_argv() {
agent_func="$1"
agent_data="$2"
if [[ -n "$LLM_TOOL_DATA_FILE" ]] && [[ -f "$LLM_TOOL_DATA_FILE" ]]; then
agent_data="$(cat "$LLM_TOOL_DATA_FILE")"
else
agent_data="$2"
fi
if [[ -z "$agent_data" ]] || [[ -z "$agent_func" ]]; then
die "usage: ./{agent_name}.sh <agent-func> <agent-data>"
fi
@@ -25,9 +46,10 @@ setup_env() {
export LLM_ROOT_DIR="$root_dir"
export LLM_AGENT_NAME="{agent_name}"
export LLM_AGENT_FUNC="$agent_func"
export LLM_AGENT_ROOT_DIR="$LLM_ROOT_DIR/agents/{agent_name}"
export LLM_AGENT_ROOT_DIR="$agent_dir"
export LLM_AGENT_CACHE_DIR="$LLM_ROOT_DIR/cache/{agent_name}"
export LLM_PROMPT_UTILS_FILE="{prompt_utils_file}"
export LLM_PROMPT_UTILS_FILE="$functions_dir/utils/prompt-utils.sh"
export LLM_AGENT_RAW_JSON="$agent_data"
}
load_env() {
@@ -54,10 +76,13 @@ run() {
die "error: no JSON data"
fi
if [[ ! -f "$tools_path" ]]; then
die "error: agent tools script not found: $tools_path"
fi
if [[ "$OS" == "Windows_NT" ]]; then
set -o igncr
tools_path="$(cygpath -w "$tools_path")"
tool_data="$(echo "$tool_data" | sed 's/\\/\\\\/g')"
fi
jq_script="$(cat <<-'EOF'
@@ -70,11 +95,11 @@ def to_args:
to_entries | .[] |
(.key | split("_") | join("-")) as $key |
if .value | type == "array" then
.value | .[] | "--\($key) \(. | escape_shell_word)"
.value | .[] | "--\($key)=\(. | escape_shell_word)"
elif .value | type == "boolean" then
if .value then "--\($key)" else "" end
else
"--\($key) \(.value | escape_shell_word)"
"--\($key)=\(.value | escape_shell_word)"
end;
[ to_args ] | join(" ")
EOF
@@ -118,4 +143,3 @@ die() {
}
main "$@"

Some files were not shown because too many files have changed in this diff Show More