Skip to content

Course · Pi · Getting started

Getting started

A friend just handed you stash - a small feed reader they built over a few weekends to keep a reading queue. It’s a FastAPI service with three routes: GET /healthz, POST /feeds/{id}/sync to fetch a feed and store its entries, and GET /articles to list what’s stored. Under that sit SQLite through stash/db.py, feed fetching and parsing in stash/feeds.py, date parsing in stash/extract.py, and two pydantic models. Eleven files, two hundred-odd lines - you can read all of it in one sitting, and this module will make you.

  • macOS or Linux (Windows via WSL2)
  • Node.js >= 22.19.0 (node --version to check)
  • Python 3.11+ (python3 --version) - stash is a Python project
  • An API key or supported subscription for at least one provider (Anthropic, OpenAI, OpenRouter)
  • Basic terminal fluency
  • Git fundamentals
  • Any Git repo to try the moves on - there is no published companion repo to clone, and any repo with a failing test you understand will carry the module
  • Estimated time: about an hour for the four moves below

The handoff came with the usual confession, and this one is checkable. The README admits to one known failing test: feed dates lose their timezone offset on parse. Run the suite on a working copy yourself and you’ll find the README is one test short - two fail, not one. (The staged project this course follows ships exactly that state; if you’re starting here, any repo with a README-confessed failing test and one more besides will carry the lesson.) The second, tests/test_feeds.py::test_fetch_feed_parses_entries, is a bug the README never mentions: fetch_feed() returns zero articles from a sample Atom feed. It’s the bigger of the two, and it can wait until you’ve shipped something.

Your warm-up ticket is the one the README does confess to. parse_date() in stash/extract.py tries two date formats, neither of which accepts a timezone offset, so a date like Mon, 03 Feb 2026 09:00:00 +0530 returns None. The article lands with no published value, and anything sorting your queue by that field puts it somewhere wrong. The failing test, test_parse_date_preserves_offset, already pins what “fixed” means: the offset survives. Small bug, clean edges - a decent first ticket, once you’ve met the tool you’ll fix it with.

Every other agent you’ve used already decided, on your behalf, what’s worth watching. A permission prompt here, a collapsed tool-call summary there - conveniences that also mean you never had to look closely at a single turn to trust the next ten. You cannot budget what you have never watched run once. Before you can decide which of another agent’s guardrails you’d actually rebuild, you need to have seen, one time, exactly what happens with none of them in place.

That’s what Pi is for. Claude Code or Codex ships a big system prompt, a curated toolbox, and a permission gate between the model and your disk; Pi ships almost none of it. Four tools. A system prompt short enough to read while your terminal opens. No subagents, no plan mode, no to-do list, no MCP (Model Context Protocol) servers mounted until you add one, and - the part that should give you pause - no permission prompts. Out of the box, it can read, write, and run anything on your machine the moment you instruct it.

That is not an oversight. Pi’s bet is that frontier models are already trained on the moves a coding agent makes - reading files, editing them, running a shell command - so a harness doesn’t need to re-teach them in a bloated prompt. On this bet, a harness’s job is to get out of the way and let you build the parts you actually want.

The scaffolding other agents bake in - guardrails, subagents, planning modes - Pi treats as extensions you add back deliberately, once you’ve felt the need for them.

So for this whole first module, you’ll feel the absence of guardrails before you build any. The model, meanwhile, is the same frontier model you’d get anywhere else. What changes the outcome is the harness wrapped around it - what it can see, what it’s told, what’s allowed to run unattended. Same model, radically different harness, radically different working day. Here’s that idea in miniature, on a task that has nothing to do with stash:

> Add rate limiting to the invite endpoint.

Pi mounts no MCP servers for you, and its rules handling reads only files named where it looks - the SYSTEM.md your friend left at the repo root doesn’t qualify yet, which is a later chapter’s job - so for now your run looks closer to the bare agent one. You’ll spend the rest of this course building toward the + your context one, by hand, on stash.

We’ll go from nothing installed to the date bug fixed, verified, and committed, in four moves:

None of it is hard, and that’s sort of the point. The habits this hour asks for - reading the loop closely, knowing exactly what has filesystem and shell access and when - are the whole safety story here, because nothing stops the model from acting except your own understanding of what you just told it to do. One fix, one still-red failure deferred to the next chapter, and one commit by the end.