core

package
v0.21.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 27, 2026 License: MIT Imports: 49 Imported by: 0

Documentation

Overview

Package core — AgentNew MCP tool. Mirrors `clawtool agent new` so a model can scaffold a Claude Code subagent persona from inside a conversation. Both surfaces share the same template renderer (internal/agentgen) so the output is byte-identical.

Terminology reminder (operator's 2026-04-27 ruling):

  • **agent** = a USER-DEFINED PERSONA (this tool scaffolds one)
  • **instance** = a configured upstream CLI bridge (claude / codex / gemini / opencode / hermes / openclaw / ...)

Don't confuse this with the legacy AgentList tool (agents_tool.go), which currently still surfaces *instances* under the legacy "agent" name. That rename is tracked separately.

Package core — SendMessage and AgentList MCP tools (ADR-014 Phase 1).

SendMessage routes a prompt to the resolved agent's transport and buffers the streaming reply for the MCP response. Full HTTP-grade streaming arrives with `clawtool serve` in Phase 2; the MCP wire here is request/response so we accept the buffer cap.

AgentList exposes the supervisor's registry snapshot — same shape as `clawtool send --list` and `GET /v1/agents`. Mirrors the v0.9 `RecipeList` pattern (read-only, structured, BaseResult-shaped).

Package core — atomic.go: shared file-mutation primitives for Edit and Write. These helpers guarantee no partial-write artifacts on crash and give us a single place to add safety polish (line-ending preservation, BOM handling) consistently across both tools.

Per ADR-007 we do not use a third-party "atomic write" library — Go's stdlib (`os.WriteFile` + `os.Rename`) is enough when used correctly. The platform invariant we rely on: rename(2) on the same filesystem is atomic, so writers never see a half-written file at the target path.

Package core implements clawtool's canonical core tools per ADR-005.

Quality bar reminder: each core tool must measurably beat the corresponding native built-in across the major agents. For Bash:

  • timeout-safe: output is captured even when the process is killed
  • predictable cwd: defaults to $HOME, never the daemon's cwd
  • structured result: stdout/stderr/exit_code/duration_ms/timed_out returned as JSON even on timeout

v0.1 implements timeout-safe + structured result + cwd. Secret redaction and per-session command history land in v0.2.

Package core — Bash background-mode task registry (ADR-021 phase B, Codex's "long-running" recommendation). Mirrors BIAM's task vocabulary (pending / active / done / failed / cancelled) without reusing the SQLite store: bash subprocess output is volatile, signing every stdout chunk via Ed25519 (which BIAM would do) is the wrong default. Process-local in-memory registry, lifetime = clawtool serve process.

Package core — MCP surface for Bash background tasks. The underlying registry is in bash_bg.go; this file is the wiring layer mapping {BashOutput, BashKill} onto Get/Kill helpers and rendering the snapshot under the standard core-tool envelope.

Package core — Bridge* MCP tools (ADR-014 Phase 1).

Mirrors `clawtool bridge add/list/remove/upgrade` over MCP so a model can install / inspect / uninstall bridges mid-conversation ("kanka gemini bridge'i kur"). Same dispatch path as the CLI — both end up calling setup.Apply on the bridge's recipe.

Package core — BrowserFetch retrieves a URL through a real browser engine (Obscura, Chromium-via-CDP) so SPA / JS-rendered content lands in the agent's context. Sister tool to WebFetch (server-side via Mozilla Readability), which can't render React / Next.js / hydrated SPAs.

Per ADR-007 we wrap mature engines: Obscura (V8 + Chrome DevTools Protocol, Apache 2.0). We never re-implement page loading. clawtool adds: agent-friendly polish (size cap, structured result, optional JS evaluator, optional CSS-selector wait, post-render readability pass for clean prose).

Stateless: each call spins a fresh browser context. For interactive multi-step flows (login + cookie + click + capture) use BrowserAction.

Package core — BrowserScrape parallelises BrowserFetch across many URLs by wrapping `obscura scrape <url...> --concurrency N --eval ... --format json`. Use case: "give me the rendered headline from these 50 SPA blog posts", "bulk-snapshot a competitor's site map", etc.

Per ADR-007 we wrap Obscura's scrape subcommand (Apache-2.0 Rust engine, V8 + CDP) — clawtool never re-implements parallel fetching. Stateless: each URL gets its own browser context, no cookies, no shared session. For interactive work use BrowserAction.

Package core — Commit MCP tool. Wraps internal/checkpoint's Commit primitive (ADR-022) so an agent can land a Conventional Commits-validated, Co-Authored-By-blocked commit through one tool call instead of three Bash invocations.

This tool is what closes the operator's earlier gap: agents shell out to `Bash git commit -m "feat: …"` because there's no Commit tool, the messages aren't always conventional-shaped, and Bash has no way to refuse a Co-Authored-By trailer. Commit makes the right path the easy path.

Pre-commit guardrails layered through (in order):

  1. Repo check — bails with a clear error if cwd isn't a Git repo.
  2. internal/rules.Evaluate at EventPreCommit — operator's declarative invariants (e.g. "skill routing-map row updated" when a core tool changed). A Verdict.IsBlocked() = true is a hard refusal.
  3. internal/checkpoint.ValidateMessage — Conventional Commits + Co-Authored-By block.
  4. Optional dirtiness guard — refuses to commit if the working tree still has unstaged changes after staging (catches "you forgot to stage X" mid-flight).

Package core — Edit performs a precise search-and-replace on an existing file with safety polish:

  • uniqueness check by default (refuses ambiguous edits)
  • atomic temp+rename so a crash never leaves a half-written file
  • line-ending preserve (LF / CRLF / CR detected from current content)
  • BOM preserve
  • binary refusal (symmetric with Read)

Per ADR-007 we wrap stdlib `os` for I/O and add our own polish layer. The search-and-replace shape mirrors what Claude Code's native Edit uses today (old_string / new_string / replace_all) — agents that learnt that interface get the same affordances against clawtool's stronger invariants.

Package core — Glob is the canonical wrapper around bmatcuk/doublestar.

Per ADR-007 we don't write a glob engine. doublestar is the de-facto double-star (`**`) glob library in Go, used by GoReleaser, k6, etc. This file's value is the polish layer: cwd-aware path resolution, uniform structured output, hard cap to protect agent context, and platform-stable separators (the wrapper always returns forward-slash paths regardless of OS — agents expect that).

ADR-021 phase B added .gitignore-aware traversal — when cwd is a Git worktree we ask `git ls-files --cached --others --exclude-standard -z` for the candidate set then run doublestar over it, which gives us the same ignore semantics as ripgrep (and keeps the operator's expected ".git/, vendor/, node_modules/ ignored by default" behaviour).

Package core — Grep wraps ripgrep when present, falls back to system grep.

Per ADR-007 we curate ripgrep as the default engine: it has the correct .gitignore semantics, --type aliases, fast Aho-Corasick matching, and a stable JSON output that is straightforward to parse. The fallback is system grep (-rn) so clawtool's Grep is never unavailable.

Output is uniform across engines so the agent never has to know which one ran. The `engine` field in the result lets users / tests verify.

Package core — typed manifest of clawtool's MCP tools (#173 Step 2 of Codex's #1 ROI refactor).

BuildManifest assembles a *registry.Manifest with one ToolSpec per shipped tool. Step 2 (this commit) ONLY adds entries for the youngest six tools — Commit, RulesCheck, AgentNew, BashOutput, BashKill, TaskNotify. server.go still calls each tool's RegisterX directly; the manifest is not yet consumed at boot. That hookup lands in Step 3, after the older tools (Bash / Read / Edit / Write / Grep / Glob / WebFetch / WebSearch / ToolSearch) get the same treatment.

Why incremental: a single big-bang manifest migration carries the risk that one register-fn signature mismatch (or one missed gate) breaks every tool at once. Doing it six tools at a time, with the surface_drift_test guarding cross-plane invariants, makes each step audit-able and rollback-able.

Why the youngest first: they have the freshest test coverage and the smallest blast radius if a migration mistake slips through. By the time we reach the older core (Bash / Read / Edit / Write) the registry harness is battle-tested.

Package core — Mcp* MCP tools (ADR-019). v0.17 fills in `McpNew` (real generator wrapper), `McpList` (real walker), and keeps thin stubs for `McpRun` / `McpBuild` / `McpInstall` that point at the CLI shortcut (those are inherently filesystem-side operations the model doesn't usually drive).

Package core — Portal* MCP tools (ADR-018). Read-only surface in v0.16.1: PortalList, PortalUse, PortalWhich, PortalUnset, PortalRemove, plus a deferred-feature stub for PortalAsk so the shape is discoverable before the v0.16.2 CDP driver lands.

PortalAdd is intentionally CLI-only — it spawns $EDITOR which has no meaning in an MCP context. Operators add portals from the terminal; agents discover and use them through MCP.

Package core — Read returns file content with stable line cursors, deterministic line counts, and per-format dispatch.

Per ADR-007 we wrap mature engines instead of writing parsers:

  • text → stdlib bufio (line-walked, single-pass)
  • pdf → pdftotext (poppler-utils) shell-out
  • ipynb → native JSON cell parse
  • docx → pandoc shell-out (universal office converter)
  • xlsx → github.com/xuri/excelize/v2 (Microsoft/Alibaba/Oracle in prod)
  • csv / tsv → stdlib encoding/csv (header + bounded preview)
  • html → github.com/go-shiori/go-readability (Mozilla Readability port)
  • json / yaml / toml / xml → text passthrough with format tag
  • binary → refused with structured error

Adding a new format means: extend detectFormat, add a reader function, update CoreToolDocs's description, ship tests for the format. The readResult shape stays uniform so the agent never has to branch on which engine ran.

Package core — Recipe* MCP tools mirror the `clawtool recipe …` CLI surface so a model can run setup tasks from inside a conversation. Same registry, same Detect/Apply/Verify cycle.

Three tools per ADR-013:

RecipeList   — enumerate recipes, optionally filter by category.
RecipeStatus — Detect output for one recipe or all of them.
RecipeApply  — full Detect→Prereqs→Apply→Verify against a repo,
               with structured options.

All three return BaseResult-shaped output (pretty text + structured JSON) so chat UIs render them the same way as Bash/Read/etc.

Package core — RulesCheck MCP tool. Surfaces the rules engine (internal/rules) so an agent can ask "are the operator's invariants satisfied right now?" without first having to call the unattended-mode supervisor or wait for pre_commit time.

This tool is read-only: it loads .clawtool/rules.toml (or the XDG fallback), evaluates against a caller-supplied Context, and returns the Verdict (results + warnings + blocked). It does NOT hook into Edit/Write/Bash automatically — rule enforcement at tool-call time lands when the Tool Manifest Registry refactor (#173) gives us a middleware seam.

Package core — Sandbox* MCP tools (ADR-020). v0.18 ships the read-only surface (List / Show / Doctor) so models can discover the profile catalog and recommend the right one to operators. SandboxRun is intentionally CLI-only — letting a model spawn sandboxed commands has the wrong default.

Package core — SemanticSearch MCP tool (ADR-014 T6, design from the 2026-04-26 multi-CLI fan-out).

Concept queries ("how is auth rotated?") that Grep can't reach because the literal token isn't there. We wrap chromem-go's in-memory vector store + the configured embedding provider (OpenAI default, Ollama override). One Store per repo, lazily built on first Search call so cold-boot doesn't pay the embedding cost when the tool isn't being used.

Coexistence with Grep: Grep stays the literal regex tool; this is the conceptual one. Tool descriptions carry the routing hint so ToolSearch ranks each correctly per query.

Package core — session-scoped read tracking for the Read-before-Write guardrail (ADR-021). MCP session id is the key; we look it up via server.ClientSessionFromContext, never from a tool argument (Codex flagged this — model-supplied session ids can't be trusted).

Package core — file-IO helpers extracted to keep session_state.go pure. Mirrors the read.go IO surface but stays small enough to embed.

Package core — SkillNew MCP tool. Mirrors `clawtool skill new` so a model can scaffold a Claude Code skill from inside a conversation, without the user dropping to a shell. Both surfaces share the same template renderer (passed in at registration so we don't reach across packages — this file stays a leaf).

Spec compliance: agentskills.io. SKILL.md gets YAML frontmatter with `name` + `description` (required) plus optional `triggers`, followed by the body template. Same shape as the CLI emits.

Package core — TaskNotify MCP tool. Edge-triggered completion push that pairs with SendMessage(bidi=true). Subscribes to the in-process biam.Notifier so the caller wakes the instant ANY of the watched tasks reaches a terminal state — no SQLite poll, no external CLI hooks.

Architecture: the runner publishes a *biam.Task to Notifier when it flips a row to a terminal state (see internal/agents/biam/ runner.go). Here we register one channel per task_id, then `select` across all of them + the timeout context. First task wins; the rest stay subscribed until the caller polls them with TaskGet (their slot decays at next Publish or process exit).

Already-terminal tasks: we eagerly check the store BEFORE blocking, so a TaskNotify call against a task that already finished returns immediately rather than waiting for a Publish that already happened.

Package core — TaskGet / TaskWait / TaskList MCP tools (ADR-015 Phase 1). Surface the BIAM SQLite store the supervisor's async runner persists into, so a calling model can:

  1. Fire SendMessage with bidi=true → receive task_id immediately.
  2. Continue its own work without blocking on the upstream.
  3. Pull back via TaskGet (snapshot) / TaskWait (block until terminal) when it actually needs the result.

All three tools are read-only and stateless beyond the BIAM store.

Package core — ToolSearch is the discovery primitive that makes a 50+ tool catalog usable per ADR-005. The agent calls ToolSearch with a natural-language query, gets ranked candidates, then binds to the right tool with its full schema via the regular tools/list output.

Package core — Verify MCP tool (ADR-014 T4, design from the 2026-04-26 multi-CLI fan-out).

Verify runs a repo's tests / lints / typechecks via whichever runner the repo declares (Make, pnpm, npm, go, pytest, ruby, cargo, just) and returns one structured pass/fail per check. Per ADR-007 we wrap maintained runners — `go test -json`, `pytest --json-report`, `cargo test --message-format json` — and fall back to the runner's plain output when the structured form isn't available on this host.

Buffered single payload (not stream): callers want the full pass/fail summary, not the live log fire hose. Bash already streams when that's what's wanted.

Package core — WebFetch retrieves a URL and renders the body in the best shape for an AI agent: clean article text for HTML, raw text for plain-text MIME types, structured rejection for binaries.

Per ADR-007 we wrap two mature engines:

  • net/http stdlib client for transport (proxy, TLS, redirect handling are all stock — battle-tested across Go's user base);
  • github.com/go-shiori/go-readability for HTML extraction (Mozilla Readability port, the same algorithm Firefox Reader View ships).

What clawtool adds: agent-friendly polish — a hard body cap so a runaway page can't blow context, structured result with content-type-aware `format`, citation metadata (final URL after redirects, fetched_at).

Package core — SSRF guard for WebFetch (ADR-021 phase B).

Without this, an agent could ask WebFetch for `http://169.254.169.254/` (AWS metadata), `http://localhost:5432/` (the operator's local Postgres), or any RFC1918 address (the operator's internal network). The guard blocks resolution to those address ranges BEFORE the GET is issued, and re-checks every redirect target so a public 302→private redirect chain is rejected too.

Per ADR-007 we don't ship our own DNS resolver — net.LookupIP is canonical. We only own the address-range allow/deny logic.

Package core — WebSearch is a pluggable web-search primitive. clawtool itself does no crawling or ranking; it adapts whichever search backend the user has configured (Brave today; Tavily / SearXNG planned) into a uniform `{results, backend, …}` shape.

Per ADR-007 we wrap, never reinvent. The backend interface is small on purpose so adding a new provider is one file (see websearch_brave.go for the canonical example).

Package core — Write creates or overwrites a whole file with given content. Sister tool to Edit; the boundary is intentional:

  • Edit modifies a substring of an existing file. Refuses if the file is missing.
  • Write replaces or creates the whole file. If the file already exists and has detectable line endings, those endings are preserved by default so existing tooling (CR LF on Windows, etc.) keeps working.

Per ADR-007 we wrap stdlib `os` for I/O. The polish layer is the same atomic-write primitive Edit uses, plus a parent-directory auto-create so agents don't need a separate `mkdir` step before writing.

Index

Constants

This section is empty.

Variables

View Source
var BashTasks = &BashTaskStore{tasks: map[string]*BashTask{}}

BashTasks is the singleton. Tests use ResetBashTasksForTest.

View Source
var ErrBlockedAddress = errors.New("WebFetch refused: target resolves to a private / loopback / link-local / cloud-metadata address (SSRF guard, ADR-021)")

ErrBlockedAddress is the sentinel returned when the resolved IP falls into a deny range. Caller surfaces it verbatim.

View Source
var ErrMissingAPIKey = errors.New("missing API key")

ErrMissingAPIKey is returned by backends when their required API key is not present in either the secrets store or process env.

View Source
var Sessions = &SessionState{
	reads: map[SessionKey]map[string]ReadRecord{},
}

Sessions is the process-wide singleton. Tests reset via ResetSessionsForTest.

Functions

func BuildManifest added in v0.21.0

func BuildManifest() *registry.Manifest

BuildManifest returns the typed manifest of every clawtool MCP tool. Caller (server.go in Step 3) walks it via manifest.Apply(s, runtime, cfg.IsEnabled).

Step 2 scope: 6 specs (Commit, RulesCheck, AgentNew, BashOutput, BashKill, TaskNotify). Each spec's Register fn adapts the existing RegisterX(s) signature to the registry.RegisterFn shape (s, runtime).

Specs added but Register-not-wired-yet are LEGAL — Apply silently skips them. We use that to document the older tools in the same manifest BEFORE migrating them, so search-index consumers (Step 4 work) can already see the canonical entry.

func CoreToolDocs

func CoreToolDocs() []search.Doc

CoreToolDocs returns search.Doc descriptors for every clawtool core tool. Step 4 of #173 collapsed the duplicated entry list into a delegate over BuildManifest().SearchDocs(nil) so the manifest is now the single source of truth. Kept as a public shim so the surface_drift_test (which iterates by spec name) stays a one-liner; internal callers go to the manifest directly.

func HashFile added in v0.20.0

func HashFile(path string) (string, error)

HashFile returns SHA-256 of the file's raw bytes as hex. Helper used by Read / Write / Edit; centralised so the format stays consistent across tools.

func HashString added in v0.20.0

func HashString(s string) string

HashString computes SHA-256 of a string. Used for range_hash after format-aware decoding (PDF / DOCX / XLSX) so the hash captures the canonical text we returned to the agent, not the raw bytes.

func RegisterAgentNew added in v0.20.0

func RegisterAgentNew(s *server.MCPServer)

RegisterAgentNew adds the AgentNew tool to s. Template + helpers come from internal/agentgen so this MCP surface and the `clawtool agent new` CLI emit byte-identical files.

func RegisterAgentTools added in v0.20.0

func RegisterAgentTools(s *server.MCPServer)

RegisterAgentTools adds SendMessage + AgentList to the MCP server.

func RegisterBash

func RegisterBash(s *server.MCPServer)

RegisterBash adds the Bash tool to the given MCP server.

func RegisterBashKill added in v0.20.0

func RegisterBashKill(s *server.MCPServer)

RegisterBashKill exposes KillBashTask over MCP as BashKill. The snapshot is returned post-cancel so the caller sees the terminal status (or `cancelled` if the kill won the race against a quick exit).

func RegisterBashOutput added in v0.20.0

func RegisterBashOutput(s *server.MCPServer)

RegisterBashOutput exposes GetBashTask over MCP as BashOutput.

func RegisterBridgeTools added in v0.20.0

func RegisterBridgeTools(s *server.MCPServer)

RegisterBridgeTools adds BridgeList/Add/Remove/Upgrade to s.

func RegisterBrowserFetch added in v0.20.0

func RegisterBrowserFetch(s *server.MCPServer)

RegisterBrowserFetch wires the BrowserFetch MCP tool.

func RegisterBrowserScrape added in v0.20.0

func RegisterBrowserScrape(s *server.MCPServer)

RegisterBrowserScrape wires the BrowserScrape MCP tool.

func RegisterCommit added in v0.20.0

func RegisterCommit(s *server.MCPServer)

RegisterCommit wires the Commit MCP tool. Idempotent.

func RegisterEdit

func RegisterEdit(s *server.MCPServer)

RegisterEdit adds the Edit tool to the given MCP server.

func RegisterGlob

func RegisterGlob(s *server.MCPServer)

RegisterGlob adds the Glob tool to the given MCP server.

func RegisterGrep

func RegisterGrep(s *server.MCPServer)

RegisterGrep adds the Grep tool to the given MCP server.

func RegisterMcpTools added in v0.20.0

func RegisterMcpTools(s *server.MCPServer)

RegisterMcpTools wires the Mcp* surface (ADR-019). McpNew runs the real generator. McpList walks the on-disk markers. McpRun / McpBuild / McpInstall are CLI-side filesystem operations and surface a hint to use the shell command — that's the natural path for a model giving advice rather than driving the build.

func RegisterPortalAliases added in v0.20.0

func RegisterPortalAliases(s *server.MCPServer, cfg config.Config)

RegisterPortalAliases scans cfg.Portals and binds a thin wrapper `<name>__ask` for each one. Same wire-naming convention as internal/sources/manager.go aggregation. Each alias forwards to PortalAsk with the portal name pre-bound, so the calling model can do `my_deepseek__ask({"prompt":"..."})` without remembering the generic shape.

func RegisterPortalTools added in v0.20.0

func RegisterPortalTools(s *server.MCPServer)

RegisterPortalTools wires the Portal* MCP surface. Always registered; missing config produces empty results, not boot failure.

func RegisterRead

func RegisterRead(s *server.MCPServer)

RegisterRead adds the Read tool to the given MCP server.

func RegisterRecipeTools added in v0.9.0

func RegisterRecipeTools(s *server.MCPServer)

RegisterRecipeTools adds the three Recipe* MCP tools to s. The registry must already be populated (typically via blank import of internal/setup/recipes).

func RegisterRulesCheck added in v0.20.0

func RegisterRulesCheck(s *server.MCPServer)

RegisterRulesCheck wires the RulesCheck tool. Idempotent.

func RegisterSandboxTools added in v0.20.0

func RegisterSandboxTools(s *server.MCPServer)

func RegisterSemanticSearch added in v0.20.0

func RegisterSemanticSearch(s *server.MCPServer)

RegisterSemanticSearch wires the tool. Always registered; missing embedding key surfaces as a per-call error, not a boot failure.

func RegisterSkillNew added in v0.9.0

func RegisterSkillNew(s *server.MCPServer)

RegisterSkillNew adds the SkillNew tool to s. Template + helpers come from internal/skillgen so this MCP surface and the `clawtool skill new` CLI emit byte-identical output.

func RegisterTaskNotify added in v0.20.0

func RegisterTaskNotify(s *server.MCPServer)

RegisterTaskNotify wires the TaskNotify tool. Idempotent.

func RegisterTaskTools added in v0.20.0

func RegisterTaskTools(s *server.MCPServer)

RegisterTaskTools wires TaskGet / TaskWait / TaskList. Idempotent — safe to call when the BIAM store wasn't initialised; per-call handlers surface the "not configured" error.

func RegisterToolSearch

func RegisterToolSearch(s *server.MCPServer, idx *search.Index)

RegisterToolSearch adds the ToolSearch tool to the given MCP server, closing over the index built at startup.

func RegisterVerify added in v0.20.0

func RegisterVerify(s *server.MCPServer)

RegisterVerify wires the Verify MCP tool.

func RegisterWebFetch

func RegisterWebFetch(s *server.MCPServer)

RegisterWebFetch adds the WebFetch tool to the given MCP server.

func RegisterWebSearch

func RegisterWebSearch(s *server.MCPServer, store *secrets.Store)

RegisterWebSearch adds the WebSearch tool to the given MCP server. The secrets-store reference is captured so per-call backend resolution can pick up updated API keys without restart.

func RegisterWrite

func RegisterWrite(s *server.MCPServer)

RegisterWrite adds the Write tool to the given MCP server.

func ResetBashTasksForTest added in v0.20.0

func ResetBashTasksForTest()

ResetBashTasksForTest wipes the registry. Test-only.

func ResetEngineCache

func ResetEngineCache()

ResetEngineCache forces a re-detection on next LookupEngine call. Used by tests that manipulate $PATH.

func ResetSemanticSearchCache added in v0.20.0

func ResetSemanticSearchCache()

ResetSemanticSearchCache lets tests drop the cached stores. No-op in production.

func ResetSessionsForTest added in v0.20.0

func ResetSessionsForTest()

ResetSessionsForTest clears the registry. Test-only escape hatch matching the pattern in agents/supervisor.go.

func SetAutoLintEnabled added in v0.20.0

func SetAutoLintEnabled(enabled bool)

SetAutoLintEnabled lets server.go's boot path flip the flag based on config.AutoLint.Enabled. Idempotent.

func SetBiamStore added in v0.20.0

func SetBiamStore(s *biam.Store)

SetBiamStore registers the process-wide BIAM store. Idempotent.

func SetLintRunner added in v0.20.0

func SetLintRunner(r lint.Runner)

SetLintRunner replaces the package-level Runner — used by tests to inject deterministic findings.

func SubmitBackgroundBash added in v0.20.0

func SubmitBackgroundBash(parent context.Context, command, cwd string, timeoutMs int) (string, error)

SubmitBackgroundBash spawns the command, registers a task, and returns the task_id. The goroutine reading stdout/stderr keeps running after the call returns; consumers poll via TaskGet until status is terminal.

Types

type Backend

type Backend interface {
	Name() string
	Search(ctx context.Context, query string, limit int, opts SearchOptions) ([]WebSearchHit, error)
}

Backend abstracts a web-search provider. Implementations must be safe to invoke from multiple goroutines and complete within the supplied context's deadline.

SearchOptions carries the optional, provider-neutral filters from ADR-021 phase B. Backends translate what they support and ignore the rest — the operator sees a uniform request shape across providers, the unsupported ones degrade silently to "behave as though the filter wasn't supplied".

type BaseResult added in v0.9.0

type BaseResult struct {
	Operation   string `json:"-"`
	Engine      string `json:"engine,omitempty"`
	DurationMs  int64  `json:"duration_ms,omitempty"`
	ErrorReason string `json:"error_reason,omitempty"`
}

BaseResult holds the fields and rendering helpers every tool result shares. Each tool's *Result struct embeds this so the JSON shape stays uniform (timing, error, engine surfaced the same way everywhere) and Render() implementations stay short — each one is just "header + body + footer" composed from BaseResult helpers and the tool-specific data.

Operation is JSON-omitted; it's a presentation concern (the header verb), not a wire field.

func (BaseResult) ErrorLine added in v0.9.0

func (b BaseResult) ErrorLine(target string) string

ErrorLine renders the canonical failure one-liner. Every tool that fails uses this — keeps "✗ <verb> — <reason>" consistent across the whole catalog.

func (BaseResult) FooterLine added in v0.9.0

func (b BaseResult) FooterLine(extras ...string) string

FooterLine joins extras with " · " and appends the duration. Used at the bottom of multi-line results (after content).

func (BaseResult) HeaderLine added in v0.9.0

func (b BaseResult) HeaderLine(title string) string

HeaderLine renders the canonical multi-line header used by tools that return content (Bash, Read, Grep). Engine — when set — is shown in brackets so the caller always knows which backend ran.

func (BaseResult) IsError added in v0.9.0

func (b BaseResult) IsError() bool

IsError is the universal failure predicate.

func (BaseResult) SuccessLine added in v0.9.0

func (b BaseResult) SuccessLine(target string, extras ...string) string

SuccessLine is the canonical single-line success format used by stateless tools (Edit, Write). Variadic extras are joined with " · " and the duration is appended automatically.

type BashTask added in v0.20.0

type BashTask struct {
	ID         string
	Command    string
	Cwd        string
	StartedAt  time.Time
	FinishedAt time.Time
	TimeoutMs  int
	// contains filtered or unexported fields
}

BashTask carries one background bash invocation's state. Output buffers grow without bound by design — the operator can always kill the task when the live tail gets noisy. We cap at 4 MiB per stream to match the BIAM body cap.

func (*BashTask) Snapshot added in v0.20.0

func (t *BashTask) Snapshot() BashTaskSnapshot

Snapshot returns the current state under the task's lock.

type BashTaskSnapshot added in v0.20.0

type BashTaskSnapshot struct {
	ID         string         `json:"task_id"`
	Command    string         `json:"command"`
	Cwd        string         `json:"cwd,omitempty"`
	Status     BashTaskStatus `json:"status"`
	Stdout     string         `json:"stdout"`
	Stderr     string         `json:"stderr"`
	ExitCode   int            `json:"exit_code"`
	TimedOut   bool           `json:"timed_out"`
	StartedAt  time.Time      `json:"started_at"`
	FinishedAt time.Time      `json:"finished_at,omitempty"`
}

snapshot returns a read-only view safe to ship over MCP.

func GetBashTask added in v0.20.0

func GetBashTask(id string) (BashTaskSnapshot, bool)

GetBashTask returns the snapshot for id. ok=false when no task matches.

func KillBashTask added in v0.20.0

func KillBashTask(id string) (BashTaskSnapshot, bool)

KillBashTask cancels the task's context, which propagates SIGKILL to the whole process group via ApplyGroupWithCtxCancel. No-op when the task is already terminal. Returns ok=false for unknown IDs.

func ListBashTasks added in v0.20.0

func ListBashTasks(limit int) []BashTaskSnapshot

ListBashTasks returns every recorded task, newest first. Bounded by limit (0 = no cap).

type BashTaskStatus added in v0.20.0

type BashTaskStatus string

BashTaskStatus mirrors BIAM's lifecycle so an agent that knows TaskGet's vocabulary doesn't need a second mental model.

const (
	BashTaskActive    BashTaskStatus = "active"
	BashTaskDone      BashTaskStatus = "done"
	BashTaskFailed    BashTaskStatus = "failed"
	BashTaskCancelled BashTaskStatus = "cancelled"
)

type BashTaskStore added in v0.20.0

type BashTaskStore struct {
	// contains filtered or unexported fields
}

BashTaskStore is the process-wide registry. Concurrent reads + writes are guarded by an RWMutex so TaskGet / TaskList stay fast under load.

type BrowserFetchResult added in v0.20.0

type BrowserFetchResult struct {
	BaseResult
	URL        string `json:"url"`
	FinalURL   string `json:"final_url,omitempty"`
	Format     string `json:"format"` // "html" | "text" | "eval"
	Title      string `json:"title,omitempty"`
	Byline     string `json:"byline,omitempty"`
	SiteName   string `json:"site_name,omitempty"`
	Content    string `json:"content"`
	EvalResult string `json:"eval_result,omitempty"`
	SizeBytes  int    `json:"size_bytes"`
	FetchedAt  string `json:"fetched_at"`
	Truncated  bool   `json:"truncated"`
}

BrowserFetchResult mirrors WebFetchResult so an agent can swap one for the other without rewriting downstream parsing. Adds EvalResult for callers that pass `eval` (raw stdout slice from obscura).

func (BrowserFetchResult) Render added in v0.20.0

func (r BrowserFetchResult) Render() string

Render keeps parity with WebFetchResult: framed body + footer.

type BrowserScrapeResult added in v0.20.0

type BrowserScrapeResult struct {
	BaseResult
	Results   []BrowserScrapeRow `json:"results"`
	Total     int                `json:"total"`
	Failed    int                `json:"failed"`
	Truncated bool               `json:"truncated"`
	FetchedAt string             `json:"fetched_at"`
}

BrowserScrapeResult lists per-URL outcomes plus aggregate counts.

func (BrowserScrapeResult) Render added in v0.20.0

func (r BrowserScrapeResult) Render() string

Render lists one row per URL.

type BrowserScrapeRow added in v0.20.0

type BrowserScrapeRow struct {
	URL    string `json:"url"`
	Result string `json:"result,omitempty"`
	Error  string `json:"error,omitempty"`
}

BrowserScrapeRow is one URL's outcome. `Result` carries the eval'd value (or rendered text); `Error` is set on per-URL failure so the rest of the batch keeps going.

type EditResult

type EditResult struct {
	BaseResult
	Path                string         `json:"path"`
	Replaced            bool           `json:"replaced"`
	OccurrencesReplaced int            `json:"occurrences_replaced"`
	SizeBytesBefore     int64          `json:"size_bytes_before"`
	SizeBytesAfter      int64          `json:"size_bytes_after"`
	LineEndings         string         `json:"line_endings"`
	LintFindings        []lint.Finding `json:"lint_findings,omitempty"`

	// HashBefore / HashAfter let the model verify exactly what
	// changed (ADR-021). Both are SHA-256 hex of the file's raw
	// bytes — pre-edit and post-edit.
	HashBefore string `json:"hash_before,omitempty"`
	HashAfter  string `json:"hash_after,omitempty"`

	// DiffUnified is a tiny `diff -u`-style patch of the change.
	// Always populated on a successful edit; empty when the edit
	// was a no-op or failed.
	DiffUnified string `json:"diff_unified,omitempty"`
}

EditResult is the uniform shape returned to the agent.

func (EditResult) Render added in v0.9.0

func (r EditResult) Render() string

Render satisfies the Renderer contract. Single-line success/failure; stateless tools don't need a multi-line body.

type Engine

type Engine struct {
	Name string // canonical name, e.g. "ripgrep"
	Bin  string // resolved absolute path, empty if absent
}

Engine describes one detected upstream binary that clawtool wraps.

Per ADR-007, clawtool curates and wraps best-in-class engines. Detection runs once on startup so wrapper tools can pick the best available engine without re-shelling-out for every call.

func LookupEngine

func LookupEngine(name string) Engine

LookupEngine returns the cached engine entry for a given binary name. The Bin field is empty if the engine is not present on this system.

type GlobResult

type GlobResult struct {
	BaseResult
	Matches          []string `json:"matches"`
	MatchesCount     int      `json:"matches_count"`
	Truncated        bool     `json:"truncated"`
	Cwd              string   `json:"cwd"`
	Pattern          string   `json:"pattern"`
	RespectGitignore bool     `json:"respect_gitignore"`
	IncludeHidden    bool     `json:"include_hidden"`
}

GlobResult is the uniform shape returned to the agent.

func (GlobResult) Render added in v0.9.0

func (r GlobResult) Render() string

Render satisfies the Renderer contract. One match per line so the chat looks like running `find` or `fd` in a terminal.

type GrepMatch

type GrepMatch struct {
	Path   string   `json:"path"`
	Line   int      `json:"line"`
	Column int      `json:"column"`
	Text   string   `json:"text"`
	Before []string `json:"before,omitempty"`
	After  []string `json:"after,omitempty"`
}

GrepMatch is a single hit. Line and column are 1-indexed for human readability and to match conventional editor jumping. Before/After arrive populated only when the caller asked for context lines.

type GrepResult

type GrepResult struct {
	BaseResult
	Matches      []GrepMatch `json:"matches"`
	MatchesCount int         `json:"matches_count"`
	Truncated    bool        `json:"truncated"`
	Cwd          string      `json:"cwd"`
	Pattern      string      `json:"pattern"`
}

GrepResult is the uniform shape returned regardless of engine.

func (GrepResult) Render added in v0.9.0

func (r GrepResult) Render() string

Render satisfies the Renderer contract. Output mirrors ripgrep's standard `path:line:col: text` so a developer reading the chat sees the same shape they'd see in a terminal.

type LineEndings

type LineEndings string

LineEndings identifies the dominant line-ending convention of a file.

const (
	LineEndingsLF      LineEndings = "lf"
	LineEndingsCRLF    LineEndings = "crlf"
	LineEndingsCR      LineEndings = "cr" // ancient Mac
	LineEndingsUnknown LineEndings = "unknown"
)

type ReadRecord added in v0.20.0

type ReadRecord struct {
	Path      string    `json:"path"`
	FileHash  string    `json:"file_hash"`            // SHA-256 of raw bytes
	RangeHash string    `json:"range_hash,omitempty"` // SHA-256 of returned line range
	LineStart int       `json:"line_start,omitempty"`
	LineEnd   int       `json:"line_end,omitempty"`
	ReadAt    time.Time `json:"read_at"`
}

ReadRecord captures what a Read tool call observed about a path at a single point in time. Edit + Write consult these to verify the agent has seen the file AND the file hasn't drifted since.

type ReadResult

type ReadResult struct {
	BaseResult
	Path       string `json:"path"`
	Content    string `json:"content"`
	LineStart  int    `json:"line_start"`
	LineEnd    int    `json:"line_end"`
	TotalLines int    `json:"total_lines"`
	SizeBytes  int64  `json:"size_bytes"`
	Format     string `json:"format"`
	Truncated  bool   `json:"truncated"`

	// FileHash is SHA-256 of the file's raw bytes (hex). Edit /
	// Write check this against the recorded read-time hash to
	// detect "file changed since you last looked" (ADR-021).
	FileHash string `json:"file_hash,omitempty"`

	// RangeHash is SHA-256 of the canonical returned content
	// (after format-aware decoding for PDF / DOCX / XLSX). Lets
	// range-based Edits prove they're operating on the slice
	// the model just saw.
	RangeHash string `json:"range_hash,omitempty"`

	// Sheets is populated only for spreadsheet formats; lets the agent
	// page through workbook structure without re-reading the file.
	Sheets []string `json:"sheets,omitempty"`
}

ReadResult is the uniform shape across all formats. Embeds BaseResult so common fields (engine, duration, error) and their rendering helpers are inherited.

func (ReadResult) Render added in v0.9.0

func (r ReadResult) Render() string

Render satisfies the Renderer contract. The body is the file content framed by horizontal rules; header carries path and engine, footer carries cursor + size.

type Renderer added in v0.9.0

type Renderer interface {
	Render() string
}

Renderer is the contract every tool result implements. The MCP dispatch helper below relies on it exclusively; tools that don't implement it would fall through to JSON marshaling, but every core tool overrides it.

type SearchOptions added in v0.20.0

type SearchOptions struct {
	IncludeDomains []string // e.g. ["docs.python.org", "go.dev"]
	ExcludeDomains []string // e.g. ["pinterest.com"]
	Recency        string   // "24h" | "1w" | "1m" | "1y" | ""  (empty = no filter)
	Country        string   // ISO 3166-1 alpha-2 (e.g. "US", "TR"); empty = backend default
	Topic          string   // free-form classifier the backend may honour
}

SearchOptions are the optional filters layered on top of (query, limit). Each backend maps these to its own API: Brave uses goggles for site filters + freshness for recency; Tavily uses include_domains / exclude_domains / topic / time_range; Google CSE uses sort=date + as_sitesearch.

type SemanticSearchResult added in v0.20.0

type SemanticSearchResult struct {
	BaseResult
	Repo    string         `json:"repo"`
	Query   string         `json:"query"`
	Results []index.Result `json:"results"`
}

SemanticSearchResult is the MCP response shape.

func (SemanticSearchResult) Render added in v0.20.0

func (r SemanticSearchResult) Render() string

Render satisfies Renderer. One result per line in the human form, score in parentheses. Path:lines: snippet first 80 chars.

type SessionKey added in v0.20.0

type SessionKey string

SessionKey is the trusted MCP session identifier. "anonymous" when the transport doesn't supply one (typical stdio).

func SessionKeyFromContext added in v0.20.0

func SessionKeyFromContext(ctx context.Context) SessionKey

SessionKeyFromContext extracts the trusted MCP session id from a tool handler's ctx. Falls back to "anonymous" so unit tests (and stdio sessions without a transport-supplied id) still get a meaningful key.

type SessionState added in v0.20.0

type SessionState struct {
	// contains filtered or unexported fields
}

SessionState is the process-local read registry. Concurrent callers share one instance via Sessions.

func (*SessionState) ReadOf added in v0.20.0

func (s *SessionState) ReadOf(sid SessionKey, path string) (ReadRecord, bool)

ReadOf returns the latest record for (session, path).

func (*SessionState) RecordRead added in v0.20.0

func (s *SessionState) RecordRead(sid SessionKey, r ReadRecord)

RecordRead stores a Read observation. Idempotent — re-reading the same path overwrites the prior record.

type ToolSearchResult

type ToolSearchResult struct {
	BaseResult
	Query        string       `json:"query"`
	Results      []search.Hit `json:"results"`
	TotalIndexed int          `json:"total_indexed"`
	TypeFilter   string       `json:"type_filter,omitempty"`
}

ToolSearchResult is the JSON envelope returned to the agent.

func (ToolSearchResult) Render added in v0.9.0

func (r ToolSearchResult) Render() string

Render satisfies the Renderer contract. Each hit is rendered as "[score] name (type) — description" so the agent and the user see a plausible top-N list rather than raw JSON.

type VerifyCheck added in v0.20.0

type VerifyCheck struct {
	Name              string `json:"name"`
	Status            string `json:"status"` // "pass" | "fail" | "timeout" | "skipped"
	DurationMs        int64  `json:"duration_ms"`
	Summary           string `json:"summary,omitempty"`
	DetailsLogExcerpt string `json:"details_log_excerpt,omitempty"`
}

VerifyCheck is one per-runner result. `DetailsLogExcerpt` is the last verifyMaxLogExcerpt bytes of combined stdout+stderr — enough for an agent to read the last failing assertion without blowing the response budget.

type VerifyResult added in v0.20.0

type VerifyResult struct {
	BaseResult
	Repo    string        `json:"repo"`
	Checks  []VerifyCheck `json:"checks"`
	Overall string        `json:"overall"` // "pass" | "fail"
}

VerifyResult is the uniform response. `Overall` is "pass" iff every check passed; one fail flips the whole result.

func (VerifyResult) Render added in v0.20.0

func (r VerifyResult) Render() string

Render satisfies the Renderer contract. One line per check + a final overall verdict.

type WebFetchResult

type WebFetchResult struct {
	BaseResult
	URL         string `json:"url"`
	FinalURL    string `json:"final_url"`
	Status      int    `json:"status"`
	ContentType string `json:"content_type"`
	Format      string `json:"format"` // "html" | "text" | "binary-rejected"
	Title       string `json:"title,omitempty"`
	Byline      string `json:"byline,omitempty"`
	SiteName    string `json:"site_name,omitempty"`
	Content     string `json:"content"`
	SizeBytes   int    `json:"size_bytes"`
	FetchedAt   string `json:"fetched_at"`
	Truncated   bool   `json:"truncated"`
}

WebFetchResult is the uniform shape returned to the agent.

func (WebFetchResult) Render added in v0.9.0

func (r WebFetchResult) Render() string

Render satisfies the Renderer contract. Header carries the URL + status + format; body is the extracted article (or raw text); footer notes truncation when applicable.

type WebSearchHit

type WebSearchHit struct {
	Title   string `json:"title"`
	URL     string `json:"url"`
	Snippet string `json:"snippet,omitempty"`
}

WebSearchHit is one ranked result from any backend.

type WebSearchResult

type WebSearchResult struct {
	BaseResult
	Query        string         `json:"query"`
	Results      []WebSearchHit `json:"results"`
	ResultsCount int            `json:"results_count"`
	Truncated    bool           `json:"truncated"`
}

WebSearchResult is the uniform result envelope the agent receives. Backend lives in BaseResult.Engine because the engine concept is the same — which backend ran this query — and consolidating in the embedded struct keeps every tool's "who did the work" field in one place across the catalog.

func (WebSearchResult) Render added in v0.9.0

func (r WebSearchResult) Render() string

Render satisfies the Renderer contract. Header carries query + backend; body lists `[N] title — url` rows so a developer scans results the same way they would in a browser results page.

type WriteResult

type WriteResult struct {
	BaseResult
	Path         string         `json:"path"`
	BytesWritten int64          `json:"bytes_written"`
	Created      bool           `json:"created"`
	LineEndings  string         `json:"line_endings"`
	LintFindings []lint.Finding `json:"lint_findings,omitempty"`
}

WriteResult is the uniform shape returned to the agent.

func (WriteResult) Render added in v0.9.0

func (r WriteResult) Render() string

Render satisfies the Renderer contract. The Operation field switches between "Write" and "Create" based on whether the file existed beforehand — agents glance-read whether something was clobbered.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL