shell

package
v1.3.0-beta.1 Latest Latest
Warning

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

Go to latest
Published: May 29, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package shell hosts shell-side tools: Bash, Ls, Grep, Tree.

Bash is synchronous shell execution; Ls/Tree are filesystem listing helpers (cheaper than a Bash round-trip when you just want to look at a directory); Grep is a regex search across files. Long-running process monitoring is intentionally elsewhere (the monitor package).

Index

Constants

This section is empty.

Variables

View Source
var Grep tools.Tool = &GrepTool{}

Grep is the singleton GrepTool. Delegates to system grep via Bash.

View Source
var Tree tools.Tool = &TreeTool{}

Tree is the singleton TreeTool. Stateless.

Functions

func Names

func Names() []tools.ToolName

Names lists every tool name this package contributes, in canonical order.

Types

type BashTool

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

BashTool runs `/bin/sh -c <command>` with cmd.Dir set to the workdir captured at construction. One BashTool instance per agent — the toolset factory in internal/toolset/builtins.go calls NewBashWithHost so each agent (including subagents spawned with isolation: "worktree") gets a tool that runs in its own directory. The bash process is fresh per call — shell env state does NOT persist between invocations.

When run_in_background=true and a DaemonHost is installed on the agent's ToolState, Execute returns immediately with a daemon id (prefix "b") and the command runs in a detached goroutine. Completion emits a terminal Lifecycle signal on the agent's DaemonState; the drain at the next iter start folds it into <system-reminder>.

func NewBash

func NewBash(workdir string) *BashTool

NewBash constructs a BashTool bound to workdir. An empty workdir means "use the process's current directory" (cmd.Dir = "" — exec defaults). Use this for tests / narrow callers; production tooling always passes the agent's workdir.

host may be nil — in that case run_in_background falls back to the "not supported" error path. Production callers pass a non-nil host so the detached path works.

func NewBashWithHost

func NewBashWithHost(workdir string, host DaemonHost) *BashTool

NewBashWithHost is the production constructor used by the toolset builtins factory. The host supplies the DaemonState + RootCtx + AgentID the bash daemon needs; without it run_in_background is rejected with a clear message.

func (*BashTool) Description

func (t *BashTool) Description() string

func (*BashTool) Execute

func (t *BashTool) Execute(ctx context.Context, logger *slog.Logger, input json.RawMessage) (tools.Result, error)

func (*BashTool) Name

func (t *BashTool) Name() string

func (*BashTool) Schema

func (t *BashTool) Schema() json.RawMessage

type Classification

type Classification struct {
	Risk       Risk
	IsCommonFS bool
	Matched    string
	Reason     string
}

Classification is the structured result of Classify.

Matched is the rule entry that triggered the verdict — for ReadOnly it's the binary name; for Dangerous it's the prefix that matched. Surfaced in the approval UI so the user knows *why* a prompt is showing.

IsCommonFS is an orthogonal flag (NOT a Risk level) set when the binary is one of {mkdir, touch, mv, cp, rmdir, ln, chmod, chown}. The gate uses it to auto-allow these in accept_edits mode while leaving them as regular RiskMutate calls in default mode (which still asks).

func Classify

func Classify(command string) Classification

Classify inspects a shell command string and returns a structured Risk assessment. The classifier:

  1. Splits on safe operators (`|`, `&&`) and recurses on each segment. The combined Risk is the max — a chain is only ReadOnly if every segment is ReadOnly, and is Dangerous if any segment is Dangerous.
  2. Blocks unsafe operators (`;`, `||`, `>`, `>>`, `<`) — they collapse the command to RiskUnknown so the gate asks.
  3. Strips leading `VAR=value` env assignments (POSIX prefix; not the same as `env VAR=value cmd`).
  4. Checks the first non-env token against the dangerous-prefix list.
  5. Checks the first token against the read-only allowlist.

Empty input is RiskUnknown — defensive.

type DaemonHost

type DaemonHost interface {
	DaemonState() *daemon.DaemonState
	RootCtx() context.Context
	AgentID() string
}

DaemonHost is the narrow surface BashTool needs to spawn a bash daemon. Satisfied by *toolset.ToolState in production: it exposes DaemonState() (the catalog daemons register into), RootCtx() (the agent-lifetime ctx daemon goroutines bind to so they outlive the LLM call), and AgentID() (copied into snapshots so the TUI can label rows by owner).

type GrepTool

type GrepTool struct{}

func NewGrep

func NewGrep() *GrepTool

func (*GrepTool) Description

func (t *GrepTool) Description() string

func (*GrepTool) Execute

func (t *GrepTool) Execute(ctx context.Context, logger *slog.Logger, input json.RawMessage) (tools.Result, error)

func (*GrepTool) Name

func (t *GrepTool) Name() string

func (*GrepTool) Schema

func (t *GrepTool) Schema() json.RawMessage

type Risk

type Risk int

Risk classifies a shell command's safety from the gate's perspective.

The gate uses Risk to drive auto mode (RiskReadOnly → allow, RiskDangerous → ask with a hint, RiskMutate/RiskUnknown → ask without a hint). Default mode treats every risk level as "ask," so a misclassification can't bypass the user — at worst it shows a prompt that didn't need to appear.

Conservative bias: RiskUnknown is the catch-all. Anything we can't confidently rate as ReadOnly stays Unknown, which forces ask.

const (
	RiskUnknown   Risk = iota // safe fallback: forces ask in auto mode
	RiskReadOnly              // read-only allowlist binary with safe arguments
	RiskMutate                // a non-allowlisted, non-dangerous command
	RiskDangerous             // matches a known code-execution prefix
)

func (Risk) String

func (r Risk) String() string

String returns a stable, human-readable name for the risk level.

type TreeTool

type TreeTool struct{}

func (*TreeTool) Description

func (t *TreeTool) Description() string

func (*TreeTool) Execute

func (t *TreeTool) Execute(_ context.Context, logger *slog.Logger, input json.RawMessage) (tools.Result, error)

func (*TreeTool) Name

func (t *TreeTool) Name() string

func (*TreeTool) Schema

func (t *TreeTool) Schema() json.RawMessage

Jump to

Keyboard shortcuts

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