Files
openidea/Cargo.toml
l0ng-ai 2eaa6cb0c7 feat(agents): Dispatch and supervise coding agents over ACP (#72)
## What

An Agents feature: hand a task to a coding agent and follow it from the IDE. Agents speak the [Agent Client Protocol](https://agentclientprotocol.com). Built in: Claude Code, Codex, Gemini CLI, GitHub Copilot, Cursor, OpenCode, Qwen Code, Kimi CLI, Goose, Auggie, Kiro, Mistral Vibe. You can add your own in `<config>/agents/<id>.toml` (`name`, `command`, `args`, `[env]`). An agent whose command isn't on `PATH` is listed under "Not installed".

- **New Task page.** An editor page with a prompt box. You pick the agent, model, mode and thinking effort, and whether it runs in a new worktree (from a chosen branch) or in the checkout.
- **Agents tool window.** Tasks are grouped by what they need: Needs Input, Failed, Running, Ready to Review, Archived. It also shows a stripe badge, a status bar item and notifications.
- **Session tab.**
  - The conversation: Markdown, thinking, tool calls with diff stats, the plan.
  - Permission requests appear as an editor banner.
  - The changed files are compared against the task's base commit and open in the diff viewer.
  - A context-usage meter.
- **Composer.**
  - `@` references files from the project index. A referenced file's text is sent when the agent accepts embedded context, otherwise a `resource_link`.
  - `/` lists the agent's commands; ↑/↓ recall earlier messages.
  - Images can be pasted, dropped or picked; Shift+Tab cycles the mode.
  - While the agent works, Enter queues the message and ⌘↵ interrupts the turn and sends it now. Queued messages can be sent now, edited or removed.
- **Send to Agent.** In the editor and Project view context menus. It adds `@file:10-20` references to the message you typed last.
- Landing the work is left to the existing Git features: the worktree is a normal branch.

## How

- **`crates/ide-agent`**: one ACP agent process per task, running in the task's directory.
  - It folds `session/update` into a timeline and answers `session/request_permission`.
  - It tracks the session's settings: `configOptions`, falling back to the older `modes` / `models` fields. The user's choices are applied again on every new or reloaded session.
  - Tasks are saved as JSON per task. Sessions resume with `session/load` without duplicating history, and old task files are upgraded when read.
- **`ide-jsonrpc`**: adds newline-delimited framing (ACP) next to Content-Length (LSP).
- **`ide-vcs`**: adds `numstat_with_worktree` for the changes list.
- **Editor**:
  - The Switcher (⌃⇥) lists every open editor, as IDEA does, not just files.
  - `EditorKindContribution.restorable` lets a kind's tabs be saved with the editor session, so agent tabs survive reloads.
- **UI**: `Select` popups now size to fit short option details (they used to cut them off).

## Testing

- `cargo test -p ide-agent` (14 tests). They use an in-memory ACP agent and cover permissions, restart with `session/load`, settings, the queue with send-now and stop, and prompt blocks.
- There is also an ignored end-to-end test against the real Claude Code adapter: `OPENIDEA_AGENT_E2E=1 cargo test -p ide-agent real_claude -- --ignored`.
- `pnpm vitest run`: 2052 tests.
- `cargo clippy -p ide-agent -p openidea --all-targets -- -D warnings` and typecheck are clean.
- Driven in the running app with Claude Code:
  - A task in a worktree, with permission answers, changes listed and the diff opened.
  - A follow-up after restarting the IDE.
  - `@` reference with embedded content, `/` commands, Shift+Tab mode changes.
  - A queued message and send-now, an image prompt, Send to Agent from a selection.
  - The Switcher and restored tabs after a reload.

Not covered yet: only Claude Code was driven end to end. The other agents go through the same ACP path but weren't run here.


Merged on the maintainer's go-ahead. Merge with main: kept both sides in Cargo.toml (ide-agent + ide-clipboard), the feature registry (agents + vim) and zh.json (no duplicate keys). After the merge: typecheck clean, full vitest 268 files / 2247 tests passed, ide-agent/ide-jsonrpc/ide-vcs 144 passed, bindings unchanged after regeneration.
2026-10-27 14:44:28 +08:00

109 lines
3.0 KiB
TOML

[workspace]
resolver = "3"
members = ["crates/*", "src-tauri"]
[workspace.package]
version = "0.1.15"
edition = "2024"
rust-version = "1.96"
license = "Apache-2.0"
authors = ["OpenIDEA contributors"]
[workspace.dependencies]
# internal
ide-core = { path = "crates/ide-core" }
ide-vfs = { path = "crates/ide-vfs" }
ide-text = { path = "crates/ide-text" }
ide-language = { path = "crates/ide-language" }
ide-index = { path = "crates/ide-index" }
ide-syntax = { path = "crates/ide-syntax" }
ide-search = { path = "crates/ide-search" }
ide-jsonrpc = { path = "crates/ide-jsonrpc" }
ide-lsp = { path = "crates/ide-lsp" }
ide-dap = { path = "crates/ide-dap" }
ide-vcs = { path = "crates/ide-vcs" }
ide-terminal = { path = "crates/ide-terminal" }
ide-run = { path = "crates/ide-run" }
ide-workspace = { path = "crates/ide-workspace" }
ide-sdk = { path = "crates/ide-sdk" }
ide-build = { path = "crates/ide-build" }
ide-analysis = { path = "crates/ide-analysis" }
ide-coverage = { path = "crates/ide-coverage" }
ide-profiler = { path = "crates/ide-profiler" }
ide-http = { path = "crates/ide-http" }
ide-db = { path = "crates/ide-db" }
ide-web = { path = "crates/ide-web" }
ide-spell = { path = "crates/ide-spell" }
ide-agent = { path = "crates/ide-agent" }
ide-clipboard = { path = "crates/ide-clipboard" }
# serialization & IPC typing
serde = { version = "1", features = ["derive"] }
serde_json = "1"
toml = "0.9"
toml_edit = "0.25"
specta = { version = "=2.0.0-rc.25", features = ["derive"] }
# async & concurrency
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time", "process", "io-util", "fs"] }
tokio-util = { version = "0.7", features = ["rt"] }
parking_lot = "0.12"
rayon = "1"
dashmap = "6"
futures = "0.3"
# errors & diagnostics
thiserror = "2"
anyhow = "1"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
# text & fs
ropey = "1.6"
encoding_rs = "0.8"
chardetng = "0.1"
ignore = "0.4"
globset = "0.4"
notify = "8"
notify-debouncer-full = "0.6"
trash = "5"
blake3 = "1"
dunce = "1"
dirs = "6"
# search, index, language
grep-searcher = "0.1"
grep-regex = "0.1"
grep-matcher = "0.1"
nucleo-matcher = "0.3"
redb = "4"
tree-sitter = "0.25"
# protocols & integrations
lsp-types = "0.97"
gix = { version = "0.89", default-features = false }
portable-pty = "0.9"
reqwest = { version = "0.13", default-features = false, features = ["rustls-no-provider", "http2", "charset", "gzip", "deflate", "brotli"] }
rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12", "logging"] }
rustls-platform-verifier = "0.7"
sqlx = { version = "0.9", default-features = false, features = ["runtime-tokio", "tls-rustls-ring-webpki", "sqlite", "postgres", "mysql"] }
uuid = { version = "1", features = ["v4"] }
# testing
insta = { version = "1", features = ["yaml"] }
proptest = "1"
tempfile = "3"
[profile.dev]
# Keep debug builds snappy while giving dependencies decent speed.
opt-level = 0
[profile.dev.package."*"]
opt-level = 2
[profile.release]
codegen-units = 1
lto = true
opt-level = 3
panic = "abort"
strip = true