In Part 1 we ran mini init
and got pycalc β a small calculator for the terminal. init
asked only four questions, and .mini/project.md
is accordingly thin: a name, one line about what, who it's for, and the constraints ("Python 3, standard library only").
That's enough to start, but not enough to steer. Three things are missing: how we'll build it, what we deliberately won't build, and how we'll know it's done.
That's exactly what mini project
adds.
mini project
opens an interactive Claude Code session β but not to write code. It's a plan-before-code interview. Claude gets your current project.md
as the starting point (it won't ask what the project is about again) and walks you through four stages:
project.md
is shown for approval; only then does it get saved.One trait, faithful to the philosophy from Part 0: the interview is instructed to be critical, not agreeable. A weak idea doesn't get nodded through β you're told why it's weak.
And what the command doesn't do: it never touches .mini/state.json
. Phase state stays in tested TypeScript; project
rewrites one markdown page. The finished document isn't saved by a hallucination either β at the end the agent runs mini project --apply
, and the contract goes through the same parser and renderer as init
.
cd pycalc
mini project
Mini first prints the exact prompt it will send to Claude (no magic β you see everything) and asks whether to start the session. What follows is from a real session, condensed; the answers are mine.
Claude opened by reflecting the project back and refusing to assume anything:
"Terminal calculator" can mean three quite different things, and each leads to a different design.
- How is it used?One-shot (
pycalc "2+3*4"
β prints14
and exits), an interactive REPL, or both? This is the most important decision β it determines the whole structure. And careful: for the one-shot case people already usepython -c "print(2+3*4)"
orbc
. What makes pycalc different, so that it has a reason to exist? That's not nitpicking β it's the question the "What I'm building" line has to answer.
- How much math?Just
+ - * / ( )
with precedence? Powers, modulo? Functions likesqrt
,sin
? Variables? Every extra level means a more complex parser, and variables only make sense in a REPL.
- How will you evaluate expressions?Fair warning: the tempting shortcut is
eval()
. It's one line, but it's a security hole (it evaluates arbitrary Python) and you learn nothing from it. Alternatives:ast.parse
with a node whitelist, or your own parser. What's your goal β tohavea tool, or tolearn how to write a parser?
My answers: one-shot; versus python -c
it's less typing and nicer error messages, versus bc
natural syntax and decimals without scale
setup. Just the four operators and parentheses. And the goal is to learn β own tokenizer and recursive descent, no eval()
.
Claude then proposed the pipeline (tokenizer β recursive descent parser β evaluation, tokens carrying their position in the input) and pushed back where my framing was lazy:
Number semantics and division.You yourself list "decimals without scale setup" as the advantage overbc
. So1/2
must be0.5
, not0
. I'd propose floats β but floats have the known artifacts (0.1+0.2
β0.30000000000000004
). The alternative is thedecimal
module (still standard library). For a calculator whose selling point is decimal numbers, I'd seriously considerdecimal
. Which one?
Good catch β decimal
it is. The interview just changed a design decision I would have gotten wrong by default. Two more came out of the same batch: unary minus goes in ("without it the calculator feels broken β but it's a conscious scope extension, so I'm saying it out loud"), and errors get a defined shape β stderr, non-zero exit code, and a message that points at the problem:
error: unexpected character '+' at position 2
2++3
^
Then stage three β the part I've come to value most. First the agreed leave-outs, then the things Claude would be tempted to add:
Power ``and modulo`
%** β tempting, "just one more rule". But
is right-associative, which is a new class of problem in the parser. **Leave outin v1; once recursive descent is clear to you, it's the ideal v2 exercise.Reading from stdin/pipes(echo "2+2" | pycalc`
) β useful, but it grows the CLI layer.Leave out.Colored output,β the project needs none of it. One positional argument.argparse
with ten flagsLeave out.Packaging (pyproject.toml, pip install)β needless overhead for a learning script.Leave out.
And the closing move the prompt demands β "is there any question that, answered wrong, would send us down the wrong path?" β earned its place:
We chose
decimal
, but we never saidwhat. The default context gives 28 significant digits:1/3
prints0.3333333333333333333333333333
. Technically correct, absurd as calculator output β and the fix, "set your precision", is exactly thescale
frombc
you wanted to avoid. I propose: compute at default precision,round the output to 10 places and strip trailing zeros(6.0
β6
). Trade-off: the output is no longer the bit-exact intermediate value, but for a calculator readability wins.
After I approved the draft, Claude saved it itself β through the contract, not by editing the file freehand:
mini project --apply <<'EOF'
NAME: pycalc
WHAT: A small command-line calculator: `pycalc "2+3*4"` evaluates one expression and exits. Less typing than `python -c`, nicer error messages, and decimal arithmetic that works without bc's scale setup.
FOR_WHOM: People who want quick arithmetic in the terminal
CONSTRAINTS: Python 3, standard library only
APPROACH:
- One-shot CLI: single positional argument, result on stdout, errors on stderr with a non-zero exit code.
- Pipeline: tokenizer β recursive descent parser β evaluation. No eval(); the goal is to learn how to write a parser.
- Grammar: + - * / with correct precedence, parentheses, unary minus.
- Numbers via decimal (exact decimal arithmetic: 0.1+0.2 = 0.3); output rounded to 10 places with trailing zeros stripped (6.0 β 6).
- Tokens carry their position in the input so errors can point at the exact spot (message + caret line).
- Tests: unittest with a table of cases (expression β expected result or error).
NON_GOALS:
- Do not add an interactive REPL mode in this version.
- Do not add functions (sqrt, sin, β¦), constants, variables or assignment.
- Do not use eval() β ever, not even as a temporary shortcut.
- Do not add ** or % in this version (right-associativity of ** is a v2 exercise).
- Do not read expressions from stdin/pipes; one positional argument only.
- Do not add packaging (pyproject.toml, pip install) β run as a plain script.
- Do not add colors or extra CLI flags.
SUCCESS:
- `pycalc "2+3*4"` returns 14; precedence and parentheses verified by the unittest case table.
- `0.1+0.2` β 0.3, `1/2` β 0.5, `-5+3` β -2.
- Every invalid input (2++3, (2+3, abc, empty, division by zero) exits non-zero with a stderr message showing position and caret β never a traceback.
- Runs on plain Python 3 with zero dependencies outside the standard library.
EOF
[ok] Updated .mini/project.md
That contract is also the command's second mode: --apply
reads stdin, starts no Claude, and just writes the file. Handy when you want to edit project.md
from a script or by hand without a session.
One thing to watch: --apply
does a full replace. Whatever you omit from the contract disappears from the file. The session prompt instructs the agent to keep the existing NAME / FOR_WHOM / CONSTRAINTS β but when you write the contract by hand, that's on you.
No new file appeared β project.md
is still one page, it just steers now. Compare the before and after: init
gave us "A small command-line calculator"; the interview turned that into a sharpened one-liner, six approach points, seven non-goals and four testable success criteria. The non-goals aren't decoration: mini keeps them in front of every later step, so when an agent in phase 7 feels like "improving" the calculator with colored output, the project has it in writing that no, it won't.
And the cost side: the whole thing is one interactive session, and you pay for it once. The result is a page that saves tokens afterwards β newer versions of mini send warm sessions only a reference ("read .mini/project.md
if you don't already have it in context") instead of inlining it again and again.
Is the interview worth it for every project? Honestly, no β for a weekend script, init
alone is fine. It pays off the moment a project is big enough that an agent will work on it across many sessions without you re-explaining the vision each time.
project.md
now says what, for whom, how, what not, and when it's done. But the interview also left something behind: ideas like the ** operator β rejected for v1, too good to lose. A backlog for things that aren't phases yet is exactly what mini todo
is for. Capturing ideas β that's Part 3.
*mini is open source: npm install -g mini-orchestrator, then mini install-commands in your project. Source and docs on *