A minimal, working scaffold for a governed agent workspace in Claude Code. Click "Use this template", open a terminal in your copy, and you have session discipline (orient → work → wrap), a self-improvement loop, and two safety hooks running on day one.
This is the runnable companion to agent-workspace-architecture — a documented production workspace published as a reference. The reference explains the patterns and the reasoning; this template is the smallest honest subset you can actually start from. Take the interactive tour of the full system to see where a workspace like this can go.
# 1. Scaffold your workspace from this template
gh repo create my-workspace --template jimy-r/agent-workspace-starter --private --clone
cd my-workspace
# 2. Confirm the hooks can find their interpreter. This must print 1.
python3 -c "print(1)"
# 3. Open Claude Code
claude
# 4. Say:
# "orient" → briefing on workspace state + recommended next action
# "add to tasks: <thing>" → captured in tasks/notes.md
# "wrap" → close-out ritual when a task finishesIf step 2 prints anything else, both guards stay off. The hooks are wired to python3 in .claude/settings.json, the name macOS and most Linux distributions ship, and Claude Code treats a hook it can't start as a non-blocking error, so the tool call goes through and at most you see a notice. On Windows, the Python Install Manager and the Microsoft Store build provide python3. The python.org installer and conda provide only python and py. Change "command": "python3" to "python" or "py" in both hook entries, then re-run step 2 with the name you chose.
Your copy also inherits the files that maintain this template's own repository. Delete or replace AGENTS.md, SECURITY.md, CONTRIBUTING.md and .github/ISSUE_TEMPLATE/, since each one is written about this template. Keeping .github/workflows/ci.yml and .github/dependabot.yml is optional. The workflow checks that both hooks still block what they should and, on pull requests, runs a redaction scan, which earns its place if your copy is public. Dependabot proposes updates to the workflow's pinned actions, so keep or delete the two together.
New to how agent context works? Read docs/before-your-first-session.md first: what the model can see, why long sessions drift, and the five habits the loop below assumes. Five minutes.
No build step, no dependencies beyond Python 3.12+ on PATH (for the optional hooks; delete the hooks block in .claude/settings.json if you don't want them). 3.12 is what CI compile-checks the hooks against, so it is the floor this template actually tests. Older interpreters may well run them, untested.
| Path | What it does |
|---|---|
CLAUDE.md |
Always-loaded context: working principles, command shortcuts, credential law. Edit the placeholders first. |
.claude/skills/orient/ |
Session-start briefing: reads a fixed file set, reports state, recommends one next action. Read-only by design. |
.claude/skills/wrap/ |
Close-out ritual: updates the plan, strikes the task bullet, sweeps registries, captures lessons. |
.claude/skills/tasks/ |
Quick task-queue readout, lighter than orient. |
.claude/hooks/protect_files.py |
PreToolUse guard: blocks agent edits to protected paths (.env, credentials, anything you list). |
.claude/hooks/check_bash_command.py |
PreToolUse guard: blocks shell-level writes to the same paths, plus force-pushes and pushes to main. |
.claude/protected-paths.txt |
One place to declare what the agent must never write to. |
tasks/todo.md |
The active implementation plan (plan first, then build). |
tasks/notes.md |
Your running task list, grouped by topic. |
tasks/lessons.md |
The self-improvement loop: every correction becomes a rule the next session reads. |
- Orient at session start. The agent reads a small fixed file set and tells you where things stand. No wandering, no speculative exploration.
- Plan before building. Work gets a checklist in
tasks/todo.md; you approve it before implementation. - Capture corrections as lessons. When you correct the agent, the correction is written to
tasks/lessons.mdbefore work continues. Sessions start by reading it. The mistake rate drops because the rules accumulate. - Wrap when done. Completed work is integrated everywhere it needs to be recorded, not just left in the diff.
That loop is the smallest version of the governance patterns documented in the reference repo — see PATTERNS.md for the full set (scheduled agents, review queues, audits, memory hygiene) and what each one costs.
If you'd rather build than read, docs/tutorial.md walks this exact loop in seven checked steps.
The two guards are mistake-catchers, not security boundaries. They block the casual failure modes — an agent redirecting output over your .env, a well-meaning force-push — by string-matching tool calls before they run. A determined process writing files from inside Python is out of scope. They fail open: a bug in a hook will never brick your session. A missing interpreter fails open the same way, which is why step 2 of the Quickstart exists.
Declare what's off-limits in .claude/protected-paths.txt (one substring per line). Both hooks read it; sensible defaults apply if it's missing.
Four known sharp edges, inherited from real use. The bash guard checks quoted strings as commands too, so a commit message that itself reads as a blocked command (git push origin main) is refused. Any path containing a protected substring is blocked even in quoted strings. Text inside braces is read as a command, so a hashtable or JSON body whose key is named like a delete or a move (@{ del = '.env' }) is refused when its value is a protected path. And a comment is read as part of the command, because a comment line must not hide the command below it. So a note after a delete that names a protected file (rm old.log # not .env), a note after a push that names main, and a comment that holds a blocked command of its own after a ;, a | or a brace are all refused. Reword the message or the comment, rename the key or loosen the list rather than fighting it.
The shell guard has limits as well. It reads the command each statement starts with and the paths written out beside it, and it misses a write that takes a longer route. It doesn't follow a redirect, tee, touch or chmod whose path is quoted only in part, a path built by an unquoted $(...) or a parenthesised expression, or a file name that arrives through a pipe. It doesn't look behind a prefix it has no rule for (timeout, eval, sudo -u <user>, coproc), into a command handed unquoted to another shell (cmd /c, wsl, powershell -Command) or run inside backticks, or past a comma that continues a PowerShell list onto the next line. And some writers it has no rule for at all, among them dd of=, install, ln -sf, find -delete, git checkout --, git clean, the &> and >| redirects, and PowerShell cmdlets outside its list such as Clear-Item and Export-Csv.
Quotes and comments set limits of their own. A quote left open anywhere in a command sends the guard to a plain split that doesn't follow quotes, so it can miss a command inside them or behind a quoted value with a space in it, and Bash still runs every line above the broken one. A quote mark that the shell reads as plain text can do the same, such as one behind a backslash or an apostrophe in a here-document, and then nothing is broken and the whole script runs. The guard reads each command again with its comments cut out, because a quote mark in a comment would otherwise count as a quote, and once more as only the text in front of the first comment, which nothing further down can reach. It takes a # straight after a brace for the start of a comment, which is right for PowerShell and wrong for Bash, so both of those readings stop at a ${#name}. It cuts comments out of a quoted script too, but the script's line breaks are separators by then, so a comment there runs to the end of the script and a quote mark in it can still hide a command on a later line. In PowerShell a block comment in front of a command on the same line (<# note #> Remove-Item x) hides it, and the guard reads a lone carriage return as a space where PowerShell ends the line. A casual slip rarely takes one of these shapes, and that is the bar these guards are built for.
Your copy doesn't update itself. A guard fix made here reaches only the copies made after it, so each one is published as a release that names the files to copy across. Watch this repository's releases (Watch → Custom → Releases) to hear about the next one.
CLAUDE.md— replace the placeholders, trim what you don't use. Every line costs tokens on every turn; short is correct.- Add project folders, each with its own
CONTEXT.md(entity facts) — orient's freshness scan picks them up automatically. - New verbal shortcut? Add a row to the Command shortcuts table in
CLAUDE.mdso it works next session. - Outgrowing the starter? The reference repo documents the next pieces in adoption order: ADOPTION.md.
James Ross. I design agent workspaces and AI-orchestration systems; this template is extracted from the workspace I run daily, published in full (redacted) at agent-workspace-architecture. If you're standing up something similar inside an organisation, the practice site is jamesross.ai.
Maintained best-effort as a curated artifact: no roadmap, no SLA. Issues for real defects welcome; CONTRIBUTING.md says what counts as one and the bug report form is the intake.
MIT. Fork freely; that's what it's for.