shell

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jul 14, 2026 License: Apache-2.0 Imports: 25 Imported by: 0

Documentation

Overview

Package shell provides cross-platform shell execution capabilities.

This package provides Shell instances for executing commands with their own working directory and environment. Each shell execution is independent.

WINDOWS COMPATIBILITY: This implementation provides POSIX shell emulation (mvdan.cc/sh/v3) even on Windows. Commands should use forward slashes (/) as path separators to work correctly on all platforms.

Index

Constants

View Source
const (
	// MaxBackgroundJobs is the maximum number of concurrent background jobs allowed
	MaxBackgroundJobs = 50
	// CompletedJobRetentionMinutes is how long to keep completed jobs before auto-cleanup (8 hours)
	CompletedJobRetentionMinutes = 8 * 60
)

Variables

View Source
var NoUnset atomic.Bool

NoUnset controls whether ExpandValue treats unset variables as an error. Default false matches bash: $UNSET expands to "". Store true to re-enable strict mode globally. Not exposed in seshat.json; this is an internal escape hatch in case the lenient default turns out to be the wrong call.

Declared atomic because ExpandValue is invoked concurrently (multiple MCP / LSP / provider loads in flight at startup, hook execution, etc.) and an unsynchronised read/write pair is a data race under the Go memory model regardless of test-level happens-before reasoning. The atomic load on the hot path is negligible against the cost of parsing and running through mvdan.

Functions

func ExitCode

func ExitCode(err error) int

ExitCode extracts the exit code from an error

func ExpandValue

func ExpandValue(ctx context.Context, value string, env []string) (string, error)

ExpandValue expands shell-style substitutions in a single config value.

Supported constructs match the bash tool:

  • $VAR and ${VAR}.
  • ${VAR:-default} / ${VAR:+alt} / ${VAR:?msg}.
  • $(command) with full quoting and nesting.
  • escaped and quoted strings ("...", '...').

Contract:

  • Returns exactly one string. No field splitting, no globbing, no pathname generation. Multi-word command output is preserved verbatim; it is never split into multiple values.
  • Nounset is off by default, matching bash: unset variables expand to "". Opt in to strict behaviour per-reference with ${VAR:?msg}, which errors loudly when VAR is unset regardless of the global toggle. Flip the global default via shell.NoUnset.Store(true) as an internal escape hatch.
  • Embedded whitespace and newlines in the input are preserved verbatim. Command substitution strips trailing newlines only (POSIX), never leading or internal whitespace.
  • Errors wrap the failing inner command's exit code and a bounded prefix of its stderr. Callers that surface the error to users should additionally scrub it for the original template text.

func IsInterrupt

func IsInterrupt(err error) bool

IsInterrupt checks if an error is due to interruption

func Run

func Run(ctx context.Context, opts RunOptions) (err error)

Run parses and executes a shell command using the same mvdan.cc/sh interpreter stack that the stateful Shell type uses (builtins, optional block list, optional Go coreutils). It is safe to call concurrently from multiple goroutines: each call builds its own interp.Runner and shares no state with other callers or with any Shell instance.

Errors returned from the command itself (non-zero exit, context cancellation, parse failures) follow the same conventions as Shell.Exec: inspect with IsInterrupt and ExitCode.

func SeshatEnvMarkers

func SeshatEnvMarkers() []string

SeshatEnvMarkers returns a fresh slice of the environment variables that Seshat unconditionally sets on every shell it spawns — both the interactive bash tool's Shell and the hook runner's Run calls. Tools that want to detect "am I being invoked by an AI agent?" can check any of these. Keeping them in one place guarantees the two shell surfaces cannot drift. A fresh slice is returned on every call so callers may append freely.

Types

type BackgroundShell

type BackgroundShell struct {
	ID          string
	Command     string
	Description string
	Shell       *Shell
	WorkingDir  string
	// contains filtered or unexported fields
}

BackgroundShell represents a shell running in the background.

func (*BackgroundShell) GetOutput

func (bs *BackgroundShell) GetOutput() (stdout string, stderr string, done bool, err error)

GetOutput returns the current output of a background shell.

func (*BackgroundShell) IsDone

func (bs *BackgroundShell) IsDone() bool

IsDone checks if the background shell has finished execution.

func (*BackgroundShell) Wait

func (bs *BackgroundShell) Wait()

Wait blocks until the background shell completes.

func (*BackgroundShell) WaitContext

func (bs *BackgroundShell) WaitContext(ctx context.Context) bool

type BackgroundShellInfo

type BackgroundShellInfo struct {
	ID          string
	Command     string
	Description string
}

BackgroundShellInfo contains information about a background shell.

type BackgroundShellManager

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

BackgroundShellManager manages background shell instances.

