Spark IDE
Status of programming Spark in an IDE today.
Today’s ./spark ELF is the GAS scaffold for IDE ops (asm/).
Authoring SoT DECIDED (A+B+C): destination self-host in Spark (A);
thin C bootstrap (B); Spark-native assembler (C). GAS is disposable —
not forever SoT. Do not start a Rust/Python VM from this lane.
Verified ops only — buffer, keymap, ask, show, and paint wire.
| Path | What it is | Status |
|---|---|---|
ide language ops |
ide new\|open\|save\|run\|buffer\|ask\|show |
Works — §1 |
| open→save→reopen | examples/ide_save_reopen.spark + sha256 |
Works — 11ab006 |
ide keys / ide key |
Keymap loop (quit/save/run/open/show) | Works — §1b |
| Paint bind | status path strip + gutter + AI strip → PPM | Works — b029a4a |
| Cursor workspace | make ide interim editor host |
Works — not the product |
CLI ./spark --dry-run / --live |
Primary runtime today | Works |
| Runtime rewrite | A+B+C DECIDED; GAS scaffold | In progress — see SELF_HOST.md |
1. IDE core ops (real on today’s scaffold)
Implemented in asm/ide_ops.s on the current ./spark binary
(kw_ide → keys lane first, else ide_ops_dispatch). Scaffold path on today’s ./spark binary.
| Op | Behavior |
|---|---|
ide new |
Clear in-memory buffer (+ optional "path" sets path, empty body) |
ide open "path" |
Load file into buffer; remember path |
ide save / ide save "path" |
Write buffer to path (out/ide/ mkdir as needed) |
ide buffer |
Terminal dump of buffer (display SoT for core) |
ide run |
Flush buffer to path or out/ide/buffer.spark; fork/exec ./spark |
ide ask ["instruction"] |
Buffer (+ optional quote) → real ask_run_prompt |
ide show ["path.ppm"] |
After paint: engine_window_show → dry validate / live ./spark-engine-show |
ide ask (AI strip): dry uses the same fixture picker as language
ask (pick_ask_reply_ptr — e.g. Gravity). Live forks
./spark-ask-http. Updates paint AI panel via ide_ai_set, then
rewrites out/ide/editor.ppm. Example: examples/ide_ask.spark.
ide show (display window): reuses the real engine B path
(asm/engine_window.s + companion tools/browser/spark_engine_show.c).
Default PPM is out/ide/editor.ppm (after open/ask paint). Dry-run
validates P6 and writes out/browser/show.json (no X11). --live forks
./spark-engine-show --ppm …. Optional quote must be a .ppm / .rgb.
Example: examples/ide_show.spark.
Paint wire (status strip b029a4a + dirty * + AI panel): core
exports ide_buf / ide_buf_len / ide_dirty. After new/open/
save, core binds paint and writes out/ide/editor.ppm (top
status path bar via ide_status_set + gutter + glyphs + cursor +
bottom AI strip). Buffer edit (ide new) sets dirty → status appends
* (also out/ide/status_dirty.txt); open/save clear dirty.
Paint owns ide_cursor, ide_status_set, and ide_ai_set. Standalone
make test-ide-paint / ide_status_strip_ppm prove the paint ELF.
Paint runs via the paint wire (make test-ide-paint), not as an ide paint statement. Proven keymap tokens are listed below.
Dry-run safe: parent --dry-run → child always ./spark --dry-run <file>.
Parent --live → child --live. The ide path does not open its own network connections.
E2E dry (usable now):
./spark --dry-run examples/ide_hello.spark
# ide.open → out/ide/editor.ppm (P6)
# ide.run → child ./spark --dry-run … + Gravity
./spark --dry-run examples/ide_ask.spark
# ide.open → editor.ppm; ide ask → fixture (Gravity) + AI strip
# live: ./spark --live examples/ide_ask.spark → spark-ask-http
./spark --dry-run examples/ide_show.spark
# ide.open → editor.ppm; ide show → engine validate + show.json
# live: ./spark --live examples/ide_show.spark → spark-engine-show
./spark --dry-run examples/ide_run_show.spark
# ide.open → editor.ppm; ide run → child; ide show → show.json
# live: ./spark --live examples/ide_run_show.spark → run + spark-engine-show
./spark --dry-run examples/ide_ask_show.spark
# ide.open → editor.ppm; ide ask → fixture + AI strip; ide show → show.json
# live: ./spark --live examples/ide_ask_show.spark → ask-http + spark-engine-show
./spark --dry-run examples/ide_save_reopen.spark
# ide.open → save → reopen; out/ide/ide_saved_rt.spark sha256==hello.spark
1b. Keymap / command loop (asm/ide_keys.s)
Dispatched before core when the line is ide keys or ide key ….
| Op | Behavior |
|---|---|
ide keys "script" |
Read command file list; run quit/save/run/open/show |
ide key quit\|save\|run\|open\|show ["path"] |
Single keymap command |
| Script tokens | q/quit, s/save, r/run, o path/open path, w/show |
Dry-run: prints {"op":"ide.keys",…} traces (and appends
out/ide/keys_trace.jsonl). Open still uses real open+read into
shared ide_buf, then paints out/ide/editor.ppm. Show dry-validates
via engine_window_show (live: ./spark-engine-show). Save/run do
not write/fork under dry-run (live: write + fork ./spark --dry-run).
./spark --dry-run examples/ide_keys.spark
# wire proof: ide key open … then ide key show → editor.ppm + show.json
./spark --dry-run examples/ide_keys_save.spark
# proven keys: script `s` → {"cmd":"save"} + path; dry no write
./spark --dry-run examples/ide_keys_run.spark
# proven keys: script `r` → {"cmd":"run"} + path; dry no fork
# landing `ff7b545` (example + cmds_run.txt + report stamp)
./spark --dry-run examples/ide_dirty_status.spark
# open clean → new dirty (*) → save clean; status_dirty.txt ends *
Proven script tokens:
| Token | Cmd | Dry | Live |
|---|---|---|---|
o path / open |
open | real read + paint | same |
s / save |
save | JSON trace only | write buffer |
r / run |
run | JSON trace only | fork ./spark --dry-run |
w / show |
show | validate PPM | ./spark-engine-show |
q / quit |
quit | end loop | end loop |
No n/new keymap token (core ide new only). No mouse GUI in this lane.
2. Open the Cursor workspace (editor host)
make ide
# equivalent:
./tools/open-spark-ide.sh
This opens Cursor on spark.code-workspace with .spark grammar + Bifrost.
It is not a substitute for the ide ops above.
Workspace folders
- Spark (language) — this repo
- Spark Browser —
../spark-browser
Tasks
| Label | Command |
|---|---|
spark: make |
make |
spark: test |
make test |
spark: dry-run hello |
./spark --dry-run examples/hello.spark |
spark: machine-proof |
make machine-proof |
spark: live ask |
./spark --live examples/ask_live.spark |
Agent card: AGENTS.md (../AGENTS.md in repo). Rules:
.cursor/rules/spark-ide-ai-coding.mdc, spark-language.mdc.
AI coding (gateway)
| Surface | Endpoint | Models |
|---|---|---|
| Cursor Override (when ON) | http://127.0.0.1:4010/cursor/v1 |
Bifrost aliases fast / code / code-bulk / code-max / best |
Terminal live ask |
AI_GATEWAY_URL=http://127.0.0.1:4000 + Bearer sk-bf-* |
model in .spark |
Never commit keys. Never route coding to voice GPU aliases.
Public Bifrost probes use Infisical $VK_PROBE only — ASK_LIVE.md (ASK_LIVE.md in repo).
3. CLI workflow (always the runtime SoT)
make
./spark --dry-run examples/hello.spark
./spark --dry-run examples/ide_hello.spark
make test
Live ask (only when you intend network + gateway):
export AI_GATEWAY_URL=http://127.0.0.1:4000
export OPENAI_API_KEY=…
./spark --live examples/ask_live.spark
ELF flags (from ./spark with no args): --dry-run, --live,
--allow-net, --allow-net-capture, --pstn-live, --version only.
4. Language coding loop (codegen — not buffer IDE)
./spark --dry-run examples/ide.spark
# writes out/program.spark via implement
Same ops as examples/review_builder.spark. Static review only — never
evals JS.
Related
- Program with Spark
- Self-host path (A+B+C)
- AGENTS.md (
../AGENTS.mdin repo) - Language reference
- ASK_LIVE.md (
ASK_LIVE.mdin repo) - Reports (local):
spark-ide-ask-20260831.md(dc54d92),spark-ide-show-20260831.md(efa2d90),spark-ide-ask-show-20260831.md(b58ab9f),spark-ide-status-strip-20260831.md(b029a4a),spark-ide-save-reopen-20260831.md(11ab006),spark-ide-dirty-status-20260831.md(dirty*afteride new),spark-ide-keys-run-20260831.md(ff7b545)