Features Don't Compose
I'd been circling something for weeks. Building toward it without being able to name it. You know that feeling where every decision you make points the same direction but you couldn't tell someone why.
Then I read "Agent-Native Architecture" and it clicked.
"Tools should be atomic primitives. Features are outcomes achieved by an agent operating in a loop."
OpenPKG had compound commands: coverage, audit, report. Feature-rich, helpful defaults, the kind of CLI developers expect. The kind I expect, honestly.
But those compound commands were just simpler operations glued together. coverage was really list + get + compare against docs. audit was snapshot + diff. Every "feature" decomposed into the same five primitives, every single one.
So I deleted the features. Kept the primitives:
openpkg list ./src/index.ts # list exportsopenpkg get ./src/index.ts foo # single export detailsopenpkg snapshot ./src/index.ts # full spec JSONopenpkg diff old.json new.json # compare specsopenpkg docs spec.json # generate markdown
Five commands. Everything else is composition.
The article has a test I keep coming back to: to change behavior, do you edit prose or refactor code? If the answer is refactor code you've bundled logic that belonged in prompts.
I keep thinking about it like gardening. I was trying to build flowers, specific features, specific outcomes. What I shouldve been doing is growing the environment flowers come out of. Build the primitives, let the prompts do the composing.
This hit harder when I looked at another project I'd built on top of openpkg. Lots of logic, lots of features encoded in code. Turns out everything it did could be done by composing the primitives I already had, with prompts, not code.
More on that soon.