func GetBackgroundShellManager

func GetBackgroundShellManager() *BackgroundShellManager

GetBackgroundShellManager returns the singleton background shell manager.

func (*BackgroundShellManager) Cleanup

func (m *BackgroundShellManager) Cleanup() int

Cleanup removes completed jobs that have been finished for more than the retention period

func (*BackgroundShellManager) Get

Get retrieves a background shell by ID.

func (*BackgroundShellManager) Kill

func (m *BackgroundShellManager) Kill(id string) error

Kill terminates a background shell by ID.

func (*BackgroundShellManager) KillAll

func (m *BackgroundShellManager) KillAll(ctx context.Context)

KillAll terminates all background shells. The provided context bounds how long the function waits for each shell to exit.

func (*BackgroundShellManager) List

func (m *BackgroundShellManager) List() []string

List returns all background shell IDs.

func (*BackgroundShellManager) Remove

func (m *BackgroundShellManager) Remove(id string) error

Remove removes a background shell from the manager without terminating it. This is useful when a shell has already completed and you just want to clean up tracking.

func (*BackgroundShellManager) Start

func (m *BackgroundShellManager) Start(ctx context.Context, workingDir string, blockFuncs []BlockFunc, command string, description string) (*BackgroundShell, error)

Start creates and starts a new background shell with the given command.

type BlockFunc

type BlockFunc func(args []string) bool

BlockFunc is a function that determines if a command should be blocked

func ArgumentsBlocker

func ArgumentsBlocker(cmd string, args []string, flags []string) BlockFunc

ArgumentsBlocker creates a BlockFunc that blocks specific subcommand

func CommandsBlocker

func CommandsBlocker(cmds []string) BlockFunc

CommandsBlocker creates a BlockFunc that blocks exact command matches

type Logger

type Logger interface {
	InfoPersist(msg string, keysAndValues ...any)
}

Logger interface for optional logging

type Options

type Options struct {
	WorkingDir string
	Env        []string
	Logger     Logger
	BlockFuncs []BlockFunc
}

Options for creating a new shell

type RunOptions

type RunOptions struct {
	// Command is the shell source to parse and execute.
	Command string
	// Cwd is the working directory for the execution. Required: callers
	// must supply a non-empty value. Run does not silently fall back to
	// the Seshat process cwd — hooks and the bash tool have different
	// notions of "default" and each owns that decision.
	Cwd string
	// Env is the full environment visible to the command. The caller is
	// responsible for inheriting from os.Environ() if that's desired.
	Env []string
	// Stdin is the command's standard input. nil is equivalent to an empty
	// input stream.
	Stdin io.Reader
	// Stdout receives the command's standard output. nil discards output.
	Stdout io.Writer
	// Stderr receives the command's standard error. nil discards output.
	Stderr io.Writer
	// BlockFuncs is an optional list of deny-list matchers applied before
	// each command reaches the exec layer. nil disables blocking entirely.
	BlockFuncs []BlockFunc
}

RunOptions configures a single stateless shell execution via Run.

The zero value is not useful; at minimum Command must be set. Stdin, Stdout, and Stderr may be nil (nil readers/writers are treated as empty/discard). BlockFuncs may be nil to disable block-list enforcement — hooks use this to run user-authored commands with the same trust level as a shell alias.

type Shell

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

Shell provides cross-platform shell execution with optional state persistence

func NewShell

func NewShell(opts *Options) *Shell

NewShell creates a new shell instance with the given options

func (*Shell) Exec

func (s *Shell) Exec(ctx context.Context, command string) (string, string, error)

Exec executes a command in the shell

func (*Shell) ExecStream

func (s *Shell) ExecStream(ctx context.Context, command string, stdout, stderr io.Writer) error

ExecStream executes a command in the shell with streaming output to provided writers

func (*Shell) GetEnv

func (s *Shell) GetEnv() []string

GetEnv returns a copy of the environment variables

func (*Shell) GetWorkingDir

func (s *Shell) GetWorkingDir() string

GetWorkingDir returns the current working directory

func (*Shell) SetBlockFuncs

func (s *Shell) SetBlockFuncs(blockFuncs []BlockFunc)

SetBlockFuncs sets the command block functions for the shell

func (*Shell) SetEnv

func (s *Shell) SetEnv(key, value string)

SetEnv sets an environment variable

func (*Shell) SetWorkingDir

func (s *Shell) SetWorkingDir(dir string) error

SetWorkingDir sets the working directory

type ShellType

type ShellType int

ShellType represents the type of shell to use

const (
	ShellTypePOSIX ShellType = iota
	ShellTypeCmd
	ShellTypePowerShell
)

Jump to

Keyboard shortcuts

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