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.
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:
- Simple (exact match)
- LineTrimmed
- BlockAnchor (first/last line anchors, ≥0.65 similarity on middle lines, Levenshtein)
- WhitespaceNormalized
- IndentationFlexible
- EscapeNormalized
- TrimmedBoundary
- ContextAware
- 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):
- Filter by the agent's
toolswildcard map ({ "write": false, "mcp_*": false }). - Hard-remove tools denied for the session depth (e.g.
task,todowriteat depth > 0 by default). - The
tasktool's description is generated dynamically, listing available subagents with their descriptions (so routing knowledge comes from agent frontmatter, not hardcoded text).