The edit lifecycle: author → apply/regenerate → implement → test → iterate¶
just-makeit.toml is the manifest. Every CLI verb (object, method,
add, …) writes to it, then materializes files. You can also edit the TOML by
hand. The verbs treat your files differently — this is the
sacred/glue contract:
| File | Class |
|---|---|
<comp>_ext.c |
Glue — always regenerated from the manifest; never hand-edited |
src/<pkg>/<comp>.pyi |
Glue — always regenerated |
CMakeLists.txt |
Glue — always regenerated |
<comp>_core.c |
Sacred — created once; a structural change rebuilds it via jm regenerate, which lifts your hand-written bodies out and splices them back in by function name (--discard for a clean reset instead) |
<comp>_core.h |
The state struct + inline step() are sacred; method/property declarations refresh from the TOML |
The additive verbs never touch an existing body in place — they only inject what's missing:
jm method, computedjm property, andjm functionare additive — they inject one declaration into_core.hand append a fresh stub to_core.c. Existing bodies are never touched. A field-backedjm property --fieldinjects one struct member directly.jm add(adding state) is structural — it writes[[obj.state]]to the manifest, then rebuilds the object via the regenerate path with a clean reset (--discard), so it does discard hand-written_core.cbodies and the inlinestep()body in_core.h(see below) — same forjm remove --state.jm applyinjects any TOML-declared declaration missing from_core.hand keeps the struct +step()sacred. A state-field change or a signature change is structural →jm regenerate.
So the flow is:
- Author — run a CLI verb, or hand-edit
just-makeit.toml. - Apply / regenerate —
jm applyrefreshes the glue and injects missing declarations; a structural change (new state field, changed signature) needsjm regenerateto rebuild the object. - Implement — fill in the new
step()/steps()/method body in_core.c. - Test —
make test. - Iterate — back to step 1.
You only ever own _core.c and the TOML.
When you change a signature in TOML (an arg type, a method's return type),
or add a state field, the structure of the object changed — rebuild it from
the manifest with jm regenerate:
git stash # safety net — see below
just-makeit regenerate gain # deletes every file 'gain' owns, re-runs apply
regenerate deletes every file the component owns and rebuilds it from the
manifest, then asks for a single confirmation (--force skips it). Unlike
jm remove, it leaves the manifest untouched — it is the deliberate-rebuild
half of the contract. By default it lifts your hand-written _core.c/
_core.h bodies (create/destroy/reset/step()/getters/setters/methods) out
before deleting the files, and splices them back into the freshly generated
ones by function name — --discard skips that and does a clean reset
instead. The lift/splice is best-effort (a changed signature, e.g. a new
parameter, means the fresh body wins instead), so git stash first is still
good practice, not a requirement. Works for standalone and module objects.
Lifting an existing C body with --impl¶
When the algorithm already exists in another .c file, --impl lifts it into
the generated stub instead of having you paste it:
just-makeit object gain --arg-type float --return-type float \
--state gain:float:1.0 \
--impl legacy/dsp.c::apply_gain
--impl file::funcname injects the body of funcname. --impl file::N:M
lifts source lines N..M (inclusive, 1-based) instead — useful when there is
no clean function to name; out-of-bounds or inverted ranges error cleanly.
--replace old::new applies string substitutions before injection (e.g.
renaming a struct field). The same keys exist in TOML: impl, impl_file
("path::funcname" or "path::N:M"), create_impl, reset_impl,
destroy_impl. Because _core.c is sacred, lifting is safe — apply never
clobbers what you injected.