Metadata-Version: 2.4
Name: shell-cli
Version: 0.14.0
Summary: Agent-first CLI and agent owning the file-and-shell tool surface for AI coding agents: read, write, edit, list, view media, and gated shell execution, with path confinement and an operator approval policy. Pure-stdlib core, extracted from colleague so any host imports one safe execution layer.
Project-URL: Homepage, https://github.com/agentculture/shell-cli
Project-URL: Issues, https://github.com/agentculture/shell-cli/issues
Author: AgentCulture
License-Expression: Apache-2.0
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development
Requires-Python: >=3.12
Description-Content-Type: text/markdown

# shell-cli

**The guarded local operations plane for AI agents.**

An agent harness has to touch the local machine constantly — read a file, write
one, run a command, run the tests, run a linter, fire a hook, drive git, invoke
a trusted CLI. Every harness reimplements that surface, and reimplements its
safety model with it, usually worse each time.

shell-cli provides one place to **plan, authorize, execute, observe, and record**
a local operation. It does not decide *why*, *when*, or *which* — that is the
calling harness's job. It is not another agent: it contains no model, planner,
memory, web browser, or domain workflow. It is the execution substrate beneath
them.

[`colleague`](https://github.com/agentculture/colleague) is the first consumer,
not the owner. colleague imports shell-cli; shell-cli imports nothing from
colleague.

Repo token `shell-cli`, console command **`shell`**, import package **`shell`**,
PyPI distribution **`shell-cli`**.

## ⚠️ A guard, not a sandbox

**Read this before you rely on anything here.**

The execution gate is **best-effort**. It inspects the command string it is
handed, so it is bypassable by `sh -c`, pipelines, shell expansion, here-docs,
and any interpreter that takes code as an argument. There is no namespace,
container, or seccomp isolation.

It protects against **accidental and careless** model behaviour. It does **not**
protect against an adversarial one, and it is not a security boundary to put
untrusted input behind.

A container runner is planned, and when it lands it will be described
separately: a declared boundary with a documented profile, recorded per
operation. It will not retroactively change what the host path above provides.
The two claims stay textually separate on purpose.

Full detail — assets, actors, known bypasses, and what real isolation would
require — in [`docs/threat-model.md`](docs/threat-model.md). The same posture is
readable in-band via `shell explain safety`, and
[`tests/test_honesty.py`](tests/test_honesty.py) fails the build if any shipped
surface drops the disclaimer or claims isolation.

## Status

**The operation core is built, and the CLI has started catching up to it.** The
library has `Operation`, `OperationResult`, `Environment`, the ported policy
evaluator, the evidence contract, and a `HostRunner` with real execution —
`fs.read`, `fs.list`, `fs.stat`, `fs.write`, `fs.edit`, `fs.media`,
`process.exec`, and `process.shell` are all built and green.

The CLI exposes the introspection verbs (`whoami`, `learn`, `explain`,
`overview`, `doctor`, `cli`), two operation-aware nouns — `policy
check`/`policy explain` evaluate the same gate `execute()` uses, and `operation
show` retrieves persisted evidence — and two groups whose verbs **execute**
operations: `shell fs read|list|stat|write|edit|media` and `shell process
exec|shell`. They front `operations.execute()` rather than reimplementing
anything, so the CLI and the library share one engine. Mutation and execution
verbs preview by default and require `--apply`, and the flag is derived from
each kind's registered intent rather than hand-maintained. `process shell`
accepts the `project` profile only — a raw string is re-interpreted by a shell
after the gate read it, so it cannot honestly carry control-plane trust.

`shell env describe` renders the selected environment's two axes and the
runner's posture. `env create`/`env destroy` are deliberately absent until the
lifecycle engine exists, rather than shipped as a facade.

**Still library-only:** the `git` noun group does not exist yet, and neither
does a container runner or colleague delegation.
Publishing is gated on the full quality suite plus a built-wheel smoke test.

[Issue #1](https://github.com/agentculture/shell-cli/issues/1) is the source of
truth for scope; the converged spec and the buildable plan live in
[`docs/specs/`](docs/specs/) and [`docs/plans/`](docs/plans/).

## The model

An **operation** is any local observation, mutation, process invocation, or
environment lifecycle action. The boundary is **work-affecting I/O** — not every
internal byte a runtime writes. Every operation follows one lifecycle:

```text
intent -> Operation -> policy + preview -> environment backend
       -> result + evidence -> caller
```

### Environments have two independent axes

"Host", "worktree" and "container" are not one overloaded mode. A workspace axis
selects what may be observed and changed; a runner axis selects what executes
it.

**The Container rows are a design target, not a description of shipped
behaviour.** `HostRunner` is built and is what every shipped operation runs
through today; `ContainerRunner` does not exist yet. The `Intended guarantee`
column says what each combination provides for the Host rows now, and what the
Container rows are *meant* to provide once built — read the Container rows as a
specification and do not rely on them as a present-tense security property.

| Workspace | Runner | Intended guarantee | Built? |
|---|---|---|---|
| Checkout | Host | Guarded host execution; no isolation | Yes |
| Worktree | Host | Reviewable/recoverable changes; no process isolation | Yes |
| Checkout | Container | Execution isolation against the selected checkout | No |
| Worktree | Container | Preferred autonomous mode | No |

When the container rows are built, their actual profile gets measured and
recorded per operation, and documented separately from the host claim above.
Until then the honest summary of what shell-cli isolates is: **nothing**.

### Three operation profiles

Every subprocess declares why it is running, because not all of them deserve
equal trust.

- **`project`** — executes repository-controlled code: model-issued commands,
  tests, linters, repo hooks.
- **`control`** — executes trusted control-plane programs: git mechanics,
  neighbouring AgentCulture CLIs, the container engine itself. Argv vectors
  only; raw shell strings are not appropriate here.
- **`observe`** — structured reads confined to the selected root.

### Evidence is a product surface

Every operation produces a structured record suitable for the model, the
operator, telemetry, and an external validator: what was requested and how it
normalized, the policy verdict, the environment and runner, timing and exit
status, separately captured stdout/stderr with truncation markers, and the known
filesystem/git effects — plus **an honest marker for whether that effect list is
complete**. Secrets are never recorded, and a failure to capture evidence never
turns an executed action into an unrecorded success.

## Constraints that hold it together

- **Pure-stdlib core, zero base dependencies.** `dependencies = []`, guarded by a
  test. colleague allow-lists exactly its sanctioned base deps; a third-party
  dependency here means colleague cannot import this package at all. Optional
  integrations go behind extras.
- **A guard, not a sandbox** — see above.
- **Mutation and execution preview by default.** The CLI requires `--apply`;
  imported callers must pass `apply=True` explicitly. A preview is never
  reported as success — it gets its own status.
- **Library first, CLI second.** The CLI is a front end over the same operation
  engine, never a second implementation.
- **Every PR bumps the version** — every push to `main` publishes to PyPI. See
  [`docs/release-runbook.md`](docs/release-runbook.md).

## Quickstart

```bash
uv sync
uv run pytest -n auto                 # run the test suite
uv run shell whoami                   # identity from culture.yaml
uv run shell learn                    # self-teaching prompt (add --json)
uv run shell explain safety           # the safety posture, in-band
uv run teken cli doctor . --strict    # the agent-first rubric gate CI runs
```

## CLI

| Verb | What it does |
|------|--------------|
| `whoami` | Report this agent's nick, version, backend, and model from `culture.yaml`. |
| `learn` | Print a structured self-teaching prompt. |
| `explain <path>` | Markdown docs for any noun/verb path (`explain safety` for the threat model). |
| `overview` | Read-only descriptive snapshot of the agent. |
| `doctor` | Check the agent-identity invariants (prompt-file-present, backend-consistency). |
| `cli overview` | Describe the CLI surface itself. |

Every command supports `--json`. Results go to stdout, errors/diagnostics to
stderr (never mixed). Exit codes: `0` success, `1` user error, `2` environment
error, `3+` reserved.

## Non-goals

shell-cli is deliberately not a general agent layer. It owns local operation
mechanics and nothing else:

- reasoning, planning, roles, or agent orchestration — colleague's job
- model APIs or prompt construction
- **web search and navigation semantics** — `webglass-cli` owns them, as a
  separate repository and a separate colleague capability seam. shell-cli does
  not embed a browser and does not model web actions as local operations. Only
  provider-neutral artifact transfer crosses between the two: a file webglass
  produces is just a file shell-cli can be asked to read.
- memory semantics — `eidetic-cli` owns them
- PR policy, acceptance judgment, or handoff decisions
- claiming containers are perfect isolation, or that worktrees contain anything

## Repo furniture

- **Mesh identity** — `culture.yaml` (`suffix` + `backend`) and the matching
  resident prompt file (`AGENTS.colleague.md`, since this agent runs
  `backend: colleague`). `CLAUDE.md` is the Claude Code prompt and the
  contributor guide.
- **The canonical guildmaster skill kit** under `.claude/skills/`, vendored
  cite-don't-import. Provenance and the re-sync procedure:
  [`docs/skill-sources.md`](docs/skill-sources.md).
- **Build + deploy baseline** — pytest, lint, the agent-first rubric gate, and
  PyPI Trusted Publishing wired into GitHub Actions.

## License

Apache 2.0 — see [`LICENSE`](LICENSE).
