Imported from chrisdiana/breezybox-cardputer (
AGENTS.md). Install upstream withnpx skills add chrisdiana/breezybox-cardputer. Copyright stays with the author.
AGENTS
Project Split
What’s still in breezybox-firmware/:
- Project/build files:
breezybox-firmware/CMakeLists.txtbreezybox-firmware/Makefilebreezybox-firmware/partitions.csvbreezybox-firmware/sdkconfigbreezybox-firmware/sdkconfig.defaultsbreezybox-firmware/dependencies.lock
- App-specific firmware code in
breezybox-firmware/main:breezybox-firmware/main/main.c- Cardputer keyboard/input:
breezybox-firmware/main/cardputer_keyboard.cbreezybox-firmware/main/cardputer_keyboard.h
- Console/display bridge:
breezybox-firmware/main/my_console_io.cbreezybox-firmware/main/my_console_io.h
- Built-in app wrappers:
breezybox-firmware/main/cmd_vi_builtin.cbreezybox-firmware/main/cmd_plasma_builtin.cbreezybox-firmware/main/cmd_termbench_builtin.cbreezybox-firmware/main/cmd_wget_builtin.cbreezybox-firmware/main/cmd_gzip_builtin.cbreezybox-firmware/main/cmd_gunzip_builtin.cbreezybox-firmware/main/cmd_testgfx.cbreezybox-firmware/main/cmd_app_compat.c
- Loader symbol table patch:
breezybox-firmware/main/all_my_symbols.c
- Local overridden components that are still Cardputer-specific or patched:
breezybox-firmware/components/valdanylchuk__breezy_rgb_lcdbreezybox-firmware/components/valdanylchuk__breezy_termbreezybox-firmware/components/espressif__elf_loader
- Managed dependencies still fetched normally:
breezy_btlittlefszlib- cmake utilities
- under
breezybox-firmware/managed_components
- Runtime/build artifacts:
breezybox-firmware/fs_imagebreezybox-firmware/build
What is no longer in breezybox-firmware/ as the active shell core:
components/valdanylchuk__breezybox- that moved to
breezybox-cardputer
So the split is now:
breezybox-cardputer/= shell corebreezybox-firmware/= Cardputer firmware app, board glue, built-in app wrappers, patched display/term/loader components
Built-in app source ownership:
breezybox-cardputer/apps/now holds the local app sources that the firmware wrappers compile in:viplasmatermbenchwgetgzipgunzip
Scripting Direction
The old dedicated MicroPython runtime path is no longer part of the active product configuration.
The active replacement path is now:
- embedded Lua inside the main
breezybox-firmware/image - no second app partition
- no boot handoff between runtimes
- lower RAM and flash overhead than the old dual-runtime MicroPython model
Current active Lua surface:
lualua shelllua -e <chunk>lua <script.lua> [args...]- a built-in
breezyLua module for shell/filesystem basics breezy.gfxfor simple graphics-mode drawing from Lua
Treat micropython-cardputer/, MicroPythonShell-main/, and
Cardputer-MicroHydra/ as inactive reference material unless a future task
explicitly revives that path.
ELF Loader Note
Original Waveshare breezydemo could run external ELF apps because its memory layout and board assumptions were more favorable to the loader, including PSRAM-oriented expectations.
This Cardputer ADV port is different:
CONFIG_SPIRAMis not enabled in the active firmware build- runtime testing showed
EXEC free=0 largest=0before ELF launch - when the loader relocates into normal SRAM/DRAM instead, that code path is not executable here
- jumping to the relocated ELF entry causes
InstructionFetchError
So external ELF apps are currently not a reliable execution model on this Cardputer build. Built-in apps are the supported path unless the loader/memory strategy is redesigned.
claw (AI agent)
claw is an on-device AI agent. User documentation is docs/claw.md; the
design record, including the measurements behind it, is
docs/claw-architecture.md.
Where things live:
breezybox-cardputer/claw/agent core, C:claw_agent.crequest/response round plus the tool loopclaw_backend{,_anthropic,_openai,_gemini}.cone vtable, three providersclaw_config.csettings, SD-preferred with flash fallbackclaw_memory.clong-term memory: index injected per request, bodies on demandclaw_models.cmodel catalogue, read from JSON not compiled inclaw_session.cJSONL transcripts, replayed under a byte budgetclaw_sse.cincremental SSE parser, fixed buffersclaw_text.cmodel-facing text, overridable from SD with format-specifier validation (see the constraints below)claw_util.cshared small-file readerclaw_tools.ccapability registry, path confinement, destructive-action guard
breezybox-cardputer/cmd/claw.ctheclawconsole command and REPLbreezybox-cardputer/cmd/lua_https.cbreezy.httpsbinding (Lua-side TLS)packages/espclaw/root/apps/espclaw/data staged into the firmware image:ca/gts_root_r1.pem,models.json,lua_api.mdtools/gen_claw_text.pyregenerates the shipped default text (prompt.md,tools.json,messages.json) from the sources. Run it after changing any prompt, tool description or failure message in C.tools/gen_lua_api.pyregenerateslua_api.mdfrom the bindings incmd/lua.candcmd/lua_led.c, and folds in the usage block fromdocs/lua.md. Run it after changing any Lua binding or that block. The reference is generated rather than written so it cannot drift; a stale reference is worse than none, because the model believes it.tests/c/host tests for the C core;sh tests/c/run.sh. The backend tests compile the real backend sources against small stubs intests/c/stub/, so they check the shipped code rather than a copy. Run them after touching any backend: the request bodies are the contract with three different APIs, and a mistake surfaces as an unexplained HTTP 400 on the device.
Things worth knowing before changing it:
- The agent runs on the standard
cardputerbuild. Memory is still the binding constraint on a board with no PSRAM: the largest single allocation during a request is the 12 KB stream parser, and every change here should be weighed against the largest contiguous block available, not total free heap. claw_session_replay()returns neutral{role, content}turns so the transcript stays provider-agnostic. Backends append tool turns in their own native shape into the same array, so everybuild_bodymust pass native turns through untouched. Getting this wrong makes the model repeat a tool call forever; there is a guard for exactly that.- Tool failures must say what would have worked. The model cannot consult docs it does not have, so an error message is the documentation at that moment: a missing section lists the real ones, an unknown shell command lists the real ones, a nil module names the real ones. Several wasted rounds came from tools that reported failure without direction.
- All model-facing text -- prompt, tool and parameter descriptions, and the 47
failure messages -- is overridable from
/sd/claw/{prompt.md,tools.json, messages.json}viaclaw_text.c, so wording can be tuned without a flash. Defaults are generated bytools/gen_claw_text.py; run it after changing any of that text. Nothing is auto-seeded onto the card: an absent file must mean the compiled text, or a firmware update carrying better wording would silently have no effect. - Overriding a format string loses the compiler's check that it matches its
arguments, so
claw_text()refuses an override whose conversion specifiers differ from the compiled fallback. That check is the only thing preventing a typo from reading an integer as a pointer; it is tested intests/c/test_claw_text.c. claw_prompt.cdescribes the device on every request, and lists the shell commands by enumerating the live registry rather than repeating them, so the prompt matches the build. Anything the model reliably gets wrong belongs there rather than in a tool's error path, which only fires after a turn has already been spent.- Only the memory index is injected into requests (capped at
CLAW_MEMORY_INJECT_MAX); bodies are fetched withmemory_read. Injecting everything would spend the context budget on usually-irrelevant facts. - Anthropic takes the system prompt as a top-level field and rejects
role: "system"inside messages, so itsbuild_bodyhoists it out. Gemini hoists it tosystemInstruction. OpenAI takes it as a message. - Nothing may scale with conversation length in RAM. The request body is staged to disk and streamed; responses are parsed per SSE event and never buffered.
packages/espclaw/also contains a superseded Lua implementation of the agent, kept as the reference the C was ported from. Onlyca/,models.jsonandlua_api.mdare shipped.
Repo Layout
breezybox-firmware/active Cardputer ADV firmwarebreezybox-cardputer/apps/built-in app sources and related assetsbreezybox-cardputer/claw/AI agent core (see above)packages/espclaw/agent data files, and the superseded Lua prototype
Build Profiles
cardputeruniversal image for Cardputer and Cardputer ADVcardputer-advas above; kept for compatibility, the keyboard is detected at runtimecardputer-clawretained for testing a minimal build; not the shipping image.cardputernow runs the agent too, so there is one image to distribute. What made that possible: writing the request straight to a file instead of building it in memory (claw_json_write.c), 8 KB rather than 16 KB streaming buffers, andclawborrowing the 36 KB graphics framebuffer while it runs. Before those, the agent needed roughly 100 KB that the full build did not have.sticks3M5StickC S3
Current Limits
Important current limits:
- terminal geometry is fixed at
40x16 - on
cardputer-clawthere is one virtual terminal and no Bluetooth or SSH - claw tool support is implemented for all three providers, but only the Gemini path has been exercised on hardware
- runtime font scaling is not implemented
- external ELF app execution is not the primary supported path
lnsupports hard links onlysedsupports simple substitution form only:s/old/new/s/old/new/g