Compare commits
7
Commits
08dddb09fb
...
176a81412a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
176a81412a | ||
|
|
cebc32f70a | ||
|
|
d3914955e2 | ||
|
|
a8d730cf01 | ||
|
|
cf638305e6 | ||
|
|
3601faa969 | ||
|
|
e4c5f42a25 |
@@ -137,6 +137,15 @@ instructions: |
|
|||||||
Open questions, and a **Task breakdown** where **each task is sized to ~1 engineer-day** (decompose
|
Open questions, and a **Task breakdown** where **each task is sized to ~1 engineer-day** (decompose
|
||||||
anything bigger NOW).
|
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
|
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
|
{{project_dir}} — and `librarian` for unfamiliar external libraries — to confirm the design's
|
||||||
assumptions before sizing. Do NOT guess file/symbol names — verify them.
|
assumptions before sizing. Do NOT guess file/symbol names — verify them.
|
||||||
@@ -255,6 +264,10 @@ instructions: |
|
|||||||
- Do NOT diverge from the task's stated scope; if the plan is wrong, STOP and report back.
|
- Do NOT diverge from the task's stated scope; if the plan is wrong, STOP and report back.
|
||||||
|
|
||||||
## CONTEXT
|
## 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
|
<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>
|
snippets explore found showing the patterns to follow>
|
||||||
"
|
"
|
||||||
@@ -278,7 +291,9 @@ instructions: |
|
|||||||
this isolates THIS task's commits on the shared run branch from earlier tasks' work.
|
this isolates THIS task's commits on the shared run branch from earlier tasks' work.
|
||||||
|
|
||||||
PLAN — acceptance criteria to check against:
|
PLAN — acceptance criteria to check against:
|
||||||
<paste the task index.md body + the relevant PLAN-<slug>.md section VERBATIM>"
|
<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
|
- **`ADVERSARIAL_REVIEW: DIVERGES`** → treat it as a blocker: resume the SAME Sisyphus session
|
||||||
@@ -328,7 +343,11 @@ instructions: |
|
|||||||
5. **Close the task.** Per `task-tracking`: check off Steps + Acceptance criteria (verified, not
|
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
|
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
|
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); set `status: complete`.
|
(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
|
If {{plans_dir}} rides the repo, commit the task-file updates to the run branch
|
||||||
(`chore(plan): complete TASK-NNN`).
|
(`chore(plan): complete TASK-NNN`).
|
||||||
|
|
||||||
@@ -391,9 +410,17 @@ instructions: |
|
|||||||
the responsible Sisyphus session) before any PR exists.
|
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
|
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
|
`feat/PLAN-<slug>` → {{base_branch}}. First collect every "Follow-ups:" line from the completed
|
||||||
tasks' `log.md` files. Title: `PLAN-<slug>: <design doc title>`. Body MUST contain, in order:
|
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,
|
- 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 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,
|
- 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
|
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.
|
section is the reviewer's contract for what the code does NOT do by itself.
|
||||||
@@ -411,7 +438,8 @@ instructions: |
|
|||||||
`status: pending`, with the WHAT/WHERE/WHY/WHEN and which TASK-NNN surfaced it. Then edit the
|
`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
|
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
|
implement these yourself — creating them IS the deliverable; the user picks them up after the
|
||||||
merge.
|
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
|
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
|
`**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
|
completed task's `log.md`. If {{plans_dir}} rides the repo, commit these planning updates to
|
||||||
@@ -476,6 +504,10 @@ instructions: |
|
|||||||
- Polling `agent__collect` on a running agent.
|
- Polling `agent__collect` on a running agent.
|
||||||
- Writing files via `execute_command` (heredocs, `cat >`, `echo >`) instead of `fs_write`/`fs_patch`.
|
- 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.
|
- 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)
|
## Hard blocks (NEVER)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
name: code-reviewer
|
name: code-reviewer
|
||||||
description: CodeRabbit-style code reviewer - spawns per-file reviewers, synthesizes findings
|
description: CodeRabbit-style code reviewer - spawns per-file reviewers, synthesizes findings
|
||||||
version: 2.3.0
|
version: 2.4.0
|
||||||
|
|
||||||
auto_continue: true
|
auto_continue: true
|
||||||
max_auto_continues: 20
|
max_auto_continues: 20
|
||||||
@@ -23,6 +23,12 @@ variables:
|
|||||||
- name: prior_art_agent
|
- 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.
|
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: ''
|
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
|
- name: auto_confirm
|
||||||
description: Auto-confirm command execution
|
description: Auto-confirm command execution
|
||||||
default: '1'
|
default: '1'
|
||||||
@@ -49,13 +55,20 @@ instructions: |
|
|||||||
## Workflow
|
## Workflow
|
||||||
|
|
||||||
1. **Get the diff:** Run `get_diff` to get the git diff (defaults to staged changes, falls back to unstaged)
|
1. **Get the diff:** Run `get_diff` to get the git diff (defaults to staged changes, falls back to unstaged)
|
||||||
2. **Parse changed files:** Extract the list of files from the diff
|
2. **Resolve quality bar:** Determine the rigor and surfaces governing this review, in strict precedence order:
|
||||||
3. **Create todos:** One todo per phase (get diff, spawn reviewers, operational-history lane, collect results, synthesize report)
|
- **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`.
|
||||||
4. **Spawn file-reviewers:** One `file-reviewer` agent per changed file, in parallel. Apply the `delegation-protocol` structured prompt format.
|
- **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`.
|
||||||
5. **Broadcast sibling roster:** Send each file-reviewer a message with all sibling IDs and their file assignments
|
- **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`.
|
||||||
6. **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.
|
|
||||||
7. **Collect all results:** Per `parallel-research`, do not poll. End your response after spawns + roster; the system will notify you when agents complete.
|
Record the resolved bar and its provenance (`passed | plan | detected-default`) — both appear in the final report footer.
|
||||||
8. **Synthesize:** Combine all findings into a CodeRabbit-style report. Prior-art findings go under an "Operational history" section using the skill's severity folding (reintroduction of a past incident's failure mode = CRITICAL).
|
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
|
## Spawning File Reviewers
|
||||||
|
|
||||||
@@ -77,6 +90,21 @@ instructions: |
|
|||||||
- Load `code-review` and `ai-slop-remover` skills before reading any code
|
- 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 `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 `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
|
- Apply all loaded skill checklists to the diff
|
||||||
- Use targeted fs_read with offset/limit; max 5 file reads
|
- Use targeted fs_read with offset/limit; max 5 file reads
|
||||||
- End with REVIEW_COMPLETE
|
- End with REVIEW_COMPLETE
|
||||||
@@ -89,6 +117,11 @@ instructions: |
|
|||||||
## CONTEXT
|
## CONTEXT
|
||||||
Project: {{project_dir}}
|
Project: {{project_dir}}
|
||||||
File under review: <file_path>
|
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:
|
||||||
<diff content for this file>
|
<diff content for this file>
|
||||||
@@ -97,6 +130,18 @@ instructions: |
|
|||||||
|
|
||||||
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.
|
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
|
## Sibling Roster Broadcast
|
||||||
|
|
||||||
After spawning ALL file-reviewers (collecting their IDs), send each one a message with the roster:
|
After spawning ALL file-reviewers (collecting their IDs), send each one a message with the roster:
|
||||||
@@ -117,6 +162,23 @@ instructions: |
|
|||||||
|
|
||||||
Skip binary files and files with only whitespace changes.
|
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
|
## Final Report Format
|
||||||
|
|
||||||
After collecting all file-reviewer results, synthesize into:
|
After collecting all file-reviewer results, synthesize into:
|
||||||
@@ -148,8 +210,12 @@ instructions: |
|
|||||||
## Operational history
|
## Operational history
|
||||||
<only when the lane ran: archaeology + prior-art findings with incident/commit references, or "no relevant incident history found">
|
<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*
|
*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
|
## Edge Cases
|
||||||
@@ -164,7 +230,7 @@ instructions: |
|
|||||||
1. **Always use `get_diff` first:** Don't assume what changed
|
1. **Always use `get_diff` first:** Don't assume what changed
|
||||||
2. **Spawn in parallel:** All file-reviewers should be spawned before collecting any
|
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
|
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
|
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
|
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.
|
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.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
name: file-reviewer
|
name: file-reviewer
|
||||||
description: Reviews a single file's diff for bugs, style issues, and cross-cutting concerns
|
description: Reviews a single file's diff for bugs, style issues, and cross-cutting concerns
|
||||||
version: 2.2.0
|
version: 2.3.0
|
||||||
|
|
||||||
skills_enabled: true
|
skills_enabled: true
|
||||||
enabled_skills:
|
enabled_skills:
|
||||||
@@ -8,6 +8,13 @@ enabled_skills:
|
|||||||
- ai-slop-remover
|
- ai-slop-remover
|
||||||
- transactional-integrity
|
- transactional-integrity
|
||||||
- logging-discipline
|
- logging-discipline
|
||||||
|
- rest-api-review
|
||||||
|
- cli-review
|
||||||
|
- library-review
|
||||||
|
- worker-review
|
||||||
|
- iac-review
|
||||||
|
- migration-review
|
||||||
|
- cicd-review
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
- name: project_dir
|
- name: project_dir
|
||||||
@@ -118,6 +125,10 @@ instructions: |
|
|||||||
- **🟢 SUGGESTION** — Clarity, coupling, naming, footgun mitigations, missing tests for the change
|
- **🟢 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
|
- **💡 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
|
## Rules
|
||||||
|
|
||||||
1. **Be specific.** Reference exact line numbers and code.
|
1. **Be specific.** Reference exact line numbers and code.
|
||||||
|
|||||||
@@ -306,6 +306,7 @@ instructions: |
|
|||||||
|
|
||||||
Original request: <one-line summary of what the user asked for>
|
Original request: <one-line summary of what the user asked for>
|
||||||
Scope: <which directories or files the changes are expected to touch>
|
Scope: <which directories or files the changes are expected to touch>
|
||||||
|
Quality bar: rigor=<...>, surfaces=<...>
|
||||||
|
|
||||||
Coder summaries:
|
Coder summaries:
|
||||||
- <coder 1 session_id>: <plan_summary from CODER_COMPLETE>
|
- <coder 1 session_id>: <plan_summary from CODER_COMPLETE>
|
||||||
@@ -314,13 +315,17 @@ instructions: |
|
|||||||
Run `get_diff` against the staged or recent changes, fan out file-reviewers per changed file as usual, and synthesize."
|
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
|
### 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).
|
- **🔴 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 unless the work was explicitly scoped to defer them. If unsure, ASK the user via `user__ask` whether to fix or accept.
|
- **🟡 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.
|
- **🟢 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.
|
- **`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
|
### 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.
|
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.
|
||||||
@@ -370,6 +375,8 @@ instructions: |
|
|||||||
- `standard` (default) — anything that will be deployed, shared, committed to a shared repo, or built upon. Blocks Critical + High.
|
- `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.
|
- `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.
|
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):
|
**Spawn pattern** (the prompt IS its whole context — include posture and deployment context):
|
||||||
|
|||||||
@@ -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__
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
---
|
||||||
|
description: Review CI/CD pipeline definitions - action/step pinning by SHA, token and credential permission scoping, secret exposure to fork-PR triggers, and cross-branch cache poisoning. Load when a diff touches workflow/pipeline files such as .github/workflows/*, GitLab CI config, or equivalent pipeline definitions. Findings fold into the standard code-review severity taxonomy. Grants read-only filesystem access for tracing workflows, triggers, and permission blocks.
|
||||||
|
enabled_tools: fs_read, fs_grep, fs_glob, fs_cat, fs_ls
|
||||||
|
---
|
||||||
|
You are reviewing CI/CD pipeline definitions. The generic correctness checklist asks "does this pipeline run?"; you ask **"what can this pipeline be made to do by someone who controls an input to it — a tag, a fork PR, a cache key?"** A workflow file is production code with production credentials that runs third-party code on every push, yet it is otherwise reviewed by nobody. Most pipeline incidents are not broken builds — they are a mutable action tag that started doing something new, a token scoped far beyond its job, or a secret handed to code from a fork.
|
||||||
|
|
||||||
|
## When to load this skill
|
||||||
|
|
||||||
|
The diff touches ANY of: workflow/pipeline files — `.github/workflows/*`, GitLab CI config, or equivalent pipeline definitions in other systems. If the diff is application code with unchanged pipelines — unload; this checklist has nothing for you.
|
||||||
|
|
||||||
|
## Marker semantics
|
||||||
|
|
||||||
|
Every checklist item below carries a severity emoji AND a `[convention]` or `[correctness]` marker; both ride in the finding title so downstream tooling can act on them mechanically. `[convention]` findings are rigor-foldable (the orchestrator may lower them under a relaxed quality bar) and rejectable — but ONLY with cited evidence: a repo convention at file:line, or a recorded plan decision. `[correctness]` is reserved for contract breaks; those findings are neither foldable nor rejectable.
|
||||||
|
|
||||||
|
## Linters and mechanized checks
|
||||||
|
|
||||||
|
The review orchestrator runs the domain's mechanized checker — `actionlint` for GitHub Actions workflows; your CONTEXT may already include its output — do not re-derive it. Spend your prose on what the linter cannot reach: whether a token's permissions match what the job actually does, what a fork-triggered run can see, whether a cache key crosses a trust boundary. If the repo plausibly warrants a linter config it lacks (workflow files but no `actionlint` wiring), emit a 🟢 `[convention]` finding naming the gap.
|
||||||
|
|
||||||
|
## The checklist
|
||||||
|
|
||||||
|
Severities below are the production bar. Each item is a context-sensitive question, not an absolute — read the trigger blocks and permission blocks before flagging, and state any exemption you rely on.
|
||||||
|
|
||||||
|
### 1. 🟡 `[convention]` Actions pinned by mutable tag instead of SHA
|
||||||
|
|
||||||
|
Does the diff reference third-party actions or pipeline steps by a mutable tag (`@v4`, `@main`) rather than a full commit SHA? A mutable tag means the code your pipeline runs — with its credentials — can change without any change in your repo; tags have been retargeted maliciously in the wild. The house fix is SHA-pinning with a tag comment (and a bot to update pins). First-party actions from the same repo/org can be exempt per repo convention — cite the convention if you rely on it.
|
||||||
|
|
||||||
|
### 2. 🟡 `[convention]` Token/credential permissions broader than the job needs
|
||||||
|
|
||||||
|
Does each job's token grant match what the job actually does? Look for missing explicit permission blocks (falling back to a broad default), write scopes on jobs that only read, and org-level credentials in jobs that need repo-level access. The finding names the scoped alternative: the specific permissions the job's steps use. A workflow-level broad grant with per-job narrowing is acceptable shape; per-job broad grants "to be safe" are the finding.
|
||||||
|
|
||||||
|
### 3. 🔴 `[convention]` Secrets exposed to fork-PR triggers
|
||||||
|
|
||||||
|
Can a pull request from a fork reach this workflow's secrets? The dangerous shapes: triggers that run with secret access on fork-controlled code (e.g. `pull_request_target` checking out the PR head), secrets passed into steps that execute fork-modified scripts, and label-gated runs where the gate is applied after checkout. This item is co-owned with `security-review`'s supply-chain checklist item — that skill owns the full exploitation analysis; you flag the exposure the moment the trigger/secret/checkout combination makes it possible. The severity stays 🔴 regardless of the declared quality bar — fork-reachable secrets are critical at every rigor, and this item should never be folded down. Workflows that run on fork PRs WITHOUT secrets, or with secrets only after a trusted-code boundary, are the correct shapes — verify the checkout ref before accepting them.
|
||||||
|
|
||||||
|
### 4. 🟢 `[convention]` Cross-branch cache poisoning
|
||||||
|
|
||||||
|
Do cache keys let an untrusted branch write cache entries that a trusted branch (main, release) later restores? Caches written by fork-PR or feature-branch runs and restored by default-branch runs let attacker-influenced artifacts flow into trusted builds. Check the cache key/scope construction and the platform's cache-isolation rules — some platforms already isolate caches by branch with one-way fallback; a pattern the platform provably isolates is exempt, and worth citing.
|
||||||
|
|
||||||
|
## Ground-truth discipline
|
||||||
|
|
||||||
|
- READ the trigger block and permission block of every workflow the diff touches — the risk is almost always in the trigger/checkout/secret combination, not in the step commands.
|
||||||
|
- `fs_grep` sibling workflows for the house idioms (SHA-pinning style, permission-block placement, cache-key construction) and cite the sibling at file:line when flagging deviation.
|
||||||
|
- Check what each referenced action actually is (first-party vs third-party, checkout target) before applying the pinning and fork-exposure items.
|
||||||
|
- Do not assert platform behavior (default permissions, cache isolation) from memory alone when the repo's config could override it — check the org/repo-level settings files if present, and state assumptions otherwise.
|
||||||
|
|
||||||
|
## What this skill does NOT check
|
||||||
|
|
||||||
|
- The full exploitation analysis of exposed secrets, injection via untrusted workflow inputs (`${{ }}` interpolation attacks), and supply-chain trust of the pinned actions themselves → `security-review` (item 3 above is explicitly co-owned with its supply-chain item).
|
||||||
|
- Whether deploy/release steps the pipeline runs are idempotent and safe to re-run → `transactional-integrity`.
|
||||||
|
- Log output conventions of pipeline steps → `logging-discipline`.
|
||||||
|
- Metrics/alerts on pipeline health and deploy outcomes → `observability-review`.
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
description: Review the command-line surface contract of a change - exit codes, stdout/stderr channel discipline, help text, non-interactive operation, signal/cleanup behavior, and config precedence. Load when a diff touches argument-parser definitions, the main/entrypoint of a binary, or subcommand modules. Findings fold into the standard code-review severity taxonomy. Grants read-only filesystem access for tracing entrypoints, parsers, and exit paths.
|
||||||
|
enabled_tools: fs_read, fs_grep, fs_glob, fs_cat, fs_ls
|
||||||
|
---
|
||||||
|
You are reviewing a command-line interface. The generic correctness checklist asks "does this command work when a human runs it?"; you ask **"does this command keep its contract with the scripts, pipes, and CI jobs that run it unattended?"** A CLI's real callers are rarely humans at a terminal — they are shell scripts branching on `$?`, pipelines parsing stdout, and cron jobs with no TTY. Most CLI breakage in the wild is not wrong logic; it is a success that exits 1, a diagnostic that corrupts a pipe, or a prompt that hangs a CI job forever.
|
||||||
|
|
||||||
|
## When to load this skill
|
||||||
|
|
||||||
|
The diff touches ANY of: argument-parser definitions (flag/option/subcommand declarations), the `main`/entrypoint of a binary, or subcommand modules. If the diff is library internals behind an unchanged command surface — unload; this checklist has nothing for you.
|
||||||
|
|
||||||
|
## Marker semantics
|
||||||
|
|
||||||
|
Every checklist item below carries a severity emoji AND a `[convention]` or `[correctness]` marker; both ride in the finding title so downstream tooling can act on them mechanically. `[convention]` findings are rigor-foldable (the orchestrator may lower them under a relaxed quality bar) and rejectable — but ONLY with cited evidence: a repo convention at file:line, or a recorded plan decision. `[correctness]` is reserved for contract breaks; those findings are neither foldable nor rejectable.
|
||||||
|
|
||||||
|
## Linters and mechanized checks
|
||||||
|
|
||||||
|
The review orchestrator runs mechanized checks (shell linters, help-text validators); your CONTEXT may already include their output — do not re-derive it. Spend your prose on what linters cannot reach: exit-code semantics on each error path, which stream a message lands on, whether a prompt has an escape hatch. If the repo plausibly warrants a linter config it lacks (shell scripts but no shell linter config), emit a 🟢 `[convention]` finding naming the gap.
|
||||||
|
|
||||||
|
## The checklist
|
||||||
|
|
||||||
|
Severities below are the production bar. Each item is a context-sensitive question, not an absolute — trace the actual exit paths and output calls before flagging.
|
||||||
|
|
||||||
|
### 1. 🔴 `[correctness]` Error paths exiting 0 / success paths exiting non-zero
|
||||||
|
|
||||||
|
Trace every exit path the diff adds or modifies: does each failure propagate a non-zero exit code all the way out of `main`, and does success exit 0? The classic bugs: an error that is printed and then falls through to a normal return; a caught exception that logs and continues; a match arm that swallows a `Result`. Scripts branch on `$?` — an inverted exit code silently corrupts every automation built on this command. This is the exit-code contract; it is never foldable and never rejectable.
|
||||||
|
|
||||||
|
### 2. 🟡 `[convention]` Diagnostics on stdout corrupting pipeable output
|
||||||
|
|
||||||
|
If the command's stdout is (or plausibly will be) piped or parsed — it prints data, JSON, lists, paths — then progress messages, warnings, and diagnostics on stdout corrupt the stream. Do new prints route diagnostics to stderr and reserve stdout for payload? A purely interactive command with no parseable output can be exempt — say so when you rely on that. Which *level and format* diagnostics use is `logging-discipline`'s question; yours is which stream they land on.
|
||||||
|
|
||||||
|
### 3. 🟢 `[convention]` Missing or wrong --help for new flags
|
||||||
|
|
||||||
|
Does every flag, option, and subcommand the diff adds appear in help output with an accurate description? Check the parser declarations: a flag with no help string, a stale description contradicting new behavior, or a new subcommand missing from the top-level help listing. Help text is the CLI's only discoverable documentation.
|
||||||
|
|
||||||
|
### 4. 🟡 `[convention]` Interactive prompt with no non-interactive escape
|
||||||
|
|
||||||
|
Does the diff add a prompt (confirmation, password, selection)? Then there must be a non-interactive path: a flag (`--yes`/`--force`-style), an environment variable, or reading from stdin — and ideally the prompt should detect a missing TTY rather than hang. A prompt with no escape hatch deadlocks CI and cron callers. Check what escape idiom the repo's existing prompts use and whether the new one matches.
|
||||||
|
|
||||||
|
### 5. 🟡 `[convention]` No signal/cleanup handling for long-running commands with temp state
|
||||||
|
|
||||||
|
If the diff adds a long-running command that creates temp files, lockfiles, partial output, or spawns children: what happens on Ctrl-C or SIGTERM? Look for signal handling, cleanup guards (drop/defer/finally/trap), or an idiom the repo already uses. Orphaned locks and half-written files are the finding. Short-lived commands with no temp state are exempt — this item is scoped to commands that hold state long enough for interruption to be a realistic event.
|
||||||
|
|
||||||
|
### 6. 🟢 `[convention]` Config precedence violated or undocumented
|
||||||
|
|
||||||
|
If the command reads configuration from more than one source, the conventional precedence is flag > environment variable > config file. Does the diff's resolution order honor that — and honor whatever order the repo has already established? A new setting that reads only the file when its siblings accept a flag override, or a precedence order documented nowhere, is the finding. Cite the repo's existing resolution code when flagging a deviation.
|
||||||
|
|
||||||
|
## Ground-truth discipline
|
||||||
|
|
||||||
|
- READ the full path from error site to process exit — exit-code bugs live in the propagation, not the error site. `fs_grep` for the exit/return conventions the entrypoint uses.
|
||||||
|
- Check sibling subcommands for the established idioms (stderr usage, prompt escape flags, cleanup guards) — a new subcommand skipping the house pattern is the strongest form of evidence.
|
||||||
|
- Do not flag hypothetical piping of a command that is documented interactive-only; note the assumption instead.
|
||||||
|
|
||||||
|
## What this skill does NOT check
|
||||||
|
|
||||||
|
- Whether argument or path inputs are exploitable (injection, traversal, secrets on the command line) → `security-review`.
|
||||||
|
- Whether the state a command mutates is changed idempotently and atomically under reruns → `transactional-integrity`.
|
||||||
|
- Log levels, formats, and message register of diagnostics → `logging-discipline` (this skill only checks which stream they use).
|
||||||
|
- Metrics and alerting for operationally significant commands → `observability-review`.
|
||||||
@@ -31,6 +31,18 @@ Produce a structured proposal (iterate with the user when interactive — load t
|
|||||||
- **Open questions** — ONLY questions the codebase cannot answer (business rules, priority calls). If none, say "No open questions."
|
- **Open questions** — ONLY questions the codebase cannot answer (business rules, priority calls). If none, say "No open questions."
|
||||||
- **Task breakdown** — see below.
|
- **Task breakdown** — see below.
|
||||||
|
|
||||||
|
## Quality bar round (closes Step 2)
|
||||||
|
|
||||||
|
The last round of Step 2 sets the plan's quality bar. It is grilling-compatible — run it as numbered questions, each carrying a recommended answer, like any other frontier round:
|
||||||
|
|
||||||
|
1. **Propose `rigor`** — one of `poc | prototype | production` (default `production`). Infer the recommended value from the design doc's own language: "spike"/"demo" → `poc`; "iterate"/"internal" → `prototype`; otherwise `production`. Rigor calibrates which review-finding severities BLOCK downstream work: 🔴-critical findings block at EVERY rigor; at `poc`, suggestion/nitpick-level (🟢/💡) convention findings may be dropped from reports entirely. Anything a lower rigor defers is tracked as a follow-up — never silently dropped.
|
||||||
|
2. **Propose `surfaces`** — zero or more of the closed enum: `rest-api`, `cli`, `library`, `worker`, `iac`, `db-migration`, `frontend`, `ci-cd` (`grpc`/`graphql` are aliases for `rest-api`), plus the escape hatch `other:<label>` for anything outside it. Infer from the approach: an HTTP handler → `rest-api`, a schema change → `db-migration`, and so on.
|
||||||
|
3. **Per-surface confirm/drop** — for each accepted surface, present the headline best practices its reviewers will enforce and let the user confirm or drop each one. Every drop demands a one-line reason and lands in the plan's `## Quality bar` section — a dropped practice without a recorded reason WILL be re-litigated by a reviewer.
|
||||||
|
|
||||||
|
For an `other:<label>` surface no reviewer checklist exists, so the lane is: a librarian lookup distills an authoritative best-practice checklist for the label; the user confirms or drops each item; accepted items become task acceptance criteria where possible, otherwise they live under `## Quality bar → Long-tail criteria`. (The architect drives the lookup; this skill documents the shape the results take in the plan.)
|
||||||
|
|
||||||
|
Autonomous runs (no user to grill): take the inferred values, drop nothing, and note "quality bar inferred, not user-confirmed" in the plan.
|
||||||
|
|
||||||
## Task breakdown rules
|
## Task breakdown rules
|
||||||
|
|
||||||
| Rule | Why |
|
| Rule | Why |
|
||||||
@@ -51,6 +63,8 @@ Write `PLAN-<slug>.md` (kebab-case slug from the topic; verify no collision) to
|
|||||||
slug: <slug>
|
slug: <slug>
|
||||||
status: draft # draft | active | implemented
|
status: draft # draft | active | implemented
|
||||||
created: YYYY-MM-DD
|
created: YYYY-MM-DD
|
||||||
|
rigor: production # poc | prototype | production; omitted = production
|
||||||
|
surfaces: [] # from the closed enum and/or other:<label>; omitted = []
|
||||||
---
|
---
|
||||||
|
|
||||||
# <Title>
|
# <Title>
|
||||||
@@ -60,6 +74,7 @@ created: YYYY-MM-DD
|
|||||||
## Approach
|
## Approach
|
||||||
## Alternatives considered
|
## Alternatives considered
|
||||||
## Constraints and risks
|
## Constraints and risks
|
||||||
|
## Quality bar
|
||||||
## Open questions
|
## Open questions
|
||||||
## Task breakdown
|
## Task breakdown
|
||||||
|
|
||||||
@@ -67,6 +82,8 @@ created: YYYY-MM-DD
|
|||||||
|---|------|------|-----------|-------|
|
|---|------|------|-----------|-------|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`## Quality bar` records the quality-bar round in human-readable form: the rigor line with its one-line reason, the surfaces line, the dropped-practices list (each entry with its one-line reason and the date it was decided), and the long-tail criteria block (`none`, or the distilled checklist for each `other:<label>` surface).
|
||||||
|
|
||||||
The plan is the implementers' entire context. Write for the "sealed container" standard: every question an implementer will hit is either answered inline or delegated via a pointer to the exact file/doc that answers it (where infra code goes, what DB tech, which layout to mirror, exact test commands). Paste short code snippets for load-bearing patterns — a path alone forces re-exploration; a stale claim fails the executor mid-implementation.
|
The plan is the implementers' entire context. Write for the "sealed container" standard: every question an implementer will hit is either answered inline or delegated via a pointer to the exact file/doc that answers it (where infra code goes, what DB tech, which layout to mirror, exact test commands). Paste short code snippets for load-bearing patterns — a path alone forces re-exploration; a stale claim fails the executor mid-implementation.
|
||||||
|
|
||||||
## Anti-patterns
|
## Anti-patterns
|
||||||
@@ -77,3 +94,4 @@ The plan is the implementers' entire context. Write for the "sealed container" s
|
|||||||
- Acceptance criteria describing implementation ("uses a for loop") instead of behavior.
|
- Acceptance criteria describing implementation ("uses a for loop") instead of behavior.
|
||||||
- Open questions the code could have answered — grep first, ask last.
|
- Open questions the code could have answered — grep first, ask last.
|
||||||
- Unrecorded decisions — every settled fork carries its reason.
|
- Unrecorded decisions — every settled fork carries its reason.
|
||||||
|
- Declaring `rigor: poc` to dodge review findings the user never agreed to drop.
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
description: Review the infrastructure-as-code surface of a change - Terraform, Helm charts, Kubernetes manifests, Dockerfiles, and compose files. Checks provider/module/base-image pinning, plaintext secret material, IAM/RBAC scoping, resource requests/limits, mutable image tags in deploy paths, and destructive plan operations without lifecycle guards. Load when a diff touches *.tf files, Helm charts, K8s manifests, Dockerfiles, or compose files. Findings fold into the standard code-review severity taxonomy. Grants read-only filesystem access for tracing modules, values files, and manifests.
|
||||||
|
enabled_tools: fs_read, fs_grep, fs_glob, fs_cat, fs_ls
|
||||||
|
---
|
||||||
|
You are reviewing infrastructure-as-code. The generic correctness checklist asks "does this config apply cleanly?"; you ask **"what does this change do to the running system on apply day — and on every rebuild after?"** IaC is executable: an unpinned module resolves differently next month, a wildcard grant is a standing invitation, and a resource replacement that looked like an update deletes a database. Most IaC incidents are not syntax errors — they are a drifted dependency, a `latest` tag that moved, or a destroy the plan output showed and nobody read.
|
||||||
|
|
||||||
|
## When to load this skill
|
||||||
|
|
||||||
|
The diff touches ANY of: `*.tf` files or Terraform modules, Helm charts or values files, Kubernetes manifests, Dockerfiles, or compose files. If the diff is application code with unchanged infrastructure — unload; this checklist has nothing for you.
|
||||||
|
|
||||||
|
## Marker semantics
|
||||||
|
|
||||||
|
Every checklist item below carries a severity emoji AND a `[convention]` or `[correctness]` marker; both ride in the finding title so downstream tooling can act on them mechanically. `[convention]` findings are rigor-foldable (the orchestrator may lower them under a relaxed quality bar) and rejectable — but ONLY with cited evidence: a repo convention at file:line, or a recorded plan decision. `[correctness]` is reserved for contract breaks; those findings are neither foldable nor rejectable.
|
||||||
|
|
||||||
|
## Linters and mechanized checks
|
||||||
|
|
||||||
|
The review orchestrator runs the domain's mechanized checkers — `tflint`/`checkov` for Terraform, `hadolint` for Dockerfiles, `kubeconform` for Kubernetes manifests; your CONTEXT may already include their output — do not re-derive it. Spend your prose on what those tools cannot reach: blast radius of a destructive operation, whether a wildcard grant had a scoped alternative, whether a pin was omitted deliberately. If the repo plausibly warrants a linter config it lacks (Terraform but no `tflint`/`checkov` config, Dockerfiles but no `hadolint` config, manifests but no `kubeconform` wiring), emit a 🟢 `[convention]` finding naming the gap.
|
||||||
|
|
||||||
|
## The checklist
|
||||||
|
|
||||||
|
Severities below are the production bar. Each item is a context-sensitive question, not an absolute — read the module sources, values files, and sibling stacks before flagging, and state any exemption you rely on.
|
||||||
|
|
||||||
|
### 1. 🟡 `[convention]` Unpinned providers, modules, or base images
|
||||||
|
|
||||||
|
Does the diff add or modify a provider requirement, module source, or base image without pinning it to an exact version (or digest)? Unpinned means unbuildable-reproducibly: the same code produces different infrastructure next month. Check for version constraints on providers, ref/version on module sources, and tags-plus-digests on base images. A floating constraint that the repo's lockfile then pins is a weaker finding — cite the lockfile if it exists. Internal modules versioned by the same repo's release process can be exempt; say so.
|
||||||
|
|
||||||
|
### 2. 🔴 `[convention]` Plaintext secret material in code, state, or values
|
||||||
|
|
||||||
|
Does the diff introduce secret material — passwords, tokens, keys, connection strings with credentials — in plaintext in config files, values files, environment blocks, or anywhere it lands in state or the image? Report the finding and the location; defer the exploitation analysis to `security-review`, which owns the abuse question. The severity stays 🔴 regardless of the declared quality bar — a committed secret is critical at every rigor, and this item should never be folded down. Values wired from an external secret manager, encrypted-at-rest secret stores, or CI-injected references are the correct shapes — verify the reference is actually a reference, not an inlined value.
|
||||||
|
|
||||||
|
### 3. 🟡 `[convention]` Wildcard IAM/RBAC where a scoped grant is available
|
||||||
|
|
||||||
|
Does the diff grant `*` actions, `*` resources, cluster-admin, or a similarly broad role where the workload's actual needs are enumerable? The finding must name the scoped alternative: the specific actions the code paths use, the resource ARNs/namespaces in play. A genuinely dynamic resource set can justify a partial wildcard — the finding is a wildcard chosen for convenience when a scoped grant was available. Whether the over-grant is *exploitable* in this environment is `security-review`'s question; yours is the least-privilege contract.
|
||||||
|
|
||||||
|
### 4. 🟢 `[convention]` Missing resource requests/limits on workloads
|
||||||
|
|
||||||
|
Do new or modified workloads (Deployments, StatefulSets, Jobs, compose services in deploy paths) declare resource requests and limits? A workload with no requests schedules blind and a workload with no limits can starve its node neighbors. Check whether the repo sets these via a shared chart/library or namespace defaults (LimitRange) before flagging — a house mechanism that already applies them is an exemption worth citing.
|
||||||
|
|
||||||
|
### 5. 🟡 `[convention]` Mutable image tags in deploy paths
|
||||||
|
|
||||||
|
Does anything in a deploy path reference an image by a mutable tag — `latest`, a branch name, an unversioned tag? A mutable tag means the deployed artifact changes without a corresponding code change: rollbacks stop meaning anything and two environments running "the same tag" can run different code. The fix is an immutable version tag or digest. Local-development compose files not used for deployment are exempt — verify which one this file is before flagging, and say so.
|
||||||
|
|
||||||
|
### 6. 🟡 `[convention]` Destructive plan operations without lifecycle guards
|
||||||
|
|
||||||
|
Will applying this diff destroy or replace stateful resources — a changed identifier forcing replacement, a removed resource holding data, a rename the tool treats as destroy-and-create? For resources where destruction means data loss (databases, buckets, volumes), look for the guardrails: `prevent_destroy` lifecycle blocks, deletion protection flags, `moved`/state-migration blocks for renames. The finding names the resource, why the plan will destroy it, and the guard or migration that is missing. Stateless, freely recreatable resources are exempt.
|
||||||
|
|
||||||
|
## Ground-truth discipline
|
||||||
|
|
||||||
|
- READ the module source and values files a manifest consumes, not just the diff hunk — pins, secrets, and defaults often live one level up or down from the change.
|
||||||
|
- `fs_grep` sibling stacks/charts for the house idioms (version-pinning style, secret-reference mechanism, shared resource-limit templates) and cite the sibling at file:line when flagging deviation.
|
||||||
|
- Distinguish deploy-path files from local-dev scaffolding before applying deploy-path severities; the file's consumers, not its syntax, determine which it is.
|
||||||
|
- Do not guess what a plan will do from the diff alone when the change is ambiguous — say what evidence would settle it (the plan output) and flag the ambiguity itself.
|
||||||
|
|
||||||
|
## What this skill does NOT check
|
||||||
|
|
||||||
|
- Whether an exposed secret, over-grant, or open ingress is actually exploitable, and supply-chain trust of images/modules → `security-review` (this skill reports the presence of the hazard; that skill owns the abuse analysis).
|
||||||
|
- Whether provisioning/deployment scripts mutate state idempotently and survive reruns → `transactional-integrity`.
|
||||||
|
- Log configuration conventions inside deployed workloads → `logging-discipline`.
|
||||||
|
- Metrics, alerts, and dashboards for new infrastructure → `observability-review`.
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
description: Review the public-API surface contract of a library change - semver discipline against the manifest version, panic reachability from public entry points, doc coverage, error-type information quality, dependency weight/pinning, and internal-type leakage. Load when a diff touches the public API of a lib crate/package - exported symbols, pub items, __init__/index exports - or its manifest version. Findings fold into the standard code-review severity taxonomy. Grants read-only filesystem access for tracing exports, manifests, and public signatures.
|
||||||
|
enabled_tools: fs_read, fs_grep, fs_glob, fs_cat, fs_ls
|
||||||
|
---
|
||||||
|
You are reviewing a library's public surface. The generic correctness checklist asks "does this code work?"; you ask **"what did downstream consumers just inherit?"** A library's public API is a versioned contract: every exported symbol, signature, error type, and transitive dependency becomes someone else's problem the moment it ships. Most library pain downstream is not broken logic — it is a silent semver break, a panic escaping an API that promised a `Result`, or a private type welded into a public signature that can now never change.
|
||||||
|
|
||||||
|
## When to load this skill
|
||||||
|
|
||||||
|
The diff touches ANY of: the public API of a lib crate/package — exported symbols, `pub` items, `__init__`/index exports, re-export lists — or its manifest version (Cargo.toml, package.json, pyproject.toml). If the diff is purely private internals with no public-surface or manifest change — unload; this checklist has nothing for you.
|
||||||
|
|
||||||
|
## Marker semantics
|
||||||
|
|
||||||
|
Every checklist item below carries a severity emoji AND a `[convention]` or `[correctness]` marker; both ride in the finding title so downstream tooling can act on them mechanically. `[convention]` findings are rigor-foldable (the orchestrator may lower them under a relaxed quality bar) and rejectable — but ONLY with cited evidence: a repo convention at file:line, or a recorded plan decision. `[correctness]` is reserved for contract breaks; those findings are neither foldable nor rejectable.
|
||||||
|
|
||||||
|
## Linters and mechanized checks
|
||||||
|
|
||||||
|
The review orchestrator runs mechanized checks (API-diff/semver checkers, doc-coverage lints); your CONTEXT may already include their output — do not re-derive it. Spend your prose on what linters cannot reach: whether a behavioral change breaks callers even though signatures held, whether an error type actually tells the caller what to do, whether a new dependency is worth its weight. If the repo plausibly warrants a linter config it lacks (a published library with no API-breakage check or doc lint configured), emit a 🟢 `[convention]` finding naming the gap.
|
||||||
|
|
||||||
|
## The checklist
|
||||||
|
|
||||||
|
Severities below are the production bar. Each item is a context-sensitive question, not an absolute — establish what is actually public and actually published before flagging.
|
||||||
|
|
||||||
|
### 1. 🔴 `[correctness]` Breaking public-API change without a major-version note
|
||||||
|
|
||||||
|
Does the diff remove, rename, or change the signature/behavior of anything exported — or tighten what an input accepts, or change what an error variant means? Compare against the manifest version: a breaking change is a 🔴 `[correctness]` finding unless the diff carries a major-version bump or an explicit note that one is planned for the release. Verify "published" first: symbols added earlier on this same unreleased branch are fair game to change freely, and a 0.x line may follow a different compatibility policy — read the repo's versioning statement before firing. Semver is the contract; this is never foldable and never rejectable.
|
||||||
|
|
||||||
|
### 2. 🟡 `[convention]` Panic/unwrap reachable from public API on user input
|
||||||
|
|
||||||
|
Trace new public entry points: can caller-supplied input reach a panic — `unwrap`/`expect` on values derived from arguments, unchecked indexing/slicing, unchecked arithmetic, assertions on caller data? A library that panics on bad input takes down the host application; the contract is to return the error type instead. Panics on programmer error (violated documented invariants) or in internal-only paths that input cannot reach are exempt — say so when you rely on that distinction.
|
||||||
|
|
||||||
|
### 3. 🟢 `[convention]` Public items with no doc comments
|
||||||
|
|
||||||
|
Does every new public item — function, type, trait/interface, module, re-export — carry a doc comment saying what it does, what its parameters mean, and what errors/panics it can produce? Match the repo's documentation register: in a library where every existing public item is documented, an undocumented newcomer is a clear finding; cite a documented sibling at file:line.
|
||||||
|
|
||||||
|
### 4. 🟡 `[convention]` Error types erasing caller-actionable information
|
||||||
|
|
||||||
|
Read the error paths crossing the public boundary: does the error type let a caller distinguish the cases they would handle differently — retry vs give up, bad input vs internal failure, which resource was missing? Stringly-typed errors, a single opaque variant swallowing distinct causes, and lossy conversions that drop the source error are the finding. The caller cannot match on a message string; they need variants, codes, or a source chain.
|
||||||
|
|
||||||
|
### 5. 🟢 `[convention]` Heavyweight or unpinned new required dependency
|
||||||
|
|
||||||
|
Does the diff add a required dependency? For a library, every required dependency lands in every consumer's tree: is it proportionate to what it is used for (a large framework pulled in for one helper is the finding), is its version constraint sane per the ecosystem's norm (a wildcard or unbounded range is the finding), and could it be optional/feature-gated instead? Dev/test-only dependencies are exempt. Whether the dependency is *trustworthy* (typosquats, abandonment, supply-chain risk) is `security-review`'s question.
|
||||||
|
|
||||||
|
### 6. 🟢 `[convention]` Internal types leaking through the public surface
|
||||||
|
|
||||||
|
Do new public signatures expose types that were meant to stay internal — a private module's struct now returned publicly, a third-party type welded into a public signature (locking the dependency into the public contract), or an implementation detail that consumers will now depend on? Once shipped, these can only be removed by a major version. Look for the repo's existing pattern (newtype wrappers, re-export boundaries, facade modules) and cite it when flagging.
|
||||||
|
|
||||||
|
## Ground-truth discipline
|
||||||
|
|
||||||
|
- Establish the actual public surface first: `fs_grep` the export list / re-exports / visibility modifiers — an item can be `pub` yet unreachable from outside, or private yet re-exported.
|
||||||
|
- READ the manifest for the current version and any versioning policy notes before calling anything a semver break.
|
||||||
|
- Check a documented, well-shaped sibling API for the house style (doc register, error-type shape, newtype boundaries) — deviation from a cited sibling is the strongest form of evidence.
|
||||||
|
- Do not flag behavior-preserving refactors of private internals; the contract is the public surface.
|
||||||
|
|
||||||
|
## What this skill does NOT check
|
||||||
|
|
||||||
|
- Whether inputs are exploitable or a new dependency is malicious/compromised → `security-review` (this skill only weighs dependency size and pinning).
|
||||||
|
- Whether stateful helpers the library exposes are idempotent, atomic, or retry-safe → `transactional-integrity`.
|
||||||
|
- Log lines a library emits and their conventions → `logging-discipline`.
|
||||||
|
- Metrics/alerts for the library's operational behavior → `observability-review`.
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
description: Review database schema migrations - expand/contract compatibility with currently-running code, reversibility, online/concurrent index creation, backfills mixed into DDL transactions, and down-migrations. Load when a diff touches migration directories/files, schema definitions, or ORM model changes. Findings fold into the standard code-review severity taxonomy. Grants read-only filesystem access for tracing migrations, schema definitions, and the code that reads the affected tables.
|
||||||
|
enabled_tools: fs_read, fs_grep, fs_glob, fs_cat, fs_ls
|
||||||
|
---
|
||||||
|
You are reviewing a schema migration. The generic correctness checklist asks "does this migration apply?"; you ask **"what happens in the window when this schema and the currently-running code coexist — and what happens if we have to go back?"** A migration does not run against an idle system: for the minutes (or hours) of a rolling deploy, old code runs against the new schema, and a rollback runs old code against it indefinitely. Most migration incidents are not failed DDL — they are a column dropped while running code still reads it, a lock held on a hot table during business hours, or a bad deploy with no way back.
|
||||||
|
|
||||||
|
## When to load this skill
|
||||||
|
|
||||||
|
The diff touches ANY of: migration directories or files, schema definition files, or ORM model changes that generate schema changes. If the diff is query/application logic against an unchanged schema — unload; this checklist has nothing for you.
|
||||||
|
|
||||||
|
## Marker semantics
|
||||||
|
|
||||||
|
Every checklist item below carries a severity emoji AND a `[convention]` or `[correctness]` marker; both ride in the finding title so downstream tooling can act on them mechanically. `[convention]` findings are rigor-foldable (the orchestrator may lower them under a relaxed quality bar) and rejectable — but ONLY with cited evidence: a repo convention at file:line, or a recorded plan decision. `[correctness]` is reserved for contract breaks; those findings are neither foldable nor rejectable.
|
||||||
|
|
||||||
|
## Linters and mechanized checks
|
||||||
|
|
||||||
|
The review orchestrator runs mechanized checks (migration linters, schema-diff tools, lock-analysis checkers); your CONTEXT may already include their output — do not re-derive it. Spend your prose on what those tools cannot reach: whether the currently-running code still depends on what this migration removes, whether irreversibility was a decision or an accident, whether a table is big enough for lock duration to matter. If the repo plausibly warrants a linter config it lacks (a migration directory but no migration linter or schema-diff check configured), emit a 🟢 `[convention]` finding naming the gap.
|
||||||
|
|
||||||
|
## The checklist
|
||||||
|
|
||||||
|
Severities below are the production bar. Each item is a context-sensitive question, not an absolute — read the code that touches the affected tables and the repo's deploy story before flagging, and state any exemption you rely on.
|
||||||
|
|
||||||
|
### 1. 🔴 `[correctness]` Expand/contract violation against currently-running code
|
||||||
|
|
||||||
|
Does this migration remove or rename a column/table, tighten a constraint, or change a type that the CURRENTLY-DEPLOYED code still reads or writes? During a rolling deploy — and after any rollback — that code runs against this schema, and the violation is an outage, not a style issue. The safe sequence is expand/contract: additive schema change first, code migrated in a separate deploy, contraction only after no running code references the old shape. `fs_grep` the codebase for references to everything this migration drops or renames; a rename must land as add-new/backfill/drop-old across deploys, not as a single in-place rename. This is a contract break with the running system: never foldable, never rejectable. The exemption is genuine confirmation that nothing running references the old shape — a column already unreferenced for several releases, or a pre-first-deploy table; cite the evidence when you rely on it.
|
||||||
|
|
||||||
|
### 2. 🟡 `[convention]` Irreversible migration without an explicit stated reason
|
||||||
|
|
||||||
|
Does the migration destroy information — dropping a column with data, lossy type narrowing, collapsing values — such that no down-migration could restore it? Irreversible is sometimes the right call, but it must be a *stated* decision: a comment in the migration or an equivalent recorded note saying what is lost and why that is acceptable. Silent irreversibility is the finding; the reviewer after an incident should not have to discover it from the diff.
|
||||||
|
|
||||||
|
### 3. 🟡 `[convention]` Index creation without concurrent/online mode on large tables
|
||||||
|
|
||||||
|
Does the migration create an index on a table that is large or hot in production? Default index builds take locks that block writes for the duration of the build — on a big table that is a self-inflicted outage. Look for the engine's online path (concurrent/online index creation, e.g. `CREATE INDEX CONCURRENTLY` in Postgres, `ALGORITHM=INPLACE` in MySQL) and note that concurrent builds often cannot run inside a transaction — the migration tool may need its transaction wrapper disabled for that step. A genuinely small, cold, or brand-new table is exempt — say which and why.
|
||||||
|
|
||||||
|
### 4. 🟡 `[convention]` Data backfill in the same transaction as DDL
|
||||||
|
|
||||||
|
Does the migration mix a data backfill (UPDATE/INSERT over existing rows) into the same transaction as schema changes? A backfill over a large table holds the DDL's locks for the whole rewrite, blocks concurrent writes, and can bloat/timeout the transaction. The safe shape is: schema change in the migration, backfill as a separate batched step (separate migration, background job, or chunked script). A backfill over a provably tiny table can be exempt — state the size reasoning. How the backfill itself behaves under interruption and rerun is `transactional-integrity`'s question.
|
||||||
|
|
||||||
|
### 5. 🟢 `[convention]` Missing down-migration where the tool supports it
|
||||||
|
|
||||||
|
Does the migration tool in this repo support down/rollback scripts, and do sibling migrations provide them? Then a new migration without one is the finding — the first schema rollback should not be authored during the incident that needs it. Where the down-path is genuinely impossible (see item 2), the down script should say so explicitly rather than be omitted. Repos whose tooling or stated convention is forward-only are exempt; cite the convention.
|
||||||
|
|
||||||
|
## Ground-truth discipline
|
||||||
|
|
||||||
|
- `fs_grep` the application code for every column, table, and constraint this migration touches — the expand/contract question is answered by the code, not by the migration file.
|
||||||
|
- READ sibling migrations for the house idioms (down-scripts, concurrent-index flags, backfill separation, naming) and cite a sibling at file:line when flagging deviation.
|
||||||
|
- Check the migration tool's config for transaction-wrapping behavior before reasoning about what runs atomically — tools differ, and per-migration overrides matter.
|
||||||
|
- Reason about table size honestly: if you cannot tell whether a table is large, say so and frame the finding conditionally rather than asserting an outage.
|
||||||
|
|
||||||
|
## What this skill does NOT check
|
||||||
|
|
||||||
|
- Whether backfill or migration-adjacent application code is idempotent, atomic, and safe under rerun/crash → `transactional-integrity`.
|
||||||
|
- Whether schema changes expose sensitive data or weaken access controls in exploitable ways → `security-review`.
|
||||||
|
- Log output of migration runs and its conventions → `logging-discipline`.
|
||||||
|
- Metrics/alerts for migration execution and post-migration health → `observability-review`.
|
||||||
@@ -31,9 +31,12 @@ Walk EVERY category. For each, ask: "when the implementer hits this, does the pl
|
|||||||
| 8 | **Config, secrets & environments** | New env vars/config keys — where are they declared and injected? Secrets — vault/parameter store conventions? Staging vs production differences that affect implementation? |
|
| 8 | **Config, secrets & environments** | New env vars/config keys — where are they declared and injected? Secrets — vault/parameter store conventions? Staging vs production differences that affect implementation? |
|
||||||
| 9 | **Scope boundaries** | Is Out of scope present and specific? Are "tempting adjacent fixes" explicitly deferred? |
|
| 9 | **Scope boundaries** | Is Out of scope present and specific? Are "tempting adjacent fixes" explicitly deferred? |
|
||||||
| 10 | **Settled decisions** | Are choices that were debated recorded WITH their one-line reason ("RDS over in-cluster Postgres because ops owns backups")? An unrecorded decision WILL be re-litigated by the implementer. |
|
| 10 | **Settled decisions** | Are choices that were debated recorded WITH their one-line reason ("RDS over in-cluster Postgres because ops owns backups")? An unrecorded decision WILL be re-litigated by the implementer. |
|
||||||
|
| 11 | **Quality bar** | Are `rigor` and `surfaces` declared (or the plan explicitly generic)? Is every dropped best practice recorded WITH a one-line reason? Does every `other:<label>` surface carry non-empty long-tail criteria? |
|
||||||
|
|
||||||
Not every category applies to every plan (a docs-only plan has no data layer). Mark inapplicable categories as such — silently skipping one is how leaks survive.
|
Not every category applies to every plan (a docs-only plan has no data layer). Mark inapplicable categories as such — silently skipping one is how leaks survive.
|
||||||
|
|
||||||
|
For category 11 specifically: a missing Quality bar declaration is FRICTION when the plan is plausibly `surfaces: []` (a docs-only or pure-refactor plan), BLOCKING when the plan self-evidently builds an enum surface (an HTTP API, a CLI, migrations…) but declares none.
|
||||||
|
|
||||||
## Pointer verification (do not trust, verify)
|
## Pointer verification (do not trust, verify)
|
||||||
|
|
||||||
For every pointer the plan offers:
|
For every pointer the plan offers:
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
---
|
||||||
|
description: Review the API surface contract of a change - REST/HTTP routes and handlers, request/response types, OpenAPI specs, plus gRPC services and GraphQL schemas/resolvers when the diff is that flavor. Checks pagination, recorded auth decisions, HTTP method semantics, error-shape consistency, boundary validation, and versioned contract evolution. Load when a diff touches HTTP route/handler definitions, request/response types, OpenAPI specs, proto files, or GraphQL schemas/resolvers. Findings fold into the standard code-review severity taxonomy. Grants read-only filesystem access for tracing routes, types, and published contracts.
|
||||||
|
enabled_tools: fs_read, fs_grep, fs_glob, fs_cat, fs_ls
|
||||||
|
---
|
||||||
|
You are reviewing an API surface. The generic correctness checklist asks "does this handler work?"; you ask **"does this endpoint honor the contract its callers already depend on — and did anyone record the decisions callers will need?"** An API is a promise: clients you cannot see build against the shapes, semantics, and error formats you ship. Most API pain in production is not broken logic — it is a silently changed shape, an unbounded list that grew, or an error format that differs from every sibling endpoint.
|
||||||
|
|
||||||
|
## When to load this skill
|
||||||
|
|
||||||
|
The diff touches ANY of: HTTP route or handler definitions, request/response types, OpenAPI/Swagger specs, gRPC `.proto` files or service implementations, GraphQL schemas or resolvers. If the diff is internal logic behind an unchanged API surface — unload; this checklist has nothing for you.
|
||||||
|
|
||||||
|
## Marker semantics
|
||||||
|
|
||||||
|
Every checklist item below carries a severity emoji AND a `[convention]` or `[correctness]` marker; both ride in the finding title so downstream tooling can act on them mechanically. `[convention]` findings are rigor-foldable (the orchestrator may lower them under a relaxed quality bar) and rejectable — but ONLY with cited evidence: a repo convention at file:line, or a recorded plan decision. `[correctness]` is reserved for contract breaks; those findings are neither foldable nor rejectable.
|
||||||
|
|
||||||
|
## Linters and mechanized checks
|
||||||
|
|
||||||
|
The review orchestrator runs mechanized checks (spec linters, schema diff tools, breaking-change detectors); your CONTEXT may already include their output — do not re-derive it. Spend your prose on what linters cannot reach: whether pagination is warranted, whether the auth decision was recorded, whether an evolution is actually compatible for real callers. If the repo plausibly warrants a linter config it lacks (an OpenAPI spec but no spec linter, protos but no breaking-change check), emit a 🟢 `[convention]` finding naming the gap.
|
||||||
|
|
||||||
|
## The checklist (REST/HTTP)
|
||||||
|
|
||||||
|
Severities below are the production bar. Each item is a context-sensitive question, not an absolute — read the surrounding code and the callers before flagging.
|
||||||
|
|
||||||
|
### 1. 🟡 `[convention]` Unbounded collection endpoint without pagination
|
||||||
|
|
||||||
|
Does the diff add or modify an endpoint that returns a collection? If the collection can grow without bound (rows in a table, user-generated items), missing pagination is a finding: name the endpoint, the backing query, and the growth vector. A provably bounded small set is exempt — an enum-backed list, a fixed config table, a per-user set with a hard cap — and when you rely on that exemption, say so explicitly in your notes so the next reviewer sees it was considered, not missed.
|
||||||
|
|
||||||
|
### 2. 🟡 `[convention]` Route without a recorded authn/z decision
|
||||||
|
|
||||||
|
For every new or changed route: is there a recorded decision that this route is public, authenticated, or role-gated? "Recorded" means visible in code or spec — middleware attached, an annotation, a spec `security` block, or an explicit comment for deliberately public routes. A route with no discernible decision is the finding. Whether the auth implementation is *bypassable* is not your question — that belongs to `security-review`; you only verify the decision exists and is stated.
|
||||||
|
|
||||||
|
### 3. 🟡 `[convention]` Non-idempotent PUT/DELETE semantics
|
||||||
|
|
||||||
|
PUT and DELETE carry idempotency promises by HTTP contract: repeating a PUT must converge on the same state; repeating a DELETE must not error in a way that breaks retrying clients (a second DELETE returning 404 or 204 is fine; returning 500 is not). Does the diff's handler honor the method it is mounted on — or should it be a POST? You check the *declared method semantics*; whether the state change is mechanically idempotent under retries and concurrency belongs to `transactional-integrity`.
|
||||||
|
|
||||||
|
### 4. 🟡/🟢 `[convention]` Error responses leaking internals or inconsistent error shape
|
||||||
|
|
||||||
|
Read the error paths: do responses leak internals — stack traces, SQL fragments, internal hostnames, framework default error pages (🟡)? Do they match the error shape the repo's sibling endpoints already return — same envelope, same code/message fields (🟢 when merely inconsistent)? `fs_grep` a sibling handler's error response to establish the house shape before flagging. Whether a leak is *exploitable* is `security-review`'s call; yours is the contract and consistency question.
|
||||||
|
|
||||||
|
### 5. 🔴 `[correctness]` Breaking a published request/response shape without versioning
|
||||||
|
|
||||||
|
Does the diff remove or rename a field, change a type, tighten accepted input, or change status codes on an endpoint that is already published (in a released spec, consumed by known clients, or exposed beyond this repo)? That is a contract break — a 🔴 `[correctness]` finding unless the change ships behind a new version (path version, header version, or an additive evolution that old clients tolerate). Verify "published" before firing: an endpoint added earlier in this same unreleased branch is not published, and changing it freely is fine.
|
||||||
|
|
||||||
|
### 6. 🟡 `[convention]` Missing boundary input validation
|
||||||
|
|
||||||
|
At the request boundary, is input validated at all — types enforced, required fields checked, sizes/ranges bounded — before it flows inward? Absence of any validation on a new input path is the finding. Whether unvalidated input is *exploitable* (injection, traversal) is deferred to `security-review`; you flag the missing guardrail, not the attack.
|
||||||
|
|
||||||
|
## gRPC section (apply when the diff touches protos or gRPC services)
|
||||||
|
|
||||||
|
- 🟡 `[convention]` **Deadlines** — do new client calls set deadlines, and do servers propagate the caller's deadline to their own outbound calls? A call chain with no deadline anywhere hangs forever on a stuck dependency.
|
||||||
|
- 🟡 `[convention]` **Status-code discipline** — do handlers return meaningful gRPC status codes (`NOT_FOUND`, `INVALID_ARGUMENT`, `ALREADY_EXISTS`) rather than collapsing every failure into `UNKNOWN`/`INTERNAL`? Callers branch on these codes; a flattened code space breaks their error handling.
|
||||||
|
- 🔴 `[correctness]` **Backwards-compatible proto evolution** — on a published proto: no field-number reuse, no type changes on existing fields, no renumbering, new fields optional with fresh numbers. Violating any of these silently corrupts data for old clients — same contract-break bar as item 5.
|
||||||
|
- 🟢 `[convention]` **Field deprecation** — removed fields should be `reserved` (number and name) and deprecations marked with the `deprecated` option, not deleted outright, so the number can never be reused.
|
||||||
|
|
||||||
|
## GraphQL section (apply when the diff touches schemas or resolvers)
|
||||||
|
|
||||||
|
- 🟡 `[convention]` **Resolver N+1** — does a new list-field resolver fetch per-item (a query inside a loop, or a per-parent resolver hitting the DB)? Look for a dataloader/batching layer; its absence on a list path is the finding.
|
||||||
|
- 🟡 `[convention]` **Depth/complexity limits** — if the diff grows the schema's reachable graph (new nested relations), is there a depth or complexity limit configured anywhere? An unlimited schema is a self-service DoS invitation; check the server setup before assuming.
|
||||||
|
- 🟡 `[convention]` **Connection-style pagination** — list fields over unbounded collections should use the repo's established pagination idiom (connections/edges or equivalent). The same bounded-set exemption as REST item 1 applies — and state it when you use it. Breaking a published schema field without a deprecation cycle falls under item 5's 🔴 `[correctness]` bar.
|
||||||
|
|
||||||
|
## Ground-truth discipline
|
||||||
|
|
||||||
|
- READ the route registration and middleware chain, not just the handler — auth decisions and pagination defaults often live up-stack.
|
||||||
|
- `fs_grep` for the spec file (OpenAPI, proto, GraphQL schema) that publishes the shape the diff changes; the spec, not the struct, is the contract.
|
||||||
|
- Check sibling endpoints for the house error shape, pagination idiom, and auth annotation style before flagging deviation — the strongest finding cites the sibling at file:line.
|
||||||
|
|
||||||
|
## What this skill does NOT check
|
||||||
|
|
||||||
|
- Whether auth is bypassable, input is exploitable, or errors leak abusable secrets → `security-review`.
|
||||||
|
- Whether state-changing handlers are mechanically idempotent, atomic, or retry-safe → `transactional-integrity`.
|
||||||
|
- Log lines, levels, and message conventions in handlers → `logging-discipline`.
|
||||||
|
- Metrics, alerts, and dashboards for new endpoints → `observability-review`.
|
||||||
@@ -28,6 +28,7 @@ points: 1.0 # engineer-days; ~1.0 per the sizing rule
|
|||||||
plan: PLAN-<slug>.md
|
plan: PLAN-<slug>.md
|
||||||
blocked_by: [] # TASK ids that must be complete first
|
blocked_by: [] # TASK ids that must be complete first
|
||||||
created: YYYY-MM-DD
|
created: YYYY-MM-DD
|
||||||
|
surfaces: [] # this task's surfaces; omitted = inherit the plan's list
|
||||||
---
|
---
|
||||||
|
|
||||||
## What
|
## What
|
||||||
@@ -47,6 +48,8 @@ One paragraph: what this task produces, named concretely (files, symbols, behavi
|
|||||||
|
|
||||||
Status lives in frontmatter — there are no lifecycle directories. `status: complete` plus all boxes checked IS done.
|
Status lives in frontmatter — there are no lifecycle directories. `status: complete` plus all boxes checked IS done.
|
||||||
|
|
||||||
|
`surfaces` come from the plan's `## Quality bar`; rigor is run-level only — there is no per-task rigor.
|
||||||
|
|
||||||
## log.md conventions
|
## log.md conventions
|
||||||
|
|
||||||
Append-only. Each entry is an H2: `## YYYY-MM-DD — <short label>` (`created`, `started`, `implemented`, `diverged`, `completed`, ...). Body is 1-3 sentences of prose; structured data lives in markdown links (branch URLs, commit SHAs, PR links). Never rewrite an old entry — add a new one.
|
Append-only. Each entry is an H2: `## YYYY-MM-DD — <short label>` (`created`, `started`, `implemented`, `diverged`, `completed`, ...). Body is 1-3 sentences of prose; structured data lives in markdown links (branch URLs, commit SHAs, PR links). Never rewrite an old entry — add a new one.
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
description: Review the background-work surface contract of a change - queue/stream consumers, scheduled jobs, and cron handlers. Checks retry/backoff policy, DLQ/poison-message routing, graceful-shutdown drain, concurrency/prefetch bounds, and visibility-timeout vs processing-time reasoning. Load when a diff touches queue/job/cron consumer registration, handler wiring, or schedule definitions. Findings fold into the standard code-review severity taxonomy. Grants read-only filesystem access for tracing consumer registration, transport config, and shutdown paths.
|
||||||
|
enabled_tools: fs_read, fs_grep, fs_glob, fs_cat, fs_ls
|
||||||
|
---
|
||||||
|
You are reviewing background-work wiring. The generic correctness checklist asks "does this handler process a message?"; you ask **"what happens to this consumer on the bad days — a poison message, a deploy mid-batch, a downstream outage?"** A worker's contract is with the queue, the scheduler, and the deploy pipeline, not with a single happy-path message. Most worker incidents are not broken handler logic — they are a queue wedged behind one malformed message, in-flight work silently dropped by a rolling restart, or a retry storm hammering a struggling dependency.
|
||||||
|
|
||||||
|
## When to load this skill
|
||||||
|
|
||||||
|
The diff touches ANY of: 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). If the diff is handler business logic behind unchanged wiring — unload; this checklist has nothing for you. Note: a diff that wires a consumer AND changes state is two reviews — this skill covers the consumer contract, and `transactional-integrity` covers the state changes; state-changing worker diffs load BOTH.
|
||||||
|
|
||||||
|
## Marker semantics
|
||||||
|
|
||||||
|
Every checklist item below carries a severity emoji AND a `[convention]` or `[correctness]` marker; both ride in the finding title so downstream tooling can act on them mechanically. `[convention]` findings are rigor-foldable (the orchestrator may lower them under a relaxed quality bar) and rejectable — but ONLY with cited evidence: a repo convention at file:line, or a recorded plan decision. `[correctness]` is reserved for contract breaks; those findings are neither foldable nor rejectable.
|
||||||
|
|
||||||
|
## Linters and mechanized checks
|
||||||
|
|
||||||
|
The review orchestrator runs mechanized checks (config validators, schema checks for queue/schedule definitions); your CONTEXT may already include their output — do not re-derive it. Spend your prose on what linters cannot reach: whether a retry policy exists at all, where a poison message goes, what a deploy does to in-flight work. If the repo plausibly warrants a linter config it lacks, emit a 🟢 `[convention]` finding naming the gap.
|
||||||
|
|
||||||
|
## The checklist
|
||||||
|
|
||||||
|
Severities below are the production bar. Each item is a context-sensitive question, not an absolute — read the transport's own guarantees and the deployment story before flagging, and when you rely on an exemption, state it so the next reviewer sees it was considered.
|
||||||
|
|
||||||
|
### 1. 🟡 `[convention]` No retry/backoff policy where the transport provides none
|
||||||
|
|
||||||
|
For each new or rewired consumer: when the handler fails, who retries, how many times, with what backoff? Some transports provide redelivery with backoff out of the box — READ the transport config before claiming they do here. If neither the transport nor the code establishes a policy, a transient downstream blip becomes permanent message loss (or an immediate hot-loop of retries). Name the consumer, the transport, and what a single failure currently does. A consumer whose transport is configured with sane redelivery is exempt — cite the config at file:line.
|
||||||
|
|
||||||
|
### 2. 🟡 `[convention]` No DLQ/poison-message route — one bad message wedges the queue
|
||||||
|
|
||||||
|
A message that fails every retry must go SOMEWHERE terminal: a dead-letter queue, a parked table, a quarantine topic. Trace the exhausted-retries path for each consumer the diff adds: if the message returns to the head of the queue forever, one malformed payload halts all processing behind it. Ordered/single-partition consumers are the highest-blast-radius case. Where the message *lands* is your question; whether the handler's error path also acknowledges correctly under at-least-once delivery is `transactional-integrity`'s.
|
||||||
|
|
||||||
|
### 3. 🟡 `[convention]` No graceful-shutdown drain — in-flight work lost on deploy
|
||||||
|
|
||||||
|
Every deploy sends this worker a termination signal mid-message. Does the diff's worker stop taking new work, finish (or cleanly nack) what is in flight, and exit within the platform's grace period? Look for a shutdown hook, drain loop, or the framework's built-in drain — and check the termination grace configured for the deployment. Exemptions are real: a stateless cron job reading a read-only source loses nothing on interruption, and an at-least-once transport redelivers whatever was in flight (making drain an efficiency concern, not a loss) — say explicitly which exemption you are relying on. The item bites hardest for at-least-once consumers with long-running in-flight work and for anything ack-early.
|
||||||
|
|
||||||
|
### 4. 🟢 `[convention]` Unbounded concurrency/prefetch
|
||||||
|
|
||||||
|
Does the new consumer bound how many messages it processes at once — worker-pool size, prefetch/fetch-count, max in-flight? An unbounded consumer amplifies a queue backlog into a self-inflicted outage: memory blowup, connection-pool exhaustion, a thundering herd against the downstream the handler calls. Check the transport defaults before flagging — some default to a sane prefetch; some default to unlimited. A low-volume schedule-driven job with structurally bounded input is exempt.
|
||||||
|
|
||||||
|
### 5. 🟢 `[convention]` Visibility-timeout vs processing-time reasoning absent
|
||||||
|
|
||||||
|
Where the transport uses a visibility timeout, lease, or lock (with redelivery when it expires): is there any evidence — a comment, a config value derived from measurements, a heartbeat/extension call — that the timeout exceeds the handler's realistic worst-case processing time? A timeout shorter than processing time means the message redelivers WHILE the first attempt is still running: duplicate concurrent processing by design. You flag the absent reasoning; whether the handler survives that concurrent duplicate is `transactional-integrity`'s question. Transports with no visibility/lease mechanism are exempt.
|
||||||
|
|
||||||
|
## Ground-truth discipline
|
||||||
|
|
||||||
|
- READ the transport/framework configuration, not just the handler — retry counts, DLQ wiring, prefetch, and drain behavior live in config and registration code, not in the handler body.
|
||||||
|
- `fs_grep` sibling consumers for the established idioms (DLQ naming, shutdown hooks, backoff helpers) — a new consumer skipping the house pattern is the strongest form of evidence; cite the sibling at file:line.
|
||||||
|
- Check the deployment manifests for termination grace periods when reasoning about drain — the code's drain loop is only as good as the time the platform gives it.
|
||||||
|
- Do not flag a missing guard the transport demonstrably provides; cite the config that provides it instead.
|
||||||
|
|
||||||
|
## What this skill does NOT check
|
||||||
|
|
||||||
|
- Whether the handler is idempotent under redelivery, atomic across writes, or safe against dual-writes to a DB plus an external system → `transactional-integrity` (state-changing worker diffs load both skills).
|
||||||
|
- Whether job payloads, queue names, or worker logs expose secrets or abusable data → `security-review` and `logging-discipline`.
|
||||||
|
- Metrics, alerts, and dashboards for the new worker (queue depth, processing lag, failure rate) → `observability-review`.
|
||||||
|
- Log lines, levels, and message conventions inside the handler → `logging-discipline`.
|
||||||
+183
-197
@@ -1,39 +1,40 @@
|
|||||||
# ---- LLM ----
|
# ---- LLM ----
|
||||||
model: openai:gpt-4o # Specify the LLM to use
|
model: openai:gpt-4o # Specify the LLM to use
|
||||||
temperature: null # Set default temperature parameter (0, 1)
|
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
|
top_p: null # Set default top-p parameter, with a range of (0, 1) or (0, 2) depending on the model
|
||||||
reasoning_effort:
|
reasoning_effort: null # Reasoning effort level for models that support it (e.g. low, medium, high).
|
||||||
null # Reasoning effort level for models that support it (e.g. low, medium, high).
|
# Only valid when the active model declares reasoning_levels. See the Clients docs.
|
||||||
# Only valid when the active model declares reasoning_levels. See the Clients docs.
|
|
||||||
|
|
||||||
# ---- Behavior ----
|
# ---- Behavior ----
|
||||||
stream: true # Controls whether to use the stream-style APIs when querying for completions from LLM clients.
|
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
|
save: true # Indicates whether to persist the conversation to messages.md for posterity
|
||||||
keybindings: emacs # Choose keybinding style (emacs, vi)
|
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
|
editor: null # Specifies the editor used to edit the input buffer or session. (e.g. vim, emacs, nano, hx). Defaults to $EDITOR
|
||||||
wrap: no # Controls text wrapping (no, auto, <max-width>)
|
wrap: no # Controls text wrapping (no, auto, <max-width>)
|
||||||
wrap_code: false # Enables or disables the wrapping of code blocks
|
wrap_code: false # Enables or disables the wrapping of code blocks
|
||||||
|
|
||||||
# ---- Prelude ----
|
# ---- Prelude ----
|
||||||
repl_prelude: null # Set a default session or role for REPL mode to use (e.g. role:<name>, session:<name>, <session>:<role>)
|
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>)
|
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)
|
agent_session: null # Set a session to use when starting an agent (e.g. temp, default)
|
||||||
|
|
||||||
# ---- Appearance ----
|
# ---- Appearance ----
|
||||||
highlight: true # Controls syntax highlighting
|
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
|
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
|
||||||
light_theme: false # Activates a light color theme when true. env: COYOTE_LIGHT_THEME
|
light_theme: false # Activates a light color theme when true. env: COYOTE_LIGHT_THEME
|
||||||
|
|
||||||
# ---- Miscellaneous ----
|
# ---- Miscellaneous ----
|
||||||
user_agent: null # Set User-Agent HTTP header, use `auto` for coyote/<current-version>
|
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
|
save_shell_history: true # Whether to save shell execution command to the history file
|
||||||
sync_models_url: > # URL to sync model changes from
|
sync_models_url: > # URL to sync model changes from
|
||||||
https://raw.githubusercontent.com/Dark-Alex-17/coyote/refs/heads/main/models.yaml
|
https://raw.githubusercontent.com/Dark-Alex-17/coyote/refs/heads/main/models.yaml
|
||||||
|
|
||||||
# ---- REPL Prompt ----
|
# ---- 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
|
# 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: '{color.red}{model}){color.green}{?session {?agent {agent}>}{session}{?role /}}{!session {?agent {agent}>}}{role}{?rag @{rag}}{color.cyan}{?session )}{!session >}{color.reset} '
|
left_prompt:
|
||||||
right_prompt: '{color.cyan}{?reasoning_effort [{reasoning_effort}] }{color.purple}{?session {?consume_tokens {consume_tokens}({consume_percent}%)}{!consume_tokens {consume_tokens}}}{color.reset}'
|
'{color.red}{model}){color.green}{?session {?agent {agent}>}{session}{?role /}}{!session {?agent {agent}>}}{role}{?rag @{rag}}{color.cyan}{?session )}{!session >}{color.reset} '
|
||||||
|
right_prompt:
|
||||||
|
'{color.cyan}{?reasoning_effort [{reasoning_effort}] }{color.purple}{?session {?consume_tokens {consume_tokens}({consume_percent}%)}{!consume_tokens {consume_tokens}}}{color.reset}'
|
||||||
|
|
||||||
# ---- Vault ----
|
# ---- Vault ----
|
||||||
# See the [Vault documentation](https://github.com/Dark-Alex-17/coyote/wiki/Vault) for more information on the Coyote vault.
|
# See the [Vault documentation](https://github.com/Dark-Alex-17/coyote/wiki/Vault) for more information on the Coyote vault.
|
||||||
@@ -41,7 +42,7 @@ right_prompt: '{color.cyan}{?reasoning_effort [{reasoning_effort}] }{color.purpl
|
|||||||
# The secrets_provider tells Coyote where to read and write secrets referenced via {{SECRET_NAME}} syntax.
|
# 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.
|
# Shorthand: set vault_password_file to enable the local provider with that password file.
|
||||||
vault_password_file: null # Path to a file containing the password for the Coyote vault (cannot be a secret template)
|
vault_password_file: null # Path to a file containing the password for the Coyote vault (cannot be a secret template)
|
||||||
#
|
#
|
||||||
# Explicit: set secrets_provider to one of the supported types below. When secrets_provider is set,
|
# 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.
|
# vault_password_file is ignored. Note: secrets_provider itself cannot use {{SECRET}} template syntax.
|
||||||
@@ -81,39 +82,39 @@ vault_password_file: null # Path to a file containing the password for the Coyot
|
|||||||
|
|
||||||
# ---- Function Calling ----
|
# ---- Function Calling ----
|
||||||
# See the [Tools documentation](https://github.com/Dark-Alex-17/coyote/wiki/Tools) for more details
|
# 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).
|
function_calling_support: true # Enables or disables function calling (Globally).
|
||||||
mapping_tools: # Alias for a tool or toolset
|
mapping_tools: # Alias for a tool or toolset
|
||||||
fs: 'fs_cat,fs_ls,fs_mkdir,fs_rm,fs_write,fs_read,fs_glob,fs_grep'
|
fs: 'fs_cat,fs_ls,fs_mkdir,fs_rm,fs_write,fs_read,fs_glob,fs_grep'
|
||||||
enabled_tools:
|
enabled_tools: null # Which tools to enable by default.
|
||||||
null # Which tools to enable by default.
|
# Accepts either a YAML list or a comma-separated string. Use 'all' to enable everything.
|
||||||
# Accepts either a YAML list or a comma-separated string. Use 'all' to enable everything.
|
# Example (list form):
|
||||||
# Example (list form):
|
# enabled_tools:
|
||||||
# enabled_tools:
|
# - fs
|
||||||
# - fs
|
# - web_search_coyote
|
||||||
# - web_search_coyote
|
# Example (comma-separated form):
|
||||||
# Example (comma-separated form):
|
# enabled_tools: fs,web_search_coyote
|
||||||
# enabled_tools: fs,web_search_coyote
|
visible_tools: # Which tools are visible to be compiled (and are thus able to be defined in 'enabled_tools').
|
||||||
visible_tools: # 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.
|
||||||
# - ast_grep.sh
|
# - ast_grep.sh
|
||||||
# - demo_py.py
|
# - demo_py.py
|
||||||
# - demo_sh.sh
|
# - demo_sh.sh
|
||||||
# - demo_ts.ts
|
# - demo_ts.ts
|
||||||
- execute_command.sh
|
- execute_command.sh
|
||||||
# - execute_py_code.py
|
# - execute_py_code.py
|
||||||
# - execute_sql_code.sh
|
# - execute_sql_code.sh
|
||||||
# - fetch_url_via_curl.sh
|
# - fetch_url_via_curl.sh
|
||||||
# - fetch_url_via_jina.sh
|
# - fetch_url_via_jina.sh
|
||||||
- fs_cat.sh
|
- fs_cat.sh
|
||||||
- fs_ls.sh
|
- fs_ls.sh
|
||||||
# - fs_read.sh
|
# - fs_read.sh
|
||||||
# - fs_glob.sh
|
# - fs_glob.sh
|
||||||
# - fs_grep.sh
|
# - fs_grep.sh
|
||||||
# - fs_mkdir.sh
|
# - fs_mkdir.sh
|
||||||
# - fs_patch.sh
|
# - fs_patch.sh
|
||||||
# - fs_write.sh
|
# - fs_write.sh
|
||||||
- get_current_time.sh
|
- get_current_time.sh
|
||||||
# - get_current_weather.py
|
# - get_current_weather.py
|
||||||
# - get_current_weather.ts
|
# - get_current_weather.ts
|
||||||
- get_current_weather.sh
|
- get_current_weather.sh
|
||||||
# - search_arxiv.sh
|
# - search_arxiv.sh
|
||||||
# - search_wikipedia.sh
|
# - search_wikipedia.sh
|
||||||
@@ -126,106 +127,97 @@ visible_tools: # Which tools are visible to be compiled (and are thus able to be
|
|||||||
|
|
||||||
# ---- MCP Servers ----
|
# ---- MCP Servers ----
|
||||||
# See the [MCP Servers documentation](https://github.com/Dark-Alex-17/coyote/wiki/MCP-Servers) for more details
|
# 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).
|
mcp_server_support: true # Enables or disables MCP servers (globally).
|
||||||
mapping_mcp_servers: # Alias for an MCP server or set of servers
|
mapping_mcp_servers: # Alias for an MCP server or set of servers
|
||||||
git: github,gitmcp
|
git: github,gitmcp
|
||||||
enabled_mcp_servers:
|
enabled_mcp_servers: null # Which MCP servers to enable by default.
|
||||||
null # Which MCP servers to enable by default.
|
# Accepts either a YAML list or a comma-separated string. Use 'all' to enable everything.
|
||||||
# Accepts either a YAML list or a comma-separated string. Use 'all' to enable everything.
|
# Example (list form):
|
||||||
# Example (list form):
|
# enabled_mcp_servers:
|
||||||
# enabled_mcp_servers:
|
# - github
|
||||||
# - github
|
# - slack
|
||||||
# - slack
|
# Example (comma-separated form):
|
||||||
# Example (comma-separated form):
|
# enabled_mcp_servers: github,slack,ddg-search
|
||||||
# enabled_mcp_servers: github,slack,ddg-search
|
mcp_tools: null # Per-server MCP tool allowlists (glob patterns: * and ? supported).
|
||||||
mcp_tools:
|
# Tools that match no pattern are hidden from the model as if they
|
||||||
null # Per-server MCP tool allowlists (glob patterns: * and ? supported).
|
# don't exist. Stacks with the other allowlist layers (mcp.json
|
||||||
# Tools that match no pattern are hidden from the model as if they
|
# `allowedTools`, role, agent, session, skill, graph node). Every
|
||||||
# don't exist. Stacks with the other allowlist layers (mcp.json
|
# configured layer must allow a tool, so layers only ever narrow.
|
||||||
# `allowedTools`, role, agent, session, skill, graph node). Every
|
# An empty list blocks all of a server's tools.
|
||||||
# configured layer must allow a tool, so layers only ever narrow.
|
# Example:
|
||||||
# An empty list blocks all of a server's tools.
|
# mcp_tools:
|
||||||
# Example:
|
# github:
|
||||||
# mcp_tools:
|
# - get_*
|
||||||
# github:
|
# - list_*
|
||||||
# - get_*
|
# slack: []
|
||||||
# - list_*
|
no_workspace_mcp: false # Disable loading workspace-local MCP servers (default: false).
|
||||||
# slack: []
|
# When false (the default), Coyote merges the first workspace MCP config it finds
|
||||||
no_workspace_mcp:
|
# into the global MCP registry at startup, checking in order:
|
||||||
false # Disable loading workspace-local MCP servers (default: false).
|
# 1. .coyote/mcp.json
|
||||||
# When false (the default), Coyote merges the first workspace MCP config it finds
|
# 2. .coyote/.mcp.json (Claude-style file name)
|
||||||
# into the global MCP registry at startup, checking in order:
|
# 3. .mcp.json (project root; Claude Code convention)
|
||||||
# 1. .coyote/mcp.json
|
# Workspace entries shadow global ones on name collision.
|
||||||
# 2. .coyote/.mcp.json (Claude-style file name)
|
# Set to true (or pass --no-workspace-mcp) to skip this entirely.
|
||||||
# 3. .mcp.json (project root; Claude Code convention)
|
|
||||||
# Workspace entries shadow global ones on name collision.
|
|
||||||
# Set to true (or pass --no-workspace-mcp) to skip this entirely.
|
|
||||||
|
|
||||||
# ---- Skills ----
|
# ---- Skills ----
|
||||||
# Skills are modular knowledge or capability packs the LLM can load and unload mid-conversation.
|
# 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.
|
# See the [Skills documentation](https://github.com/Dark-Alex-17/coyote/wiki/Skills) for more details.
|
||||||
skills_enabled:
|
skills_enabled: true # Master switch. Set to false to hide all skill management tools from the model.
|
||||||
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.
|
||||||
# Skills also require `function_calling_support: true` above to work at all.
|
visible_skills: # The universe of skills allowed to be enabled in any context. Omit (null) for "all installed".
|
||||||
visible_skills: # The universe of skills allowed to be enabled in any context. Omit (null) for "all installed".
|
|
||||||
- ai-slop-remover
|
- ai-slop-remover
|
||||||
- code-review
|
- code-review
|
||||||
- frontend-ui-ux
|
- frontend-ui-ux
|
||||||
- git-master
|
- git-master
|
||||||
enabled_skills:
|
enabled_skills: null # Which skills are available by default (no role/agent/session active). null = all visible.
|
||||||
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.
|
||||||
# Accepts either a YAML list or a comma-separated string.
|
# Example (list form):
|
||||||
# Example (list form):
|
# enabled_skills:
|
||||||
# enabled_skills:
|
# - git-master
|
||||||
# - git-master
|
# - ai-slop-remover
|
||||||
# - ai-slop-remover
|
# Example (comma-separated form):
|
||||||
# Example (comma-separated form):
|
# enabled_skills: git-master,ai-slop-remover
|
||||||
# enabled_skills: git-master,ai-slop-remover
|
inject_skill_instructions: true # Inject a short hint pointing the model at `skill__list` when skills are enabled in
|
||||||
inject_skill_instructions:
|
# this context. Only injected if `function_calling_support`, `skills_enabled`, and the
|
||||||
true # Inject a short hint pointing the model at `skill__list` when skills are enabled in
|
# effective enabled skill set is non-empty (default: true).
|
||||||
# this context. Only injected if `function_calling_support`, `skills_enabled`, and the
|
skill_instructions: null # Custom text used for the skill hint when injected. If null, uses built-in default.
|
||||||
# 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.
|
|
||||||
|
|
||||||
# ---- Macros ----
|
# ---- Macros ----
|
||||||
# Macros are Coyote's custom commands: named sequences of REPL commands and prompts, invoked directly by name
|
# 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).
|
# (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).
|
# 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.
|
# See the [Macros documentation](https://github.com/Dark-Alex-17/coyote/wiki/Macros) for more details.
|
||||||
enabled_macros:
|
enabled_macros: null # Which macros are invocable by default (no role/agent/session active). null = all visible.
|
||||||
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
|
||||||
# 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
|
||||||
# comma-separated string. Roles, agents, and sessions may define their own
|
# `enabled_macros`; the most specific active one wins (session > agent > role > global).
|
||||||
# `enabled_macros`; the most specific active one wins (session > agent > role > global).
|
# Example (list form):
|
||||||
# Example (list form):
|
# enabled_macros:
|
||||||
# enabled_macros:
|
# - generate-commit-message
|
||||||
# - generate-commit-message
|
# Example (comma-separated form):
|
||||||
# Example (comma-separated form):
|
# enabled_macros: generate-commit-message,review
|
||||||
# enabled_macros: generate-commit-message,review
|
|
||||||
|
|
||||||
# ---- Auto-Continue (Todo System) ----
|
# ---- Auto-Continue (Todo System) ----
|
||||||
# The auto-continue system provides built-in task tracking for improved reliability.
|
# 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
|
# When enabled, the model can create todo lists and the system will automatically
|
||||||
# prompt it to continue when incomplete tasks remain.
|
# 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
|
# 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)
|
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)
|
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)
|
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
|
continuation_prompt: null # Custom prompt used when auto-continuing. If null, uses built-in default
|
||||||
|
|
||||||
# ---- Session ----
|
# ---- Session ----
|
||||||
# See the [Session documentation](https://github.com/Dark-Alex-17/coyote/wiki/Sessions) for more information
|
# 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 save; if null, ask the user what to do
|
save_session: null # Controls the persistence of the session. If true, auto save; if false, don't auto-save 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_threshold: 4000 # Compress the session when the token count reaches or exceeds this threshold
|
||||||
summarization_prompt:
|
summarization_prompt: > # The text prompt used for creating a concise summary of session message
|
||||||
> # The text prompt used for creating a concise summary of session message
|
|
||||||
'Summarize the discussion briefly in 200 words or less to use as a prompt for future context.'
|
'Summarize the discussion briefly in 200 words or less to use as a prompt for future context.'
|
||||||
summary_context_prompt:
|
summary_context_prompt: > # The text prompt used for including the summary of the entire session as context to the model
|
||||||
> # The text prompt used for including the summary of the entire session as context to the model
|
|
||||||
'This is a summary of the chat history as a recap: '
|
'This is a summary of the chat history as a recap: '
|
||||||
compression_keep_last: 0 # Number of most-recent messages to keep visible after compression (0 = compress all messages)
|
compression_keep_last: 0 # Number of most-recent messages to keep visible after compression (0 = compress all messages)
|
||||||
max_tool_result_chars: null # Cap on tool result characters forwarded to the model per call (null = no cap)
|
max_tool_result_chars: null # Cap on tool result characters forwarded to the model per call (null = no cap)
|
||||||
max_concurrent_jobs: 5 # Max background jobs (`job__*` tools) running at once per context (default: 5; 0 disables background jobs entirely)
|
max_concurrent_jobs: 5 # Max background jobs (`job__*` tools) running at once per context (default: 5; 0 disables background jobs entirely)
|
||||||
|
|
||||||
# ---- Memory ----
|
# ---- Memory ----
|
||||||
# See the [Memory documentation](https://github.com/Dark-Alex-17/coyote/wiki/Memory) for more information.
|
# See the [Memory documentation](https://github.com/Dark-Alex-17/coyote/wiki/Memory) for more information.
|
||||||
@@ -234,13 +226,11 @@ max_concurrent_jobs: 5 # Max background jobs (`job__*` tools) running at once pe
|
|||||||
# even when memory files exist. The cascade is: agent > session > role > app.
|
# even when memory files exist. The cascade is: agent > session > role > app.
|
||||||
# Bootstrap with `coyote --init-memory [global|workspace]` to create the marker file
|
# Bootstrap with `coyote --init-memory [global|workspace]` to create the marker file
|
||||||
# the LLM needs before it will write any memory.
|
# 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: null # null = enabled when memory exists on disk; true = force on; false = force off
|
||||||
memory_cap_with_tools:
|
memory_cap_with_tools: null # Char cap for injected memory when function calling is available (default: 6000).
|
||||||
null # Char cap for injected memory when function calling is available (default: 6000).
|
# Only MEMORY.md indexes are injected; the LLM uses memory__read to fetch drill files.
|
||||||
# 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 (default: 12000).
|
||||||
memory_cap_without_tools:
|
# Indexes plus drill file bodies are injected up to this cap.
|
||||||
null # Char cap when function calling is unavailable (default: 12000).
|
|
||||||
# Indexes plus drill file bodies are injected up to this cap.
|
|
||||||
|
|
||||||
# ---- Workspace Instructions ----
|
# ---- Workspace Instructions ----
|
||||||
# Human-curated project instructions injected read-only into the system prompt, in full.
|
# Human-curated project instructions injected read-only into the system prompt, in full.
|
||||||
@@ -248,23 +238,22 @@ memory_cap_without_tools:
|
|||||||
# chain below (per directory, in order). Scaffold with `coyote --init-instructions`.
|
# chain below (per directory, in order). Scaffold with `coyote --init-instructions`.
|
||||||
# Disable per-invocation with --no-workspace-instructions, or override the chain with
|
# Disable per-invocation with --no-workspace-instructions, or override the chain with
|
||||||
# repeatable --workspace-instructions-file flags.
|
# repeatable --workspace-instructions-file flags.
|
||||||
workspace_instructions: null # null/true = inject when an instructions file exists; false = never inject
|
workspace_instructions: null # null/true = inject when an instructions file exists; false = never inject
|
||||||
workspace_instructions_files:
|
workspace_instructions_files: null # File name chain to search, in priority order.
|
||||||
null # File name chain to search, in priority order.
|
# Default: [COYOTE.md, AGENTS.md, CLAUDE.md, GEMINI.md]
|
||||||
# Default: [COYOTE.md, AGENTS.md, CLAUDE.md, GEMINI.md]
|
# Set to a custom list to reorder or drop fallbacks, e.g.:
|
||||||
# Set to a custom list to reorder or drop fallbacks, e.g.:
|
# workspace_instructions_files: [COYOTE.md]
|
||||||
# workspace_instructions_files: [COYOTE.md]
|
|
||||||
|
|
||||||
# ---- RAG ----
|
# ---- RAG ----
|
||||||
# See the [RAG Docs](https://github.com/Dark-Alex-17/coyote/wiki/RAG) for more details.
|
# 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_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_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_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_size: null # Defines the size of chunks for document processing in characters
|
||||||
rag_chunk_overlap: null # Defines the overlap between chunks
|
rag_chunk_overlap: null # Defines the overlap between chunks
|
||||||
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_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_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)
|
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)
|
||||||
# Defines the query structure using variables like __CONTEXT__, __SOURCES__, and __INPUT__ to tailor searches to specific needs
|
# Defines the query structure using variables like __CONTEXT__, __SOURCES__, and __INPUT__ to tailor searches to specific needs
|
||||||
rag_template: |
|
rag_template: |
|
||||||
Answer the query based on the context while respecting the rules. (user query, some textual context and rules, all inside xml tags)
|
Answer the query based on the context while respecting the rules. (user query, some textual context and rules, all inside xml tags)
|
||||||
@@ -295,14 +284,13 @@ document_loaders:
|
|||||||
# You can add custom loaders using the following syntax:
|
# You can add custom loaders using the following syntax:
|
||||||
# <file-extension>: <command-to-load-the-file>
|
# <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.
|
# Note: Use `$1` for input file and `$2` for output file. If `$2` is omitted, use stdout as output.
|
||||||
pdf: 'pdftotext $1 -' # Use pdftotext to convert a PDF file to text
|
pdf: 'pdftotext $1 -' # Use pdftotext to convert a PDF file to text
|
||||||
# (see https://poppler.freedesktop.org for details on how to install pdftotext)
|
# (see https://poppler.freedesktop.org for details on how to install pdftotext)
|
||||||
docx: 'pandoc --to plain $1' # Use pandoc to convert a .docx file to text
|
docx: 'pandoc --to plain $1' # Use pandoc to convert a .docx file to text
|
||||||
# (see https://pandoc.org for details on how to install pandoc)
|
# (see https://pandoc.org for details on how to install pandoc)
|
||||||
jina: 'curl -fsSL https://r.jina.ai/$1 -H "Authorization: Bearer {{JINA_API_KEY}}' # Use Jina to translate a website into text;
|
jina: 'curl -fsSL https://r.jina.ai/$1 -H "Authorization: Bearer {{JINA_API_KEY}}' # Use Jina to translate a website into text;
|
||||||
# Requires a Jina API key to be added to the Coyote vault
|
# Requires a Jina API key to be added to the Coyote vault
|
||||||
git:
|
git: > # Use yek to load a git repository into the knowledgebase (https://github.com/bodo-run/yek)
|
||||||
> # Use yek to load a git repository into the knowledgebase (https://github.com/bodo-run/yek)
|
|
||||||
sh -c "yek $1 --json | jq 'map({ path: .filename, contents: .content })'"
|
sh -c "yek $1 --json | jq 'map({ path: .filename, contents: .content })'"
|
||||||
|
|
||||||
# ---- Clients ----
|
# ---- Clients ----
|
||||||
@@ -318,10 +306,10 @@ clients:
|
|||||||
# supports_function_calling: true
|
# supports_function_calling: true
|
||||||
# - name: xxxx # Embedding model
|
# - name: xxxx # Embedding model
|
||||||
# type: embedding
|
# type: embedding
|
||||||
# default_chunk_size: 1500
|
# default_chunk_size: 1500
|
||||||
# max_batch_size: 100
|
# max_batch_size: 100
|
||||||
# - name: xxxx # Reranker model
|
# - name: xxxx # Reranker model
|
||||||
# type: reranker
|
# type: reranker
|
||||||
# patch: # Patch API calls
|
# patch: # Patch API calls
|
||||||
# chat_completions: # API type; Possible values: chat_completions, embeddings, and rerank
|
# 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-.*'
|
# <regex>: # The regex to match model names, e.g. '.*' 'gpt-4o' 'gpt-4o|gpt-4-.*'
|
||||||
@@ -337,15 +325,15 @@ clients:
|
|||||||
|
|
||||||
# See https://platform.openai.com/docs/quickstart
|
# See https://platform.openai.com/docs/quickstart
|
||||||
- type: openai
|
- type: openai
|
||||||
api_base: https://api.openai.com/v1 # Optional
|
api_base: https://api.openai.com/v1 # Optional
|
||||||
api_key: '{{OPENAI_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{OPENAI_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
organization_id: org-xxx # Optional
|
organization_id: org-xxx # Optional
|
||||||
|
|
||||||
# For any platform compatible with OpenAI's API
|
# For any platform compatible with OpenAI's API
|
||||||
- type: openai-compatible
|
- type: openai-compatible
|
||||||
name: ollama
|
name: ollama
|
||||||
api_base: http://localhost:11434/v1
|
api_base: http://localhost:11434/v1
|
||||||
api_key: '{{OLLAMA_API_KEY}}' # Optional; You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{OLLAMA_API_KEY}}' # Optional; You can either hard-code or inject secrets from the Coyote vault
|
||||||
models:
|
models:
|
||||||
- name: deepseek-r1
|
- name: deepseek-r1
|
||||||
max_input_tokens: 131072
|
max_input_tokens: 131072
|
||||||
@@ -363,10 +351,9 @@ clients:
|
|||||||
# See https://ai.google.dev/docs
|
# See https://ai.google.dev/docs
|
||||||
- type: gemini
|
- type: gemini
|
||||||
api_base: https://generativelanguage.googleapis.com/v1beta
|
api_base: https://generativelanguage.googleapis.com/v1beta
|
||||||
api_key: '{{GEMINI_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{GEMINI_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
auth:
|
auth: null # When set to 'oauth', Coyote will use OAuth instead of an API key
|
||||||
null # When set to 'oauth', Coyote will use OAuth instead of an API key
|
# Authenticate with `coyote --authenticate` or `.authenticate` in the REPL
|
||||||
# Authenticate with `coyote --authenticate` or `.authenticate` in the REPL
|
|
||||||
patch:
|
patch:
|
||||||
chat_completions:
|
chat_completions:
|
||||||
'.*':
|
'.*':
|
||||||
@@ -383,27 +370,25 @@ clients:
|
|||||||
|
|
||||||
# See https://docs.anthropic.com/claude/reference/getting-started-with-the-api
|
# See https://docs.anthropic.com/claude/reference/getting-started-with-the-api
|
||||||
- type: claude
|
- type: claude
|
||||||
api_base: https://api.anthropic.com/v1 # Optional
|
api_base: https://api.anthropic.com/v1 # Optional
|
||||||
api_key: '{{ANTHROPIC_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{ANTHROPIC_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
auth:
|
auth: null # When set to 'oauth', Coyote will use OAuth instead of an API key
|
||||||
null # When set to 'oauth', Coyote will use OAuth instead of an API key
|
# Authenticate with `coyote --authenticate` or `.authenticate` in the REPL
|
||||||
# Authenticate with `coyote --authenticate` or `.authenticate` in the REPL
|
|
||||||
|
|
||||||
# See https://docs.mistral.ai/
|
# See https://docs.mistral.ai/
|
||||||
- type: openai-compatible
|
- type: openai-compatible
|
||||||
name: mistral
|
name: mistral
|
||||||
api_base: https://api.mistral.ai/v1
|
api_base: https://api.mistral.ai/v1
|
||||||
api_key: '{{MISTRAL_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{MISTRAL_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
|
|
||||||
# See https://docs.x.ai/docs - OAuth via SuperGrok / X Premium+ subscription
|
# See https://docs.x.ai/docs - OAuth via SuperGrok / X Premium+ subscription
|
||||||
- type: openai-compatible
|
- type: openai-compatible
|
||||||
name: xai
|
name: xai
|
||||||
api_base: https://api.x.ai/v1
|
api_base: https://api.x.ai/v1
|
||||||
api_key: '{{XAI_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{XAI_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
auth:
|
auth: null # When set to 'oauth', Coyote will use OAuth instead of an API key
|
||||||
null # When set to 'oauth', Coyote will use OAuth instead of an API key
|
# Authenticate with `coyote --authenticate` or `.authenticate` in the REPL
|
||||||
# Authenticate with `coyote --authenticate` or `.authenticate` in the REPL
|
# Note: Oauth requires SuperGrok/X Premium+ subscription
|
||||||
# Note: Oauth requires SuperGrok/X Premium+ subscription
|
|
||||||
|
|
||||||
# Example: private OpenAI-compatible gateway with client_credentials OAuth
|
# Example: private OpenAI-compatible gateway with client_credentials OAuth
|
||||||
# - type: openai-compatible
|
# - type: openai-compatible
|
||||||
@@ -433,31 +418,31 @@ clients:
|
|||||||
- type: openai-compatible
|
- type: openai-compatible
|
||||||
name: ai12
|
name: ai12
|
||||||
api_base: https://api.ai21.com/studio/v1
|
api_base: https://api.ai21.com/studio/v1
|
||||||
api_key: '{{AI21_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{AI21_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
|
|
||||||
# See https://docs.cohere.com/docs/the-cohere-platform
|
# See https://docs.cohere.com/docs/the-cohere-platform
|
||||||
- type: cohere
|
- type: cohere
|
||||||
api_base: https://api.cohere.ai/v2 # Optional
|
api_base: https://api.cohere.ai/v2 # Optional
|
||||||
api_key: '{{COHERE_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{COHERE_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
|
|
||||||
# See https://docs.perplexity.ai/getting-started/overview
|
# See https://docs.perplexity.ai/getting-started/overview
|
||||||
- type: openai-compatible
|
- type: openai-compatible
|
||||||
name: perplexity
|
name: perplexity
|
||||||
api_base: https://api.perplexity.ai
|
api_base: https://api.perplexity.ai
|
||||||
api_key: '{{PERPLEXITY_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{PERPLEXITY_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
|
|
||||||
# See https://console.groq.com/docs/quickstart
|
# See https://console.groq.com/docs/quickstart
|
||||||
- type: openai-compatible
|
- type: openai-compatible
|
||||||
name: groq
|
name: groq
|
||||||
api_base: https://api.groq.com/openai/v1
|
api_base: https://api.groq.com/openai/v1
|
||||||
api_key: '{{GROQ_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{GROQ_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
|
|
||||||
# See https://learn.microsoft.com/en-us/azure/ai-services/openai/chatgpt-quickstart
|
# See https://learn.microsoft.com/en-us/azure/ai-services/openai/chatgpt-quickstart
|
||||||
- type: azure-openai
|
- type: azure-openai
|
||||||
api_base: https://{RESOURCE}.openai.azure.com
|
api_base: https://{RESOURCE}.openai.azure.com
|
||||||
api_key: '{{AZURE_OPENAI_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{AZURE_OPENAI_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
models:
|
models:
|
||||||
- name: gpt-4o # Model deployment name
|
- name: gpt-4o # Model deployment name
|
||||||
max_input_tokens: 128000
|
max_input_tokens: 128000
|
||||||
supports_vision: true
|
supports_vision: true
|
||||||
supports_function_calling: true
|
supports_function_calling: true
|
||||||
@@ -469,7 +454,7 @@ clients:
|
|||||||
# Specifies an application default credentials (adc) file
|
# Specifies an application default credentials (adc) file
|
||||||
# Run `gcloud auth application-default login` to initialize the ADC file
|
# Run `gcloud auth application-default login` to initialize the ADC file
|
||||||
# see https://cloud.google.com/docs/authentication/external/set-up-adc for more information
|
# see https://cloud.google.com/docs/authentication/external/set-up-adc for more information
|
||||||
adc_file: <gcloud-config-dir>/application_default_credentials.json # Optional
|
adc_file: <gcloud-config-dir>/application_default_credentials.json # Optional
|
||||||
patch:
|
patch:
|
||||||
chat_completions:
|
chat_completions:
|
||||||
'gemini-.*':
|
'gemini-.*':
|
||||||
@@ -486,76 +471,77 @@ clients:
|
|||||||
|
|
||||||
# See https://docs.aws.amazon.com/bedrock/latest/userguide/
|
# See https://docs.aws.amazon.com/bedrock/latest/userguide/
|
||||||
- type: bedrock
|
- type: bedrock
|
||||||
access_key_id: '{{AWS_ACCESS_KEY_ID}}' # You can either hard-code or inject secrets from the Coyote vault
|
access_key_id: '{{AWS_ACCESS_KEY_ID}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
secret_access_key: '{{AWS_SECRET_ACCESS_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
secret_access_key: '{{AWS_SECRET_ACCESS_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
region: xxx
|
region: xxx
|
||||||
session_token: xxx # Optional, only needed for temporary credentials
|
session_token: xxx # Optional, only needed for temporary credentials
|
||||||
|
|
||||||
# See https://developers.cloudflare.com/workers-ai/
|
# See https://developers.cloudflare.com/workers-ai/
|
||||||
- type: openai-compatible
|
- type: openai-compatible
|
||||||
name: cloudflare
|
name: cloudflare
|
||||||
api_base: https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/ai/v1
|
api_base: https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/ai/v1
|
||||||
api_key: '{{CLOUDFLARE_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{CLOUDFLARE_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
|
|
||||||
# See https://cloud.baidu.com/doc/WENXINWORKSHOP/index.html
|
# See https://cloud.baidu.com/doc/WENXINWORKSHOP/index.html
|
||||||
- type: openai-compatible
|
- type: openai-compatible
|
||||||
name: ernie
|
name: ernie
|
||||||
api_base: https://qianfan.baidubce.com/v2
|
api_base: https://qianfan.baidubce.com/v2
|
||||||
api_key: '{{BAIDU_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{BAIDU_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
|
|
||||||
# See https://dashscope.aliyun.com/
|
# See https://dashscope.aliyun.com/
|
||||||
- type: openai-compatible
|
- type: openai-compatible
|
||||||
name: qianwen
|
name: qianwen
|
||||||
api_base: https://dashscope.aliyuncs.com/compatible-mode/v1
|
api_base: https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||||
api_key: '{{ALIYUN_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{ALIYUN_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
|
|
||||||
# See https://cloud.tencent.com/product/hunyuan
|
# See https://cloud.tencent.com/product/hunyuan
|
||||||
- type: openai-compatible
|
- type: openai-compatible
|
||||||
name: hunyuan
|
name: hunyuan
|
||||||
api_base: https://api.hunyuan.cloud.tencent.com/v1
|
api_base: https://api.hunyuan.cloud.tencent.com/v1
|
||||||
api_key: '{{TENCENT_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{TENCENT_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
|
|
||||||
# See https://platform.moonshot.cn/docs/intro
|
# See https://platform.moonshot.cn/docs/intro
|
||||||
- type: openai-compatible
|
- type: openai-compatible
|
||||||
name: moonshot
|
name: moonshot
|
||||||
api_base: https://api.moonshot.cn/v1
|
api_base: https://api.moonshot.cn/v1
|
||||||
api_key: '{{MOONSHOT_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{MOONSHOT_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
|
|
||||||
# See https://platform.deepseek.com/api-docs/
|
# See https://platform.deepseek.com/api-docs/
|
||||||
- type: openai-compatible
|
- type: openai-compatible
|
||||||
name: deepseek
|
name: deepseek
|
||||||
api_base: https://api.deepseek.com
|
api_base: https://api.deepseek.com
|
||||||
api_key: '{{DEEPSEEK_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{DEEPSEEK_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
|
|
||||||
# See https://open.bigmodel.cn/dev/howuse/introduction
|
# See https://open.bigmodel.cn/dev/howuse/introduction
|
||||||
- type: openai-compatible
|
- type: openai-compatible
|
||||||
name: zhipuai
|
name: zhipuai
|
||||||
api_base: https://open.bigmodel.cn/api/paas/v4
|
api_base: https://open.bigmodel.cn/api/paas/v4
|
||||||
api_key: '{{ZHIPUAI_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{ZHIPUAI_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
|
|
||||||
# See https://platform.minimaxi.com/document/Fast%20access
|
# See https://platform.minimaxi.com/document/Fast%20access
|
||||||
- type: openai-compatible
|
- type: openai-compatible
|
||||||
name: minimax
|
name: minimax
|
||||||
api_base: https://api.minimax.chat/v1
|
api_base: https://api.minimax.chat/v1
|
||||||
api_key: '{{MINIMAX_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{MINIMAX_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
|
|
||||||
# See https://openrouter.ai/docs#quick-start
|
# See https://openrouter.ai/docs#quick-start
|
||||||
- type: openai-compatible
|
- type: openai-compatible
|
||||||
name: openrouter
|
name: openrouter
|
||||||
api_base: https://openrouter.ai/api/v1
|
api_base: https://openrouter.ai/api/v1
|
||||||
api_key: '{{OPENROUTER_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{OPENROUTER_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
|
|
||||||
# See https://github.com/marketplace/models
|
# See https://github.com/marketplace/models
|
||||||
- type: openai-compatible
|
- type: openai-compatible
|
||||||
name: github
|
name: github
|
||||||
api_base: https://models.inference.ai.azure.com
|
api_base: https://models.inference.ai.azure.com
|
||||||
api_key: '{{GITHUB_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{GITHUB_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
|
|
||||||
# See https://deepinfra.com/docs
|
# See https://deepinfra.com/docs
|
||||||
- type: openai-compatible
|
- type: openai-compatible
|
||||||
name: deepinfra
|
name: deepinfra
|
||||||
api_base: https://api.deepinfra.com/v1/openai
|
api_base: https://api.deepinfra.com/v1/openai
|
||||||
api_key: '{{DEEPINFRA_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{DEEPINFRA_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
|
|
||||||
|
|
||||||
# ----- RAG dedicated -----
|
# ----- RAG dedicated -----
|
||||||
|
|
||||||
@@ -563,10 +549,10 @@ clients:
|
|||||||
- type: openai-compatible
|
- type: openai-compatible
|
||||||
name: jina
|
name: jina
|
||||||
api_base: https://api.jina.ai/v1
|
api_base: https://api.jina.ai/v1
|
||||||
api_key: '{{JINA_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{JINA_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
|
|
||||||
# See https://docs.voyageai.com/docs/introduction
|
# See https://docs.voyageai.com/docs/introduction
|
||||||
- type: openai-compatible
|
- type: openai-compatible
|
||||||
name: voyageai
|
name: voyageai
|
||||||
api_base: https://api.voyageai.com/v1
|
api_base: https://api.voyageai.com/v1
|
||||||
api_key: '{{VOYAGEAI_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
api_key: '{{VOYAGEAI_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||||
@@ -69,7 +69,7 @@ impl AppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut functions = Functions::init(config.visible_tools.as_ref().unwrap_or(&Vec::new()))?;
|
let mut functions = Functions::init(config.visible_tools.as_deref())?;
|
||||||
if !mcp_registry.is_empty() && config.mcp_server_support {
|
if !mcp_registry.is_empty() && config.mcp_server_support {
|
||||||
functions.append_mcp_meta_functions(mcp_registry.server_features());
|
functions.append_mcp_meta_functions(mcp_registry.server_features());
|
||||||
}
|
}
|
||||||
|
|||||||
+91
-83
@@ -150,6 +150,9 @@ fn validate_no_template_in_secrets_provider(content: &str) -> Result<()> {
|
|||||||
const DARK_THEME: &[u8] = include_bytes!("../../assets/monokai-extended.theme.bin");
|
const DARK_THEME: &[u8] = include_bytes!("../../assets/monokai-extended.theme.bin");
|
||||||
const LIGHT_THEME: &[u8] = include_bytes!("../../assets/monokai-extended-light.theme.bin");
|
const LIGHT_THEME: &[u8] = include_bytes!("../../assets/monokai-extended-light.theme.bin");
|
||||||
|
|
||||||
|
/// Fully documented config skeleton the first-run wizard splices dynamic values into.
|
||||||
|
const CONFIG_TEMPLATE: &str = include_str!("../../assets/config-template.yaml");
|
||||||
|
|
||||||
const CONFIG_FILE_NAME: &str = "config.yaml";
|
const CONFIG_FILE_NAME: &str = "config.yaml";
|
||||||
const AGENT_GRAPH_FILE_NAME: &str = "graph.yaml";
|
const AGENT_GRAPH_FILE_NAME: &str = "graph.yaml";
|
||||||
const ROLES_DIR_NAME: &str = "roles";
|
const ROLES_DIR_NAME: &str = "roles";
|
||||||
@@ -180,27 +183,6 @@ const BUNDLE_MANIFEST_FILE: &str = "coyote-bundle.yaml";
|
|||||||
const SBX_MIXIN_KITS_DIR_NAME: &str = "sbx-mixin-kits";
|
const SBX_MIXIN_KITS_DIR_NAME: &str = "sbx-mixin-kits";
|
||||||
const GIT_DIR_NAME: &str = ".git";
|
const GIT_DIR_NAME: &str = ".git";
|
||||||
const GITIGNORE_FILE_NAME: &str = ".gitignore";
|
const GITIGNORE_FILE_NAME: &str = ".gitignore";
|
||||||
const DEFAULT_VISIBLE_TOOLS: [&str; 19] = [
|
|
||||||
"execute_command.sh",
|
|
||||||
"execute_py_code.py",
|
|
||||||
"execute_sql_code.sh",
|
|
||||||
"fetch_url_via_curl.sh",
|
|
||||||
"fs_cat.sh",
|
|
||||||
"fs_glob.sh",
|
|
||||||
"fs_grep.sh",
|
|
||||||
"fs_ls.sh",
|
|
||||||
"fs_mkdir.sh",
|
|
||||||
"fs_patch.sh",
|
|
||||||
"fs_read.sh",
|
|
||||||
"fs_rm.sh",
|
|
||||||
"fs_write.sh",
|
|
||||||
"ast_grep.sh",
|
|
||||||
"get_current_time.sh",
|
|
||||||
"get_current_weather.sh",
|
|
||||||
"search_wikipedia.sh",
|
|
||||||
"search_arxiv.sh",
|
|
||||||
"web_search_coyote.sh",
|
|
||||||
];
|
|
||||||
|
|
||||||
const CLIENTS_FIELD: &str = "clients";
|
const CLIENTS_FIELD: &str = "clients";
|
||||||
|
|
||||||
@@ -811,51 +793,20 @@ pub async fn create_config_file(config_path: &Path) -> Result<()> {
|
|||||||
|
|
||||||
let client = Select::new("API Provider (required):", list_client_types()).prompt()?;
|
let client = Select::new("API Provider (required):", list_client_types()).prompt()?;
|
||||||
|
|
||||||
let mut config = json!({});
|
|
||||||
let (model, clients_config) = create_client_config(client, &vault).await?;
|
let (model, clients_config) = create_client_config(client, &vault).await?;
|
||||||
config["model"] = model.into();
|
let secrets = match &provider_choice {
|
||||||
match &provider_choice {
|
None => json!({
|
||||||
None => {
|
"vault_password_file": vault.local_password_file()?.display().to_string()
|
||||||
config["vault_password_file"] =
|
}),
|
||||||
vault.local_password_file()?.display().to_string().into();
|
|
||||||
}
|
|
||||||
Some(provider) => {
|
Some(provider) => {
|
||||||
config["secrets_provider"] = serde_json::to_value(provider)
|
let provider = serde_json::to_value(provider)
|
||||||
.with_context(|| "failed to serialize secrets_provider config")?;
|
.with_context(|| "failed to serialize secrets_provider config")?;
|
||||||
|
json!({ "secrets_provider": provider })
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
config["stream"] = json!(true);
|
|
||||||
config["save"] = json!(true);
|
|
||||||
config["keybindings"] = json!("vi");
|
|
||||||
config["wrap"] = json!("auto");
|
|
||||||
config["wrap_code"] = json!(false);
|
|
||||||
config["function_calling_support"] = json!(true);
|
|
||||||
config["enabled_tools"] = json!(null);
|
|
||||||
config["visible_tools"] = json!(DEFAULT_VISIBLE_TOOLS);
|
|
||||||
config["mcp_server_support"] = json!(true);
|
|
||||||
config["enabled_mcp_servers"] = json!(null);
|
|
||||||
config["highlight"] = json!(true);
|
|
||||||
config["light_theme"] = json!(false);
|
|
||||||
config[CLIENTS_FIELD] = clients_config;
|
|
||||||
|
|
||||||
let config_data = serde_yaml::to_string(&config).with_context(|| "Failed to create config")?;
|
let config_data = render_config_template(&model, Some(&secrets), &clients_config)?;
|
||||||
let config_data = format!(
|
write_config_file(config_path, &config_data)
|
||||||
"# see https://github.com/Dark-Alex-17/coyote/blob/main/config.example.yaml\n\n{config_data}"
|
|
||||||
);
|
|
||||||
|
|
||||||
ensure_parent_exists(config_path)?;
|
|
||||||
std::fs::write(config_path, config_data)
|
|
||||||
.with_context(|| format!("Failed to write to '{}'", config_path.display()))?;
|
|
||||||
#[cfg(unix)]
|
|
||||||
{
|
|
||||||
use std::os::unix::prelude::PermissionsExt;
|
|
||||||
let perms = std::fs::Permissions::from_mode(0o600);
|
|
||||||
std::fs::set_permissions(config_path, perms)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("✓ Saved the config file to '{}'.\n", config_path.display());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_config_file_sandbox(config_path: &Path) -> Result<()> {
|
async fn create_config_file_sandbox(config_path: &Path) -> Result<()> {
|
||||||
@@ -865,7 +816,7 @@ async fn create_config_file_sandbox(config_path: &Path) -> Result<()> {
|
|||||||
"Running in sandbox mode — your API provider credentials are managed by your host Coyote configuration if configured."
|
"Running in sandbox mode — your API provider credentials are managed by your host Coyote configuration if configured."
|
||||||
);
|
);
|
||||||
|
|
||||||
let oai_api_base = client::OPENAI_COMPATIBLE_PROVIDERS
|
let oai_api_base = OPENAI_COMPATIBLE_PROVIDERS
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(name, _)| *name == client)
|
.find(|(name, _)| *name == client)
|
||||||
.map(|(_, url)| *url);
|
.map(|(_, url)| *url);
|
||||||
@@ -878,13 +829,13 @@ async fn create_config_file_sandbox(config_path: &Path) -> Result<()> {
|
|||||||
} else {
|
} else {
|
||||||
api_base.to_string()
|
api_base.to_string()
|
||||||
};
|
};
|
||||||
serde_json::json!({
|
json!({
|
||||||
"type": "openai-compatible",
|
"type": "openai-compatible",
|
||||||
"name": client,
|
"name": client,
|
||||||
"api_base": api_base_str,
|
"api_base": api_base_str,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
serde_json::json!({ "type": client })
|
json!({ "type": client })
|
||||||
};
|
};
|
||||||
|
|
||||||
if client::client_type_supports_oauth(client) {
|
if client::client_type_supports_oauth(client) {
|
||||||
@@ -898,27 +849,35 @@ async fn create_config_file_sandbox(config_path: &Path) -> Result<()> {
|
|||||||
|
|
||||||
let model = set_client_models_config(&mut client_config, client).await?;
|
let model = set_client_models_config(&mut client_config, client).await?;
|
||||||
|
|
||||||
let mut config = serde_json::json!({});
|
let config_data = render_config_template(&model, None, &json!([client_config]))?;
|
||||||
config["model"] = model.into();
|
write_config_file(config_path, &config_data)
|
||||||
config["stream"] = serde_json::json!(true);
|
}
|
||||||
config["save"] = serde_json::json!(true);
|
|
||||||
config["keybindings"] = serde_json::json!("vi");
|
|
||||||
config["wrap"] = serde_json::json!("auto");
|
|
||||||
config["wrap_code"] = serde_json::json!(false);
|
|
||||||
config["function_calling_support"] = serde_json::json!(true);
|
|
||||||
config["enabled_tools"] = serde_json::json!(null);
|
|
||||||
config["visible_tools"] = serde_json::json!(DEFAULT_VISIBLE_TOOLS);
|
|
||||||
config["mcp_server_support"] = serde_json::json!(true);
|
|
||||||
config["enabled_mcp_servers"] = serde_json::json!(null);
|
|
||||||
config["highlight"] = serde_json::json!(true);
|
|
||||||
config["light_theme"] = serde_json::json!(false);
|
|
||||||
config[CLIENTS_FIELD] = serde_json::json!(vec![client_config]);
|
|
||||||
|
|
||||||
let config_data = serde_yaml::to_string(&config).with_context(|| "Failed to create config")?;
|
fn render_config_template(
|
||||||
let config_data = format!(
|
model: &str,
|
||||||
"# see https://github.com/Dark-Alex-17/coyote/blob/main/config.example.yaml\n\n{config_data}"
|
secrets: Option<&serde_json::Value>,
|
||||||
);
|
clients: &serde_json::Value,
|
||||||
|
) -> Result<String> {
|
||||||
|
let to_yaml = |value: &serde_json::Value| {
|
||||||
|
serde_yaml::to_string(value).with_context(|| "Failed to create config")
|
||||||
|
};
|
||||||
|
|
||||||
|
let model_block = to_yaml(&json!({ "model": model }))?;
|
||||||
|
let secrets_block = match secrets {
|
||||||
|
Some(value) => to_yaml(value)?,
|
||||||
|
None => "# Sandbox mode: no vault provider is configured; secrets are provisioned\n\
|
||||||
|
# from the host when the sandbox is created.\n"
|
||||||
|
.to_string(),
|
||||||
|
};
|
||||||
|
let clients_block = to_yaml(&json!({ CLIENTS_FIELD: clients }))?;
|
||||||
|
|
||||||
|
Ok(CONFIG_TEMPLATE
|
||||||
|
.replacen("__MODEL_BLOCK__\n", &model_block, 1)
|
||||||
|
.replacen("__SECRETS_BLOCK__\n", &secrets_block, 1)
|
||||||
|
.replacen("__CLIENTS_BLOCK__\n", &clients_block, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_config_file(config_path: &Path, config_data: &str) -> Result<()> {
|
||||||
ensure_parent_exists(config_path)?;
|
ensure_parent_exists(config_path)?;
|
||||||
std::fs::write(config_path, config_data)
|
std::fs::write(config_path, config_data)
|
||||||
.with_context(|| format!("Failed to write to '{}'", config_path.display()))?;
|
.with_context(|| format!("Failed to write to '{}'", config_path.display()))?;
|
||||||
@@ -1174,6 +1133,55 @@ clients:
|
|||||||
assert_eq!(cfg.enabled_macros, None);
|
assert_eq!(cfg.enabled_macros, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_template_renders_parseable_config() {
|
||||||
|
let secrets = json!({ "vault_password_file": "/home/user/.coyote_password" });
|
||||||
|
let clients = json!([{ "type": "openai", "api_key": "sk-test" }]);
|
||||||
|
|
||||||
|
let rendered = render_config_template("openai:gpt-4o", Some(&secrets), &clients).unwrap();
|
||||||
|
|
||||||
|
assert!(!rendered.contains("__MODEL_BLOCK__"));
|
||||||
|
assert!(!rendered.contains("__SECRETS_BLOCK__"));
|
||||||
|
assert!(!rendered.contains("__CLIENTS_BLOCK__"));
|
||||||
|
|
||||||
|
let cfg = Config::load_from_str(&rendered).unwrap();
|
||||||
|
assert_eq!(cfg.model_id, "openai:gpt-4o");
|
||||||
|
assert_eq!(
|
||||||
|
cfg.vault_password_file,
|
||||||
|
Some(PathBuf::from("/home/user/.coyote_password"))
|
||||||
|
);
|
||||||
|
assert!(cfg.secrets_provider.is_none());
|
||||||
|
assert_eq!(cfg.keybindings, "emacs");
|
||||||
|
assert!(cfg.save);
|
||||||
|
assert_eq!(cfg.wrap.as_deref(), Some("auto"));
|
||||||
|
assert!(cfg.visible_tools.is_none());
|
||||||
|
assert!(cfg.mapping_tools.is_empty());
|
||||||
|
assert!(cfg.document_loaders.is_empty());
|
||||||
|
assert_eq!(cfg.compression_threshold, 4000);
|
||||||
|
assert!(cfg.theme.is_none());
|
||||||
|
assert_eq!(cfg.clients.len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_template_renders_parseable_sandbox_config() {
|
||||||
|
let clients = json!([{ "type": "claude" }]);
|
||||||
|
|
||||||
|
let rendered =
|
||||||
|
render_config_template("claude:claude-sonnet-4-20250514", None, &clients).unwrap();
|
||||||
|
|
||||||
|
assert!(!rendered.contains("__SECRETS_BLOCK__"));
|
||||||
|
|
||||||
|
let cfg = Config::load_from_str(&rendered).unwrap();
|
||||||
|
assert_eq!(cfg.model_id, "claude:claude-sonnet-4-20250514");
|
||||||
|
assert!(cfg.vault_password_file.is_none());
|
||||||
|
assert!(cfg.secrets_provider.is_none());
|
||||||
|
assert_eq!(cfg.keybindings, "emacs");
|
||||||
|
assert!(cfg.save);
|
||||||
|
assert!(cfg.visible_tools.is_none());
|
||||||
|
assert_eq!(cfg.compression_threshold, 4000);
|
||||||
|
assert_eq!(cfg.clients.len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn config_enabled_macros_empty_string_is_some_empty() {
|
fn config_enabled_macros_empty_string_is_some_empty() {
|
||||||
let cfg: Config = serde_yaml::from_str("enabled_macros: \"\"").unwrap();
|
let cfg: Config = serde_yaml::from_str("enabled_macros: \"\"").unwrap();
|
||||||
|
|||||||
@@ -4266,7 +4266,7 @@ impl RequestContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut functions = Functions::init(app.visible_tools.as_ref().unwrap_or(&Vec::new()))?;
|
let mut functions = Functions::init(app.visible_tools.as_deref())?;
|
||||||
if self.working_mode.is_repl() {
|
if self.working_mode.is_repl() {
|
||||||
functions.append_user_interaction_functions();
|
functions.append_user_interaction_functions();
|
||||||
}
|
}
|
||||||
@@ -4687,7 +4687,7 @@ impl RequestContext {
|
|||||||
|
|
||||||
pub fn exit_agent(&mut self, app: &AppConfig) -> Result<()> {
|
pub fn exit_agent(&mut self, app: &AppConfig) -> Result<()> {
|
||||||
self.exit_session()?;
|
self.exit_session()?;
|
||||||
let mut functions = Functions::init(app.visible_tools.as_ref().unwrap_or(&Vec::new()))?;
|
let mut functions = Functions::init(app.visible_tools.as_deref())?;
|
||||||
if self.working_mode.is_repl() {
|
if self.working_mode.is_repl() {
|
||||||
functions.append_user_interaction_functions();
|
functions.append_user_interaction_functions();
|
||||||
}
|
}
|
||||||
|
|||||||
+132
-34
@@ -214,6 +214,57 @@ fn tool_source_stems() -> Result<HashSet<String>> {
|
|||||||
Ok(stems)
|
Ok(stems)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn all_tool_source_files() -> Result<Vec<String>> {
|
||||||
|
let tools_dir = paths::global_tools_dir();
|
||||||
|
if !tools_dir.exists() {
|
||||||
|
return Ok(Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file_names = Vec::new();
|
||||||
|
for entry in fs::read_dir(&tools_dir)? {
|
||||||
|
let path = entry?.path();
|
||||||
|
if path.is_file()
|
||||||
|
&& let Some(name) = path.file_name().and_then(OsStr::to_str)
|
||||||
|
{
|
||||||
|
file_names.push(name.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(dedupe_tool_files_by_stem(file_names))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dedupe_tool_files_by_stem(file_names: Vec<String>) -> Vec<String> {
|
||||||
|
fn extension_rank(name: &str) -> Option<usize> {
|
||||||
|
let ext = Path::new(name).extension().and_then(OsStr::to_str)?;
|
||||||
|
match Language::from_extension(ext) {
|
||||||
|
Language::Bash => Some(0),
|
||||||
|
Language::Python => Some(1),
|
||||||
|
Language::TypeScript => Some(2),
|
||||||
|
Language::Unsupported => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut best: HashMap<String, (usize, String)> = HashMap::new();
|
||||||
|
for name in file_names {
|
||||||
|
let Some(rank) = extension_rank(&name) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(stem) = Path::new(&name).file_stem().and_then(OsStr::to_str) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
match best.get(stem) {
|
||||||
|
Some((best_rank, _)) if *best_rank <= rank => {}
|
||||||
|
_ => {
|
||||||
|
best.insert(stem.to_string(), (rank, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut files: Vec<String> = best.into_values().map(|(_, name)| name).collect();
|
||||||
|
files.sort();
|
||||||
|
files
|
||||||
|
}
|
||||||
|
|
||||||
fn bin_entry_stem(file_name: &str) -> &str {
|
fn bin_entry_stem(file_name: &str) -> &str {
|
||||||
let name = file_name.strip_prefix("run-").unwrap_or(file_name);
|
let name = file_name.strip_prefix("run-").unwrap_or(file_name);
|
||||||
Path::new(name)
|
Path::new(name)
|
||||||
@@ -587,18 +638,23 @@ impl Functions {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(visible_tools: &[String]) -> Result<Self> {
|
pub fn init(visible_tools: Option<&[String]>) -> Result<Self> {
|
||||||
Self::remove_stale_global_function_binaries()?;
|
Self::remove_stale_global_function_binaries()?;
|
||||||
|
|
||||||
|
let (visible_tools, lenient) = match visible_tools {
|
||||||
|
Some(tools) => (tools.to_vec(), false),
|
||||||
|
None => (all_tool_source_files()?, true),
|
||||||
|
};
|
||||||
|
|
||||||
let declarations = Self {
|
let declarations = Self {
|
||||||
declarations: Self::build_global_tool_declarations(visible_tools)?,
|
declarations: Self::build_global_tool_declarations(&visible_tools, lenient)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Building global function binaries in {}",
|
"Building global function binaries in {}",
|
||||||
paths::functions_bin_dir().display()
|
paths::functions_bin_dir().display()
|
||||||
);
|
);
|
||||||
Self::build_global_function_binaries(visible_tools, None)?;
|
Self::build_global_function_binaries(&visible_tools, None, lenient)?;
|
||||||
|
|
||||||
Ok(declarations)
|
Ok(declarations)
|
||||||
}
|
}
|
||||||
@@ -608,13 +664,13 @@ impl Functions {
|
|||||||
|
|
||||||
let global_tools_declarations = if !global_tools.is_empty() {
|
let global_tools_declarations = if !global_tools.is_empty() {
|
||||||
info!("Loading global tools for agent: {name}: {global_tools:?}");
|
info!("Loading global tools for agent: {name}: {global_tools:?}");
|
||||||
let tools_declarations = Self::build_global_tool_declarations(global_tools)?;
|
let tools_declarations = Self::build_global_tool_declarations(global_tools, false)?;
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Building global function binaries required by agent: {name} in {}",
|
"Building global function binaries required by agent: {name} in {}",
|
||||||
paths::functions_bin_dir().display()
|
paths::functions_bin_dir().display()
|
||||||
);
|
);
|
||||||
Self::build_global_function_binaries(global_tools, Some(name))?;
|
Self::build_global_function_binaries(global_tools, Some(name), false)?;
|
||||||
tools_declarations
|
tools_declarations
|
||||||
} else {
|
} else {
|
||||||
debug!("No global tools found for agent: {}", name);
|
debug!("No global tools found for agent: {}", name);
|
||||||
@@ -964,13 +1020,17 @@ impl Functions {
|
|||||||
|
|
||||||
fn build_global_tool_declarations(
|
fn build_global_tool_declarations(
|
||||||
enabled_tools: &[String],
|
enabled_tools: &[String],
|
||||||
|
lenient: bool,
|
||||||
) -> Result<Vec<FunctionDeclaration>> {
|
) -> Result<Vec<FunctionDeclaration>> {
|
||||||
let global_tools_directory = paths::global_tools_dir();
|
let global_tools_directory = paths::global_tools_dir();
|
||||||
let mut function_declarations = Vec::new();
|
let mut function_declarations = Vec::new();
|
||||||
|
|
||||||
for tool in enabled_tools {
|
for tool in enabled_tools {
|
||||||
let declaration = Self::generate_declarations(&global_tools_directory.join(tool))?;
|
match Self::generate_declarations(&global_tools_directory.join(tool)) {
|
||||||
function_declarations.extend(declaration);
|
Ok(declaration) => function_declarations.extend(declaration),
|
||||||
|
Err(err) if lenient => warn!("Skipping tool {tool}: {err}"),
|
||||||
|
Err(err) => return Err(err),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(function_declarations)
|
Ok(function_declarations)
|
||||||
@@ -1032,41 +1092,50 @@ impl Functions {
|
|||||||
fn build_global_function_binaries(
|
fn build_global_function_binaries(
|
||||||
enabled_tools: &[String],
|
enabled_tools: &[String],
|
||||||
agent_name: Option<&str>,
|
agent_name: Option<&str>,
|
||||||
|
lenient: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
for tool in enabled_tools {
|
for tool in enabled_tools {
|
||||||
let language = Language::from(
|
match Self::build_global_function_binary(tool, agent_name) {
|
||||||
&Path::new(&tool)
|
Ok(()) => {}
|
||||||
.extension()
|
Err(err) if lenient => warn!("Skipping binary for tool {tool}: {err}"),
|
||||||
.and_then(OsStr::to_str)
|
Err(err) => return Err(err),
|
||||||
.map(|s| s.to_lowercase())
|
|
||||||
.ok_or_else(|| {
|
|
||||||
anyhow::format_err!("Unable to extract file extension from path: {tool:?}")
|
|
||||||
})?,
|
|
||||||
);
|
|
||||||
let binary_name = Path::new(&tool)
|
|
||||||
.file_stem()
|
|
||||||
.and_then(OsStr::to_str)
|
|
||||||
.ok_or_else(|| {
|
|
||||||
anyhow::format_err!("Unable to extract file name from path: {tool:?}")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
if language == Language::Unsupported {
|
|
||||||
bail!("Unsupported tool file extension: {}", language.as_ref());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let tool_path = paths::global_tools_dir().join(tool);
|
|
||||||
let custom_runtime = extract_shebang_runtime(&tool_path);
|
|
||||||
Self::build_binaries(
|
|
||||||
binary_name,
|
|
||||||
language,
|
|
||||||
BinaryType::Tool(agent_name),
|
|
||||||
custom_runtime.as_deref(),
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_global_function_binary(tool: &str, agent_name: Option<&str>) -> Result<()> {
|
||||||
|
let language = Language::from(
|
||||||
|
&Path::new(tool)
|
||||||
|
.extension()
|
||||||
|
.and_then(OsStr::to_str)
|
||||||
|
.map(|s| s.to_lowercase())
|
||||||
|
.ok_or_else(|| {
|
||||||
|
anyhow::format_err!("Unable to extract file extension from path: {tool:?}")
|
||||||
|
})?,
|
||||||
|
);
|
||||||
|
let binary_name = Path::new(tool)
|
||||||
|
.file_stem()
|
||||||
|
.and_then(OsStr::to_str)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
anyhow::format_err!("Unable to extract file name from path: {tool:?}")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if language == Language::Unsupported {
|
||||||
|
bail!("Unsupported tool file extension: {}", language.as_ref());
|
||||||
|
}
|
||||||
|
|
||||||
|
let tool_path = paths::global_tools_dir().join(tool);
|
||||||
|
let custom_runtime = extract_shebang_runtime(&tool_path);
|
||||||
|
Self::build_binaries(
|
||||||
|
binary_name,
|
||||||
|
language,
|
||||||
|
BinaryType::Tool(agent_name),
|
||||||
|
custom_runtime.as_deref(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn remove_stale_agent_bin_entries(name: &str) -> Result<()> {
|
fn remove_stale_agent_bin_entries(name: &str) -> Result<()> {
|
||||||
let agent_bin_directory = paths::agent_bin_dir(name);
|
let agent_bin_directory = paths::agent_bin_dir(name);
|
||||||
|
|
||||||
@@ -2603,6 +2672,35 @@ mod tests {
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{mem, process};
|
use std::{mem, process};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dedupe_tool_files_prefers_sh_over_py_over_ts() {
|
||||||
|
let files = vec![
|
||||||
|
"get_current_weather.ts".to_string(),
|
||||||
|
"get_current_weather.py".to_string(),
|
||||||
|
"get_current_weather.sh".to_string(),
|
||||||
|
"fetch.ts".to_string(),
|
||||||
|
"fetch.py".to_string(),
|
||||||
|
"demo_ts.ts".to_string(),
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
dedupe_tool_files_by_stem(files),
|
||||||
|
vec!["demo_ts.ts", "fetch.py", "get_current_weather.sh"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dedupe_tool_files_skips_unsupported_extensions() {
|
||||||
|
let files = vec![
|
||||||
|
"notes.md".to_string(),
|
||||||
|
"tool.sh".to_string(),
|
||||||
|
"README".to_string(),
|
||||||
|
"archive.tar.gz".to_string(),
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_eq!(dedupe_tool_files_by_stem(files), vec!["tool.sh"]);
|
||||||
|
}
|
||||||
|
|
||||||
fn call(name: &str, id: Option<&str>) -> ToolCall {
|
fn call(name: &str, id: Option<&str>) -> ToolCall {
|
||||||
ToolCall::new(name.to_string(), json!({}), id.map(|s| s.to_string()))
|
ToolCall::new(name.to_string(), json!({}), id.map(|s| s.to_string()))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user