This file provides repository-wide guidance for coding agents working in this repository.
Stack-chan is a JavaScript-driven M5Stack-embedded robot. The codebase is primarily TypeScript/JavaScript built on the Moddable SDK platform for ESP32 microcontrollers.
- Host Program: Core firmware (
firmware/host/app/main.ts) that provides the robot framework - MODs: User applications that extend functionality (in
firmware/mods/directory) - Drivers: Hardware abstraction for different servo types (PWM, DYNAMIXEL, RS30X, SCServo)
- UI modules: Piu Application, views, drawer, status bar, bubbles, effects, and face components under
firmware/host/modules/ui - TTS Engines: Text-to-speech providers (local, remote, VoiceVox, ElevenLabs, OpenAI)
- Services: Background services (HTTP server, network, preferences)
firmware/host/: Core firmware source codefirmware/mods/: Modular applications that can be loaded at runtime- tests: Co-located under the target
firmware/host,firmware/mods, or platform implementation firmware/typings/: TypeScript definitions for the Moddable platformfirmware/dist/: Generated firmware programs and intermediate build files; do not edit or commit themcase/: 3D printable robot case filesschematics/: PCB designs for control boards
All commands should be run from the firmware/ directory:
npm run setup- Set up ModdableSDK and ESP-IDF using xs-devnpm run setup -- --device=esp32- Additional ESP32 setupnpm run doctor- Check development environment status
npm run build- Build firmware for M5StackChan CoreS3 (default target)npm run deploy- Build and flash firmware to connected devicenpm run debug- Build and flash with debug modenpm run mod- Flash a MOD to already-deployed firmware (fast development cycle)npm run bundle- Create a bundle of the firmwarenpm run clean- Remove generated files underfirmware/dist
- Use the repository npm scripts instead of invoking
mcconfig,mcrun, ormcpackdirectly. The wrappers supply the managed Moddable-oargument. - Normal host, MOD, and test builds write programs to
firmware/dist/bin/and intermediate files tofirmware/dist/tmp/. - The host application name is always
stack-chan-host. - Do not add a custom
-o; repository commands reject it to keep worktree output isolated underfirmware/dist. npm run cleanremoves all generated files underfirmware/dist.npm run bundlebuilds every release target underfirmware/dist/, stages validated target artifacts infirmware/dist/bundle-targets/, and writes the assembled directory and ZIP underfirmware/host/app/.- The named
npm run build:release:<target>scripts andnpm run bundle:packageare the CI building blocks for parallel target builds and final artifact assembly. npm run modusesmcrun -t buildto create an archive, then discovers and writes the live device'sxspartition withesptool.
npm run lint- Run Biome linternpm run lint:fix- Auto-fix linting issuesnpm run format- Check code formatting with Biomenpm run format:fix- Auto-format code
npm run scan- Scan for connected devicesnpm run erase-flash- Erase device flash memory
npm run generate-apidoc- Generate API documentation with TypeDoc
The default target is M5StackChan CoreS3. Select other supported hardware through the named npm scripts, for example:
npm run build:stackchan_rtnpm run build:takao_core2_sg90npm run flash:stackchan_rtnpm run flash:takao_core2_sg90
Do not use --target or npm_config_target; the firmware command wrapper rejects generic target overrides so that the matching platform and application manifest are selected together.
- Write MOD in
firmware/mods/withmanifest.jsonandmod.js - From
firmware/, usenpm run mod -- mods/your-mod/manifest.jsonfor rapid iteration - MODs can add behavior via
onLaunchandonContextCreatedhooks
Configuration is managed through preferences system with these key areas:
driver: Servo motor configuration (type: scservo, dynamixel, pwm, rs30x, none)tts: Text-to-speech engine selectionui: Piu UI and face selectionwifi: Network configuration
Uses lefthook for pre-commit hooks:
- Automatically runs linting and formatting on staged files
- Install:
npm run install-hook - Uninstall:
npm run uninstall-hook
Moddable test modules live under the target implementation with manifest.test.json; substantial tests get their own manifest for isolated execution.
Cheap constructor smokes are consolidated into shared manifests (firmware/host/modules/__tests__/module-smoke, firmware/mods/examples/provider-dialogues/__tests__/dialogue-smoke) because each manifest pays a full mcconfig build.
Node.js unit tests live next to pure helper implementations and run through npm run test:unit.
Prefer XS-driven Moddable tests for behavior that touches the platform (Piu, Timer, drivers); keep Node.js tests for pure logic.
Tests must verify observable behavior or relational invariants.
Do not:
- Read production source as text merely to assert exact constants, configuration values, code fragments, or regular-expression matches.
- Add tests that fail on a legitimate implementation-literal change without detecting a behavioral or architectural regression.
It is valid to:
- Parse generated artifacts or manifests and validate their schema.
- Compare independently maintained files or dynamically discovered entries.
- Verify dependency boundaries, completeness, tombstones, and relational invariants.
- Read source when its structure is itself the maintained contract.
If a constraint cannot be tested through behavior or a relational invariant, document the reason next to the source or configuration instead of adding a source-mirroring test.
When reviewing a pull request:
- Confirm the PR description classifies release impact as
none,patch,minor, ormajor - Check whether user-visible firmware or web changes need a release note or changeset entry
- If no release note or changeset is needed, make sure the review states why
- For docs, CI, repository metadata, case, and schematics changes, verify release impact before requesting a release note or changeset
- Ask for tested targets, hardware-specific behavior, and reproduction or verification details when they affect release risk