title: "Scripts: the side assistant" description: "The scripts folder, the explorer, the editor, the console and the run lifecycle." nodes: 18 progress: 89% generated: ohm spec editor#

Scripts: the side assistant#

The scripts folder, the explorer, the editor, the console and the run lifecycle.

Contents#

☑ Overview#

☑ Scope#

note

packages/core/src/{script-files,script-service,examples}.ts, the Scripts panel, the Monaco editor integration and the console.

☑ Scripts are plain files in a folder the user owns#

decision

Documents/Ohm by default, overridable with OHM_SCRIPTS_DIR. No database, no hidden store: the same folder is visible in the OS file manager, works with git, and is where projects live too.

The one reserved area is .ohm/, which holds memory, the trash and screenshots and is hidden from the explorer.

  • spec: 12-storage

☑ The file surface#

☑ ScriptFiles#

api #security

ensureRoot(seed), list(), read, write, exists, create(rel, 'file' | 'dir'), delete, rename, listDir, exportTo, importFrom.

Every path is resolved against the root and rejected if it escapes it; the reserved .ohm directory is refused as well, so neither the UI nor a script can reach it through the normal API.

☑ Delete means trash#

requirement #safety

Deleting moves the item to .ohm/trash/<timestamp>-<name> rather than unlinking it. A misclick in a file tree should never be final.

☑ Import and export never overwrite#

requirement

Importing a folder copies it in under a unique name, and exporting writes a tree to a chosen folder. A name collision gets a suffix instead of clobbering, and the copy is bounded by a file-count budget so a mistaken pick cannot run away.

  • node: script-files

☑ First run seeds examples#

requirement #regression

An empty scripts folder is seeded with runnable examples — ai/ (agent, compose, parallel, research), models/ (list, download GGUF, download a web model, compare profiles), browse.ohm, math.ohm, memory.ohm, welcome.ohm and a README.md that doubles as the language reference. Settings has a "restore examples" action that re-seeds without touching the user's own files.

A test compiles every bundled example, so a language change that breaks the seeds fails the build.

  • spec: 02-ohm-language

☑ Running#

☑ A run#

flow

scripts.run accepts a path or inline source, allocates a runId, compiles, and streams:

  • compile diagnostics as err lines formatted name:line:column: message,
  • output events (out, err, info, warn, result) as they happen,
  • a final scripts.status with the outcome.

A script under projects/<id>/ is automatically bound to that project; a loose script uses the active project and says so clearly when there is none.

☑ The console is a persistent session#

requirement

The console runs in its own session whose Lua globals persist between lines, which is what makes it a REPL: assign a variable on one line and use it on the next. It accepts both Lua and math ($\frac{22}{7}$), and a bare expression prints its value.

Stopping resets the session and says so on the info stream, because terminating the worker necessarily discards the globals.

  • spec: 02-ohm-language — the worker runner and stopping

☑ Run history is bounded#

requirement

Finished runs are pruned to 150 entries once 200 accumulate, so a long session cannot grow without limit.

☑ Three views of one file#

component #regression

The editor toolbar switches between Code (Monaco, editable), Math (the LaTeX typeset) and Lua (the compiled output). Monaco stays mounted while the typeset view is shown, so a toggle does not lose the cursor, selection or undo history.

The typeset view is produced by renderOhm in @ohm/ohm-lang, which walks the compiler's own AST:

  • It knows Ohm's operators — \CoT_{\Psi} renders as CoT with a Ψ subscript — which a general LaTeX renderer cannot, since they are not LaTeX commands.
  • It is honest: a region the compiler would reject renders as its source plus the error, never as pretty output.
  • It emits a node tree, not HTML, and the UI maps that to MathML, so nothing is injected and no font or typesetting library ships.

Reading choices worth preserving: / and \frac both become a fraction; \sqrt[n]{x} reads as a root again although the parser rewrote it to a power; x_i stays a subscript while m["key"] becomes a bracket index; and clicking a formula flips that one back to its LaTeX.

Two rendering details that were each a visible bug: MathML puts no gap between two adjacent <mi>, so a keyword needs an explicit <mspace> or if collides with its condition; and a <math display="block"> centres its content, which is wrong for a program, so the box is shrunk to its content with width: fit-content (text-align does not reach MathML layout).

  • spec: 02-ohm-language

☑ Completion, hover and signature help#

component

Monaco providers, all driven by EditorSettings pushed in from the renderer:

  • Completion after \ offers commands with snippets, every operator with its family and signature, math functions, and library entries as \Use calls; after obj. it offers that object's host API, and after lib. the entries that actually exist.
  • Hover explains operators, commands, host API members and library entries, showing an entry's body.
  • Signature help finds the call the cursor sits in by scanning back for an unclosed (, and highlights the argument by counting commas — so it works for \CoT_{\Psi}(q; C) as well as tab.waitFor(sel, ms).

Every one of these is switchable, and "insert placeholders" decides whether a completion inserts tab stops or plain text.

  • spec: 04-models-and-settings

☑ The editor#

component

Monaco with Ohm syntax highlighting, completion after \ and after browser. / tab., live syntax errors from the real compiler, the Code/Math/Lua switch, F5 to save and run, Ctrl+S to save, Shift+F5 to stop.

Because compilation preserves line numbers, clicking an error in the console jumps to the right line of the .ohm source.

  • spec: 02-ohm-language — line-for-line compilation

☑ Review findings#

☐ Open a .ohm file passed on the command line#

task

Nothing reads process.argv for a file path, so a double-clicked script would open an empty window. This is the prerequisite for registering the .ohm file association in the installers.

  • spec: 13-packaging

☐ The editor API table is out of date#

note low

The completion table documents browser, tab, ai, specs, models, fs, json and ohm but omits project, prompts, curves and experiments, which the runtime does provide — so completion under-reports the real surface.

  • spec: 02-ohm-language
graph LR
  import_and_export_never_overwrite["Import and export never overwrite"] -.-> script_files["ScriptFiles"]