Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .commitlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": ["@commitlint/config-conventional"]
}
18 changes: 15 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,22 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: npm }
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with: { node-version: 24, cache: npm }
- run: npm ci
- run: npm run format:check
- run: npm run typecheck
- run: npm test
- run: npm run build

commitlint:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with: { fetch-depth: 0 }
- uses: actions/setup-node@v5
with: { node-version: 24, cache: npm }
- run: npm ci
- run: npx commitlint --from "${{ github.event.pull_request.base.sha }}" --to "${{ github.event.pull_request.head.sha }}" --verbose
1 change: 1 addition & 0 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npx --no-install commitlint --edit "$1"
7 changes: 7 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
branch="$(git branch --show-current)"
if [ "$branch" = "main" ] || [ "$branch" = "master" ]; then
echo "✋ Direct commits to '$branch' are blocked. Branch off and open a PR."
echo " Override (rare): git commit --no-verify"
exit 1
fi
npx lint-staged
5 changes: 5 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
dist
node_modules
package
*.tgz
package-lock.json
3 changes: 3 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"printWidth": 100
}
56 changes: 28 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ User → Claude Code → agy-bridge (MCP) → agy CLI → Gemini / Claude / GPT-

## Why this over claude-to-agy?

| | claude-to-agy | **agy-bridge** |
|---|---|---|
| Tool surface | 1 generic `delegate_to_agy` | 6 purpose-built tools — Claude self-routes reliably |
| Model selection | none (agy default only) | per-tool routing across all `agy models`, with availability detection and fallback |
| Multi-turn | stateless | session continuity — `follow_up` resumes agy conversations without resending context |
| Output safety | unbounded | configurable truncation cap protects Claude's context |
| Sandbox | no | optional `--sandbox` mode |
| Install | uvx (Python) | npx (Node) — zero install |
| | claude-to-agy | **agy-bridge** |
| --------------- | --------------------------- | ------------------------------------------------------------------------------------ |
| Tool surface | 1 generic `delegate_to_agy` | 6 purpose-built tools — Claude self-routes reliably |
| Model selection | none (agy default only) | per-tool routing across all `agy models`, with availability detection and fallback |
| Multi-turn | stateless | session continuity — `follow_up` resumes agy conversations without resending context |
| Output safety | unbounded | configurable truncation cap protects Claude's context |
| Sandbox | no | optional `--sandbox` mode |
| Install | uvx (Python) | npx (Node) — zero install |

## Requirements

