Getting Started¶
Page status: release-ready Source state: checked-example Applies to: Shepherd v0.2.0 Owner: @docs-system-owner (TBD) Validation: docs_src/quickstart/test_world_hero.py
Quickstart. This is the path that runs on the shipped wheel, offline. For the mental model, see the concepts. For exact APIs, see the reference.
Run one task, get its work back as a retained output — a proposal held beside your files — inspect it, and settle it. Deterministic, keyless, no network.
Install¶
Shepherd requires Python 3.11+. OS-level grant enforcement is exercised on macOS (Seatbelt); on Linux, Landlock enforcement is container-gated today; Windows is unsupported — use WSL. (Platform notes)
Initialize a workspace¶
Shepherd runs inside an initialized workspace. Lead with shepherd init:
Run¶
Save this as hero.py in that directory and run python hero.py:
import shepherd as sp
workspace = sp.open(".") # run `shepherd init` here first
# A task is a signature + docstring: the contract a sandboxed agent fulfils.
# The grant on `repo` is what lets it write the repository.
workspace.tasks.register_source(
task_id="quickstart.write_note",
module="quickstart_tasks",
source_text='''
import shepherd as sp
def write_note(repo: sp.May[sp.GitRepo, sp.ReadWrite], topic: str,
output_path: str, output_text: str):
"""Write one note about `topic` into the repository."""
''',
entrypoint="write_note",
may_default="ReadWrite",
)
run = workspace.run(
"quickstart.write_note",
repo=workspace.git_repo(),
args={"topic": "shepherd", "output_path": "NOTE.txt",
"output_text": "Hello from a Shepherd retained output.\n"},
runtime={"provider": "static"}, # deterministic, offline; "claude" = live agent
)
output = run.output() # a proposal, held to one side
print(output.changeset().inspect()["changed_paths"]) # what it wants to change
print(output.read_text("NOTE.txt"), end="") # read it before deciding
output.select() # record your decision — or .discard()
workspace.close()
What happens, in order:
sp.open(".")opens the initialized workspace. (On an uninitialized directory it raises — runshepherd initfirst.)register_sourceregisters a task. The task is a signature plus docstring — the contract a provider-run agent fulfils. The grant onrepo(May[GitRepo, ReadWrite]) is what would let it write the repository; the signature is the permission surface.workspace.run(...)executes it as a retained run.provider: "static"is the deterministic offline provider, so this run is reproducible and free;"claude"runs a live sandboxed agent instead (needs theclaudeCLI and auth).- The work does not touch your files. It lands as a retained output; you
read its changeset and contents first, then settle it —
select(),release(), ordiscard()— exactly once.
Output¶
Executed against the shipped 0.2.0 wheel (this exact transcript is what the page's test asserts):
Inspect the record¶
Every run leaves a durable trace you can read back from the CLI:
shepherd run list
shepherd run show --latest
shepherd run trace --latest --events
shepherd run changeset --latest
You can also fetch this same demo in script form with
shepherd demo write quickstart > quickstart_demo.py, and a live-agent
variant with shepherd demo write agent-task (see the
README).
If it fails¶
WorkspaceControlErrorfromsp.open(".")— the directory is not an initialized Shepherd workspace. Runshepherd initthere first.- Ran the script twice in the same directory? The second run raises
InvalidRepositoryStateError(readiness blocked by run-...) — the workspace still holds the first run's registration. Start from a fresh directory when repeating the walkthrough. - Looking for
with sp.workspace(model=...): my_task(...)? That ambient direct-call shape is a Dataflow roadmap surface — it does not run on the 0.2.0 wheel. Retained runs, as above, are the shipped path.
Next¶
- Grant a task repo access — read-only / read-write grants per bound repository, enforced at the OS under a jailed placement.
- Concepts: Tasks — the mental model.
- Settlement Core / Dataflow — what ships today vs. the named road.