Cyréna v0.4.0 – Major update to PlatformIO support + a question about project structure

Hi all,

Back in March I shared Cyréna here for the first time — an offline-first AI engineering agent with PlatformIO support. A lot has changed since then. v0.4.0 just shipped and the PlatformIO extension has been significantly improved. I also have a genuine question for the community about firmware project structure.


What’s new in the PlatformIO extension

The biggest change is that the extension now enforces a strict feature-based layout inside src/ and include/. One of Cyréna’s core ideas is that consistency comes from constraints, not prompts. Every supported stack has a structure the agent cannot deviate from — it uses dedicated file creation functions that place files in the correct location automatically. It physically cannot create files or folders outside the defined structure.

For PlatformIO projects, firmware is now organised into self-contained features — each hardware or software concern (display, sensors, networking, motor control) gets its own folder with a single public header as the entry point:

src/
  main.c / main.cpp
  {feature}/
    {feature}.c / {feature}.cpp
    actions/
    internals/

include/
  {feature}/
    {feature}.h
    definitions/
    actions/
    internals/

Folder responsibilities:

  • definitions/ — types, structs, enums, constants. Lives in include/ only, never in src/.
  • actions/ — function declarations in include/, implementations in src/.
  • internals/ — private headers in include/, private implementations in src/. Never exposed outside the feature.
  • {feature}.h — the single public entry point. Consumers include only this file.

Other improvements in 0.4.0:

  • The agent now properly identifies as a firmware engineer, not a generic software assistant
  • Embedded-safe rules enforced throughout — static allocation preferred, no dynamic memory, no recursion, no blocking delays, no desktop abstractions
  • platformio.ini, sdkconfig, and dependency metadata remain strictly read-only — dependency changes are flagged to the user with explicit instructions, never applied silently
  • The agent queries the active environment before making any architecture decisions — board, framework, MCU, and memory constraints are always grounded in reality, never guessed

The following are indexed as read-only — the agent has full awareness of these for context but cannot modify them:

Folder / File Purpose
lib/ Project libraries
managed_components/ ESP-IDF managed components
components/ ESP-IDF custom components
platformio.ini Project configuration
sdkconfig* ESP-IDF configuration files

My question to the community

PlatformIO projects are famously unstructured inside src/ and include/. Beyond the PlatformIO defaults, people do what works for them and there’s no real community standard.

The feature-based structure above is what I felt would work well for an AI agent — predictable, self-contained, with a clear public/private boundary per feature. But I designed it based on my own experience and instincts, not years of production embedded development.

What structure do you actually use in your PlatformIO projects? Does the feature-based approach resonate? What would you change? Is there a common pattern in the embedded community I’m missing?

The reason I’m asking is practical. Cyréna supports custom extensions and I want to build extensions for structures the community actually uses. If there’s a better or more widely adopted approach, I’ll build it. Your input will directly shape what gets built next.


Cyréna is free, open source, and runs fully offline via Ollama. OpenAI is also supported.

Full release notes and downloads: cyrena.dev
Source: github.com/Christo262/Cyrena_App

Would love to hear from anyone with experience structuring larger PlatformIO codebases.