Expand All @@ -64,20 +64,20 @@ curl -o CLAUDE.md https://raw.githubusercontent.com/sshahzaiib/agy-bridge/main/C
> The `"timeout": 600000` (10 min, milliseconds) is the **client-side** tool-call
> deadline — without it, a cold-start `analyze_files` (~40–50s) or a long
> `delegate` can hit Claude Code's default and return `timed out waiting for
> response` while the agy run is still going. If your client doesn't honor a
response` while the agy run is still going. If your client doesn't honor a
> per-server `timeout`, set the global env var `MCP_TOOL_TIMEOUT=600000` instead.
> Details and the agy-side budgets are in [Timeouts and cancellation](#timeouts-and-cancellation).

## Tools

| Tool | Use for | Model routing (first available) |
|---|---|---|
| `analyze_files` | Files >200 lines, >3 files at once, logs, dumps, generated code | Gemini 3.5 Flash (High) → Gemini 3.1 Pro (Low) |
| `deep_search` | git log/diff/blame archaeology, repo-wide greps | Gemini 3.5 Flash (Medium) → (High) |
| `web_lookup` | Docs, API references, external/current knowledge | Gemini 3.5 Flash (Medium) → (High) |
| `adversarial_review` | Plan critiques, design and code reviews | Gemini 3.1 Pro (High) → Claude Opus 4.6 (Thinking) → Flash (High) |
| `follow_up` | Continue a prior session by `session_id` — no context resend | inherits the session |
| `delegate` | Anything else heavy | Gemini 3.5 Flash (High) |
| Tool | Use for | Model routing (first available) |
| -------------------- | --------------------------------------------------------------- | ----------------------------------------------------------------- |
| `analyze_files` | Files >200 lines, >3 files at once, logs, dumps, generated code | Gemini 3.5 Flash (High) → Gemini 3.1 Pro (Low) |
| `deep_search` | git log/diff/blame archaeology, repo-wide greps | Gemini 3.5 Flash (Medium) → (High) |
| `web_lookup` | Docs, API references, external/current knowledge | Gemini 3.5 Flash (Medium) → (High) |
| `adversarial_review` | Plan critiques, design and code reviews | Gemini 3.1 Pro (High) → Claude Opus 4.6 (Thinking) → Flash (High) |
| `follow_up` | Continue a prior session by `session_id` — no context resend | inherits the session |
| `delegate` | Anything else heavy | Gemini 3.5 Flash (High) |

All tools accept optional `cwd` (project root) and `model` (exact name from `agy models`; validated, with available models listed on mismatch).

Expand Down Expand Up @@ -107,24 +107,24 @@ Failovers are annotated in the response footer (`failover: <model>: quota exhaus

Each tool has its own default timeout sized to its job: `web_lookup` 120s, `deep_search` 180s, `analyze_files` / `adversarial_review` / `follow_up` 300s, `delegate` 600s. Setting `AGY_TIMEOUT` explicitly overrides all of them at once. To change a single tool, set `AGY_TIMEOUT_<TOOL_NAME>` instead (e.g. `AGY_TIMEOUT_DEEP_SEARCH=300`); a per-tool override takes precedence over the global `AGY_TIMEOUT` and the tool's default. The full set of per-tool variables is `AGY_TIMEOUT_ANALYZE_FILES`, `AGY_TIMEOUT_DEEP_SEARCH`, `AGY_TIMEOUT_WEB_LOOKUP`, `AGY_TIMEOUT_ADVERSARIAL_REVIEW`, `AGY_TIMEOUT_FOLLOW_UP`, and `AGY_TIMEOUT_DELEGATE`. The kill path escalates SIGTERM → SIGKILL across the whole process group, and the deadline fires even if agy's helper processes hold the output pipes open. Cancelling the tool call from the MCP client (e.g. pressing Esc in Claude Code) also kills the agy run instead of orphaning it.

**Two timeout layers — align them.** The timeouts above are the *agy-side* budget. Your MCP client (Claude Code) has its own, separate *tool-call* timeout, and if it is shorter than the agy budget the client gives up first — you'll see `Error: timed out waiting for response` (note: agy-bridge's own timeout reads `agy timed out after Ns` instead). The work is not lost: the agy session persists, so `follow_up` with the returned `session_id` retrieves the result. But the real fix is to make the client wait at least as long as agy: the [Install](#install) command already sets a per-server `timeout` of 600000ms (scoped to the agy-bridge entry only). If you registered the server without it, re-run the `add-json` command from Install, or set the global env var `MCP_TOOL_TIMEOUT=600000`. Rule of thumb: **client `timeout` ≥ agy budget**.
**Two timeout layers — align them.** The timeouts above are the _agy-side_ budget. Your MCP client (Claude Code) has its own, separate _tool-call_ timeout, and if it is shorter than the agy budget the client gives up first — you'll see `Error: timed out waiting for response` (note: agy-bridge's own timeout reads `agy timed out after Ns` instead). The work is not lost: the agy session persists, so `follow_up` with the returned `session_id` retrieves the result. But the real fix is to make the client wait at least as long as agy: the [Install](#install) command already sets a per-server `timeout` of 600000ms (scoped to the agy-bridge entry only). If you registered the server without it, re-run the `add-json` command from Install, or set the global env var `MCP_TOOL_TIMEOUT=600000`. Rule of thumb: **client `timeout` ≥ agy budget**.

**Expected latency.** Most of the perceived "slowness" is cold start: the first call in a session spawns the agy CLI and warms the model. A simple `analyze_files` over 3 files measures around **40–50s cold** (≈46s observed), dropping on subsequent same-session calls. A first call that also hits a quota 429 takes longer while the bridge fails over. So a client timeout below ~60s will intermittently trip on cold starts even for "simple" questions — size it generously.

## Configuration

All optional, via environment variables:

| Variable | Default | Description |
|---|---|---|
| `AGY_PATH` | `agy` | Path to the agy binary |
| `AGY_TIMEOUT` | per-tool | Seconds; overrides all per-tool timeouts at once (see above), passed as `--print-timeout`, enforced with a 15s kill grace |
| `AGY_TIMEOUT_<TOOL>` | per-tool | Seconds; overrides the timeout for a single tool only, e.g. `AGY_TIMEOUT_DEEP_SEARCH=300`. Wins over `AGY_TIMEOUT` |
| `AGY_MAX_OUTPUT_CHARS` | `50000` | Truncation cap for tool output |
| `AGY_DEFAULT_MODEL` | unset | Fallback model when no chain entry is available |
| `AGY_SKIP_PERMISSIONS` | `true` | Pass `--dangerously-skip-permissions` to agy |
| `AGY_SANDBOX` | `false` | Run agy with `--sandbox` |
| `AGY_ON_FAILURE` | `fallback` | `strict` appends an instruction to failed-tool errors telling the calling agent not to absorb the work itself |
| Variable | Default | Description |
| ---------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------- |
| `AGY_PATH` | `agy` | Path to the agy binary |
| `AGY_TIMEOUT` | per-tool | Seconds; overrides all per-tool timeouts at once (see above), passed as `--print-timeout`, enforced with a 15s kill grace |
| `AGY_TIMEOUT_<TOOL>` | per-tool | Seconds; overrides the timeout for a single tool only, e.g. `AGY_TIMEOUT_DEEP_SEARCH=300`. Wins over `AGY_TIMEOUT` |
| `AGY_MAX_OUTPUT_CHARS` | `50000` | Truncation cap for tool output |
| `AGY_DEFAULT_MODEL` | unset | Fallback model when no chain entry is available |
| `AGY_SKIP_PERMISSIONS` | `true` | Pass `--dangerously-skip-permissions` to agy |
| `AGY_SANDBOX` | `false` | Run agy with `--sandbox` |
| `AGY_ON_FAILURE` | `fallback` | `strict` appends an instruction to failed-tool errors telling the calling agent not to absorb the work itself |

### Failure behavior

Expand Down
18 changes: 9 additions & 9 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ description: Use when analyzing large files (>200 lines), more than 3 files at o

Use the agy-bridge MCP tools instead of doing heavy work yourself:

| Situation | Tool |
|---|---|
| File >200 lines, logs, dumps, generated code | `analyze_files` |
| >3 files in one task | `analyze_files` |
| Git history / repo-wide search | `deep_search` |
| Docs or external knowledge | `web_lookup` |
| Plan critique, code review | `adversarial_review` |
| Follow-up on a prior delegation | `follow_up` (use the returned session id) |
| Anything else heavy | `delegate` |
| Situation | Tool |
| -------------------------------------------- | ----------------------------------------- |
| File >200 lines, logs, dumps, generated code | `analyze_files` |
| >3 files in one task | `analyze_files` |
| Git history / repo-wide search | `deep_search` |
| Docs or external knowledge | `web_lookup` |
| Plan critique, code review | `adversarial_review` |
| Follow-up on a prior delegation | `follow_up` (use the returned session id) |
| Anything else heavy | `delegate` |

Every response ends with `[agy-bridge] model: … | session: …`. Reuse the
session id with `follow_up` for iterative work — the context stays on agy's
Expand Down
2 changes: 1 addition & 1 deletion docs/multi-client-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ CLIs backed by the same Gemini models (and typically the same account/quota).
Delegating from Gemini CLI through agy-bridge routes Gemini work to Gemini
through an extra hop:

- No second model family for `adversarial_review` (agy *can* route reviews to
- No second model family for `adversarial_review` (agy _can_ route reviews to
Claude Opus, but a Gemini CLI user has that in agy directly).
- Context offloading is a weak argument when the user could run agy as their
primary CLI instead.
Expand Down
Loading