Files
darman 420f00494f M0: scaffold Cargo workspace, core types, event bus, permission engine, storage actor
Sets up the Cargo workspace (harness-core, harness-tools, harness-providers, harness-mcp, harness-lsp, harness-app, harness-tui) and the ten architecture docs under docs/. harness-core gets its foundational types (session/message/part/model ids), an in-process event bus, a table-driven permission evaluate function, and a SQLite-backed storage actor with schema and roundtrip coverage. Every crate compiles empty and a bin stub prints its version.
2026-07-10 16:19:27 +02:00

3.5 KiB

05 — Built-in Tools

harness-tools. All parameter structs derive schemars::JsonSchema (single source of truth for the schema sent to the model). Invalid-params errors are returned to the model as tool errors with the schema echoed (opencode's invalid pattern).

Shared: output truncation

truncate.rs: cap output at 30,000 chars; keep head + tail with ... [N chars truncated; full output: <data_dir>/tool-output/<call_id>.txt] ...; full output spilled to the session data dir.

Tool table

Tool Params Behavior Permission key / pattern
bash {command, timeout_ms?, cwd?, description} tokio::process::Command sh -c, own process group (setsid); kill group on cancel/timeout (default 2 min, max 10); merge stdout+stderr; truncate bash / command string; "always" pattern = <first word> * via shell-words
read {file_path, offset?, limit?} 2000-line default window, cat -n numbering, 2000-char line clamp, binary/image detection; reports context files to the JobBoard when in a child session read / worktree-relative path (bundled default: allow)
edit {file_path, old_string, new_string, replace_all?} replacer chain (below); CRLF/BOM preservation; per-file tokio::Mutex lock map; unified diff via similar in permission metadata + part metadata; LSP diagnostics appended edit / relative path; always-pattern *
write {file_path, content} mkdir -p; diff vs existing in metadata; LSP diagnostics edit (same key as opencode) / relative path
glob {pattern, path?} ignore::WalkBuilder (respects .gitignore) + globset; sort mtime desc; cap 100 none (read-only)
grep {pattern, path?, include?} grep-regex + grep-searcher, gitignore-aware; cap 100 matches, sorted by file mtime none
todowrite / todoread list of {content, status: pending|in_progress|completed, priority} stored on the session row (JSON); part metadata drives a TUI checklist none
webfetch {url, format: text|markdown|html, timeout?} reqwest GET (redirect cap 5), 5 MB cap, HTML→markdown via htmd, truncate webfetch / URL
task see 04-multiagent.md spawn/reuse subagent sessions task / agent name
skill {name} returns the SKILL.md body on demand skill / name (default allow)

MCP tools are registered alongside these, namespaced {server}_{tool} — see 09-integrations.md.

Edit tool: replacer chain

Port opencode's replacer chain verbatim from ~/repos/opencode/packages/opencode/src/tool/edit.ts, including its test table (edit.test.ts) — this is the highest-regression-risk code in the project:

  1. Simple (exact match)
  2. LineTrimmed
  3. BlockAnchor (first/last line anchors, ≥0.65 similarity on middle lines, Levenshtein)
  4. WhitespaceNormalized
  5. IndentationFlexible
  6. EscapeNormalized
  7. TrimmedBoundary
  8. ContextAware
  9. MultiOccurrence (with replace_all)

Plus the disproportionate-match guard (reject matches that differ too much in size from old_string).

Registry & agent filtering

ToolRegistry::for_agent(agent):

  1. Filter by the agent's tools wildcard map ({ "write": false, "mcp_*": false }).
  2. Hard-remove tools denied for the session depth (e.g. task, todowrite at depth > 0 by default).
  3. The task tool's description is generated dynamically, listing available subagents with their descriptions (so routing knowledge comes from agent frontmatter, not hardcoded text).