Mori writes real projects on your machine, runs them until they pass, and shows you the receipts.
When you ask Mori to build something, she follows a fixed sequence rather than improvising: map the codebase first (list_files, search_files, read_file — she never edits a file she hasn't read), then create the project folder, write every file with its full path, install dependencies if the project has any, and run it — run_terminal_command for a build or test, start_dev_server for a web app. She reads the command output; if it shows errors, she fixes the files and runs again until the build exits clean or the server is up. The turn ends with the preview opened so you see the result, not a description of it.
Two rules are hard-coded into her coding context: she never reports done without a successful run or preview in the same turn ("it should work" is treated as a failure state), and she never claims a file exists unless the file tool actually returned success. Everything runs locally — the code, the shell, the servers all live on your machine, and the only network call in the loop is to the model provider using your own API key.
Projects default to your Desktop unless you give a path. She won't open VS Code on her own; the UI gives you a button to open the folder, and she only calls the editor if you explicitly ask.
New code goes into files, never pasted whole into chat. For changes to existing files Mori uses edit_file — a surgical exact old-text to new-text replacement, not a whole-file rewrite. The edit fails loudly if the target text isn't found, and if it appears more than once she must either add surrounding context to make it unique or set replace_all explicitly, so an ambiguous edit can never silently land in the wrong place.
create_file followed by one append call per subsequent section. A large file emitted in a single call gets cut at the model's output-token cap; chunking makes truncation structurally impossible.rollback_file restores the most recent state, list_checkpoints shows the history, and a rollback snapshots the current state first so it can itself be undone.Under edit_file sits a dedicated edit engine built for the ways model-generated edits actually fail.
A successful edit reports the first changed line plus a real unified-style diff of what changed, computed from the on-disk before and after.
run_terminal_command executes through your login shell (zsh or bash on macOS/Linux), so PATH resolves the way it does in your own terminal — homebrew binaries, code, node version managers all work. Every result comes back in a fixed format: the command, the real exit code, and output truncated at 4,000 characters.
The default timeout is 300 seconds, adjustable per call from 5 up to 600 via timeout_seconds — builds and installs routinely take minutes, and the timeout exists so a hung process can't stall a task forever. A killed process is never reported as a normal failure: the output carries an explicit note that the command timed out and was killed, so a slow npm install doesn't read as a build error. Anything meant to run indefinitely belongs in start_dev_server instead.
The terminal is approval-gated in two tiers. Catastrophic commands — fork bombs, disk formats, raw disk writes, recursive deletes of protected roots — are blocked outright; Mori refuses and tells you to run them yourself if you truly mean it. Destructive-but-legitimate commands (sudo, git reset --hard, git clean -f, killall/pkill, recursive or forced rm, npm uninstall) trigger a native confirmation dialog before anything executes, on by default. The Stop button genuinely kills in-flight shell children — SIGTERM first, SIGKILL two seconds later if needed.
Command: npm run build
Exit code: 0
vite v5.2.0 building for production...
✓ 214 modules transformed.
dist/index.html 0.46 kB
# a timed-out command reports honestly:
[TIMED OUT after 300s — the command was killed; this is NOT a real
exit code. Long-running servers belong in start_dev_server; long
builds can pass timeout_seconds.]The same discipline governs everything that flows back to the model.
read_file or search_files the rest instead of losing it.read_file again with offset=313 to continue." There is no bare "[truncated]" for the model to misread as the end of the file.Between the two, no long output or large file ever vanishes behind an ellipsis: the model always knows exactly what it has seen, what it has not, and how to get the rest.
Web projects run under start_dev_server, which spawns the process for real and leaves it up — it is registered for emergency stop but never killed by a timeout. Mori waits a few seconds for the server to boot, captures the first output, and reports the pid plus the detected local URL (e.g. http://localhost:5173). If the process exits immediately, she gets the exit code and the tail of its output instead of a fake "started" message, and tries a different command.
list_dev_servers — every running server with pid, command, working directory, and uptime.stop_dev_server — stops by pid, or by a substring match against the command or directory; with no argument it stops everything.Servers persist across turns, so an iterating build keeps one server up rather than restarting it each time.
For big or risky jobs inside a git repo, Mori can work in a workspace — a real git worktree on its own branch, physically isolated in a .mori-worktrees/ directory so your checked-out tree is never touched mid-experiment.
create_workspace — makes a mori/<task>-<id> branch and its worktree directory, and switches Mori into it: shell, git, and relative file operations now default there.list_workspaces / enter_workspace / exit_workspace — see, resume, or leave workspaces; the list marks the active one and reconciles any whose directory has vanished.merge_workspace — commits pending work in the worktree, then merges the branch back into its base with a real merge commit; pass delete_after to clean up the worktree in the same step. Merge conflicts are reported with the repo path so they can be resolved, never papered over.remove_workspace — discards the worktree, but refuses if there are uncommitted changes unless you force it, and the branch itself is kept either way.Every build ends with the preview open. For a served project that is the dev-server URL; for static projects Mori resolves the best entry point automatically — index.html at the project root, then the common build outputs (dist/, build/, public/, out/), then any loose .html file in the folder root, which covers single-file games. The result renders as a file:// page in the app's preview drawer, so "done" is something you can look at, not take on faith. Paths written with ~ are expanded before resolution, so a project logged as ~/Desktop/my-game previews correctly.
Every assistant message that did work carries a collapsed activity record — a single "Worked — N steps" header with a summary of the top actions ("12 searched · 8 read · 3 wrote") and an error count if anything failed. Expanding it shows the full detail: one chip per tool call with its status, repeated identical calls grouped with a multiplier, and a diff card for every file edit — the filename, +added/−removed counts, and an expandable before/after view with the actual changed lines. The record is saved on the message, so the audit trail persists in your chat history.
On top of the UI record, a post-turn verifier re-checks Mori's claims against reality after any turn that changed things. Each successful file creation is probed on disk, each created folder is listed, each deleted file is confirmed gone, and each started dev server has its port probed — up to 12 checks per turn so verification never balloons a reply. The verdict is appended to the message itself.
✓ Verified: 6/6 changes confirmed on disk.
# and when something cannot be confirmed, it says so:
⚠️ Verification: 5/6 confirmed — could not verify: game.js.