@damarals/insert-maker
v0.2.0
Published
Agent-guided board game insert design and fabrication toolkit
Downloads
476
Maintainers
Readme
Insert Maker
Insert Maker is an agent-guided toolkit for designing and fabricating board-game inserts. One repository ships the agent skill and the v0 Node.js runtime: a structured CLI, a project-scoped local server, and a read-only React viewer.
This repository is under active 0.x development. The current solver handles one or
more independently dimensioned folded trays with single, grid, or corner arrangements
of stacked, loose, or nested components. It packs every tray into the box floor, settles
physical components in-process when needed, then publishes a canonical 3D scene, a
reusable solved layout, and a dimensioned A4 cut-and-fold PDF that may use continuation
pages.
Requirements
- Node.js 24 or newer
- npm 11 or newer
No Python runtime, browser automation, database, daemon, or globally active project is required.
Clean-computer workflow
Start with Node.js 24 and npm 11, then install the runtime and the repository skill as two explicit products:
npm install -g @damarals/insert-makernpx skills add damarals/insert-maker --skill insert-maker -gOpen Claude Code or Codex and ask it to use the insert-maker skill. Give it the game,
the physical measurements you already have, and a concrete destination such as
/absolute/path/to/project. The agent creates that folder and writes a real
spec.toml; there is no initializer or empty scaffold.
For example: “Use the insert-maker skill to design an insert for Quartz in
/absolute/path/to/project. Ask only for physical measurements or design intent that
you cannot infer safely.”
The agent then records decisions and drives the structured runtime with the same explicit project path on every command:
insert-maker note /absolute/path/to/project --actor agent --message "Initial design intent captured from the user"insert-maker validate /absolute/path/to/projectinsert-maker solve /absolute/path/to/projectinsert-maker status /absolute/path/to/projectinsert-maker serve /absolute/path/to/projectserve stays attached to its foreground terminal session. The agent reports the URL
from the structured started event and keeps that session alive while the user views
the project; it does not launch a browser. Specification edits, validation, solves,
and status checks can continue in other command sessions. The viewer updates from the
project files and remains read-only.
The iteration is complete when status reports a fresh layout, no unresolved typed
bottleneck remains, floor and vertical use have been reviewed, and the fabrication
plan at /absolute/path/to/project/corte.pdf matches the intended design. The agent
uses CLI state and canonical geometry for its own evaluation; no visual capture or
approval gate is part of the v0 workflow.
Manual updates
Runtime and skill updates remain separate and user-initiated. Rerun both commands when you choose to update:
npm install -g @damarals/insert-makernpx skills add damarals/insert-maker --skill insert-maker -gProject folders
Commands accept an optional project folder and otherwise use the current directory. A
project begins with a hand-authored spec.toml; Insert Maker does not create a hidden
registry or scaffold. The folder is the complete portable contract: copy or move it
to another computer, point the same commands at its new path, and its durable intent,
observations, and journal move with it. No database, daemon, or machine-global project
record is involved.
[project]
game = "Quartz"
edition = "Retail edition"
[material]
thickness = 2
cutting_tolerance = 1
cutting_grid = 5
tape_per_face = 0.1
[box]
length = 268
width = 184
height = 68
[[top_layer]]
name = "Board and rulebook"
thickness = 11
[[tray]]
name = "Cards"
arrangement = "single"
[[tray.compartment]]
name = "Deck"
[[tray.compartment.component]]
name = "Sleeved cards"
quantity = 65
length = 91
width = 66
height = 0.45
shape = "box"
accommodation = "stack"
[[tray]]
name = "Tokens"
arrangement = "single"
[[tray.compartment]]
name = "Token well"
[[tray.compartment.component]]
name = "Wooden cubes"
quantity = 20
length = 25
width = 25
height = 2
shape = "box"
accommodation = "stack"Authoring and construction contracts
The canonical specification reference documents every v0 field, default, and constraint for single, grid, corner, stacked, loose, and nested designs. The complete example above is intentionally the only schema example in this README so the two documents do not drift independently.
The solved layout is the canonical construction state. It records each folded bottom
and wall, straight or folded divider, joint, lid, access cutout, compartment, component
pose, tray footprint, and orientation. The viewer scene and corte.pdf are projections
of that same state; neither reconstructs geometry independently.
Automatic physics and packing
When no compatible observation exists, solve runs multiple deterministic Rapier
experiments directly in the Node process. Compatibility is based on component shape,
quantity, dimensions, accommodation, compartment dimensions, and obstacle geometry;
display-name changes do not invalidate an observation. The representative experiment
records world-AABB pile heights, dispersion, spill factor, seed, and one settled pose
per item. The published scene uses those poses directly.
Tray tape-fit envelopes are packed at integer 0.1 mm resolution. Tray dimensions
round up, box dimensions round down, and each tray may remain unrotated or rotate by
90 degrees. The published layout records every position and orientation, verifies
containment and non-overlap, and reports useful height, reserved top-layer height,
occupied floor, free floor, and coverage.
The complete v0 file vocabulary is:
| File | Role | Behavior |
| --- | --- | --- |
| spec.toml | Durable design intention | Parsed and validated before every solve |
| medidas.json | Durable physical observations | Validated, reused by semantic geometry, and recalibrated automatically |
| layout.json | Regenerable solved layout | Canonical geometry, scene, semantic input fingerprint, and PDF digest |
| log.jsonl | Append-only project journal | Attributed notes, successful solves, and typed bottlenecks |
| corte.pdf | Regenerable vector cut plan | Potentially scaled A4 cut, score, fold, label, and dimension reference |
corte.pdf is a dimensioned fabrication reference, not a paper template. The printed
numeric dimensions are authoritative. Never trace or cut material from an assumed page
or printed scale; transfer the labeled measurements to the material and verify them
before cutting. A tray's folded blank, lid, and divider parts may move onto continuation
pages when they do not fit legibly together. The actual PDF page count is persisted in
layout.json under artifacts.cutPdf.pages.
validate, status, and serve are read-only. note appends one journal event.
solve publishes medidas.json when needed together with layout.json and
corte.pdf as one coherent generation, then records the successful solve in
log.jsonl. Failed calibration never leaks partial observations. A failed physical
solve records its bottleneck without replacing the last valid generation.
Commands
validate [project]
Parses and validates spec.toml without running physics or producing artifacts.
Standard output contains exactly one JSON record:
{"command":"validate","ok":true,"project":{"path":"/work/quartz","name":"Quartz"},"specification":{"valid":true}}An invalid project is also structured and identifies field paths:
{"command":"validate","ok":false,"project":{"path":"/work/quartz","name":"quartz"},"specification":{"valid":false,"issues":[{"path":"box.height","message":"Too small: expected number to be >0"}]}}solve [project]
Normalizes the specification, resolves and packs every tray, projects the 3D and
fabrication views, and publishes both artifacts. Progress is JSONL so an agent can
observe the operation without a separate --json mode:
insert-maker solve /work/quartzThe event order for a successful solve that can reuse every observation is:
started → normalized → solved → projected → completedIf calibration is required, one or more bounded rounds appear between normalization and solution:
started → normalized → measuring → measured → solved → projected → completedEvery event carries the command, outcome, and project identity. For example:
{"command":"solve","event":"started","ok":true,"project":{"path":"/work/quartz","name":"Quartz"}}The completed event contains the full published layout. Invalid specifications emit
one typed invalid event and do not publish partial artifacts.
Height, floor-packing, and empty-compartment failures end with a typed bottleneck
event and exit code 4:
{"command":"solve","event":"bottleneck","ok":false,"project":{"path":"/work/quartz","name":"Quartz"},"bottleneck":{"kind":"floor-packing","element":{"kind":"tray","id":"tray-1","name":"Tokens"},"axis":"width","amount":0.1,"unit":"mm","message":"Packing the trays in one row needs 0.1 mm more box width"}}The event identifies the responsible domain element and a concrete axis expansion
that makes a row packing feasible. It is also
appended to log.jsonl; it is not reported as an unexpected exception and does not
overwrite an existing layout.json or corte.pdf.
The solver first uses binpackingjs, then a deterministic bounded exact fallback when
the heuristic misses. If that fallback reaches its search budget without proving fit
or rejection, it reports a typed packing-search-limit bottleneck with the consumed
search-step count instead of turning resource exhaustion into an unexpected failure.
note [project] --actor user|agent --message <text>
Appends one attributed decision to the durable project journal and returns the exact persisted event. The project path remains optional for human use:
insert-maker note /work/quartz \
--actor agent \
--message "Reserve the top layer for rulebooks"{"command":"note","ok":true,"project":{"path":"/work/quartz","name":"Quartz"},"event":{"id":"a7ded28a-43dc-45c3-903f-c73f56f50992","timestamp":"2026-08-22T15:00:00.000Z","kind":"note","actor":"agent","message":"Reserve the top layer for rulebooks"}}Every line has a stable UUID, ISO timestamp, event kind, actor, and message. Insert Maker appends a complete JSON record in one operation and never rewrites earlier lines. A malformed existing event is rejected when project state is read rather than silently omitted or repaired.
status [project]
Reads the current project state without requiring a server and without changing file contents or timestamps. It reports specification validity, file presence, layout freshness, the latest bottleneck separately from preserved output, and the journal:
{"command":"status","ok":true,"project":{"path":"/work/quartz","name":"Quartz"},"specification":{"valid":true},"files":{"spec.toml":true,"medidas.json":false,"layout.json":false,"log.jsonl":false,"corte.pdf":false},"layout":{"state":"missing"},"latestBottleneck":null,"journal":[]}status exits successfully when a specification is invalid because invalidity is
inspectable project state that an agent can repair.
Layout state is deterministic:
missing: no committedlayout.jsonexists.fresh: the normalized intent fingerprint and PDF SHA-256 digest still match.stale: a previous layout is preserved, but semantic input changed, the current specification is invalid, or the PDF is missing or does not match its digest.
The fingerprint payload contains normalized design intent and compatible physical
observations; stack-only trays currently contribute no measurement observations. TOML
comments, whitespace, and key order do not change it, while a real dimension or design
edit does. layout.value remains available when stale so an agent can inspect the
previous solution, but it must not be treated as current.
serve [project]
Starts one foreground Hono server for exactly one project. It binds to 127.0.0.1,
tries port 4747, and advances until it finds an available port. Its first standard
output line is a JSONL startup event:
{"command":"serve","event":"started","ok":true,"project":{"path":"/work/quartz","name":"Quartz"},"host":"127.0.0.1","port":4747,"url":"http://127.0.0.1:4747"}The URL serves the bundled read-only viewer, GET /api/state, the root-relative
GET /api/events SSE stream, and GET /artifacts/corte.pdf. The permanent viewer
workbench includes the orbitable Scene with visibility controls and compartment
inspection, an aggregate resolved-measurement ruler, the Cut plan PDF, bottlenecks, and
the project journal. Preserved stale geometry stays visible behind an explicit
not-current notice. A PDF is served only when its digest matches layout.json. One
project watcher and event hub fan validated initial state and later project changes to
every connected viewer. The command does not open a browser.
Keep the terminal session open while using the viewer and press Ctrl+C to stop it.
Starting another project creates an independent process on the next available port; it
never switches the first process to a different folder.
Process contract
- Standard output is machine-readable JSON by default.
- Progressive operations such as
serveemit JSONL. - Diagnostics and command usage go to standard error.
- Commands do not prompt interactively.
| Exit code | Meaning |
| ---: | --- |
| 0 | Successful command |
| 1 | Unexpected runtime failure |
| 2 | Invalid invocation |
| 3 | Invalid project |
| 4 | Typed physical bottleneck |
Release
The package version is 0.2.0. A release is initiated only by pushing the matching
stable tag, such as v0.2.0; ordinary branch pushes never publish. Before creating a
tag, run the same release-candidate gate locally:
npm ci
npm run typecheck
npm test
npm run build
node scripts/verify-release.mjs v0.2.0The full test suite packs the package, installs it globally under an isolated prefix, exercises the CLI and bundled static viewer, solves the canonical project, and checks the skill layout. This proves the candidate before any publication tag exists.
One-time npm bootstrap
npm requires a package to exist before a trusted publisher can be assigned to it. Once
release.yml is on the default branch, use an npm account with package write access and
account-level 2FA to publish the disposable 0.0.0 bootstrap. The script builds the
project, stages only the package files allowlist, and changes only the staged package
version:
npm install --global [email protected]
npm login
node scripts/pack-bootstrap.mjs /tmp/insert-maker-bootstrap
npm publish /tmp/insert-maker-bootstrap/damarals-insert-maker-0.0.0.tgz --access public --dry-run
npm publish /tmp/insert-maker-bootstrap/damarals-insert-maker-0.0.0.tgz --access public
npm trust github @damarals/insert-maker --file release.yml --repo damarals/insert-maker --allow-publish
npm trust list @damarals/insert-makerThis authenticated publication happens once and does not use a GitHub Actions secret or create a release tag. After the trust relationship is visible, normal releases are tag-only:
git tag v0.2.0
git push origin v0.2.0The npm package must have one GitHub Actions trusted publisher with these exact fields:
| Field | Value |
| --- | --- |
| Organization or user | damarals |
| Repository | insert-maker |
| Workflow filename | release.yml |
| Environment | none |
| Allowed action | npm publish |
The tag-only workflow uses GitHub OIDC and stores no npm publication token. A read-only job installs exact dependencies, runs every gate, packs and dry-runs the exact tarball, then hands it off with a digest. Only the second, dependency-free job receives npm and GitHub publication authority. It verifies the handoff, stages a draft GitHub release, publishes the package with public access, then publishes the matching GitHub release. A retry verifies both the registry SHA-512 and any existing release asset before continuing, so interrupted cross-service publication cannot replace a different artifact. Repository-level immutable releases then lock the published tag and asset.
The development repository remains private. npm Trusted Publishing still works for a public package from a private repository, but npm does not generate provenance for private source repositories. See the npm Trusted Publishing documentation.
Development
Use Node.js 24 or newer for every command:
npm install
npm run typecheck
npm test
npm run buildnpm run build is the single build entry point for the Node runtime and Vite viewer.
Vitest is the only test runner. npm pack automatically performs a clean build before
creating the tarball.
