moshscript.com
Script your agents like shell scripts. Secretly, it's all JavaScript.
A .mosh file is real JS with the whole moshcode vocabulary already in scope.
#moshcoding #moshscript
curl -fsSL https://moshcode.sh/install.sh | shmoshscript lands on your PATH next to moshcode. No build step — it's plain ESM.
moshcode run script.mosh # run it
moshcode run script.mosh --dry-run # narrate every side effect, do none of them
moshcode commands # the whole vocabulary
moshcode help requireLogin # one verb, in detailThe simple surface stays dead simple:
while (alive) {
code();
mosh();
notify();
repeat();
} // no bugs, only featuresUnderneath it's real JavaScript, with every moshcode verb already in scope — no imports, no new syntax:
const engines = ["claude", "codex"];
for (const e of engines) install(e); // → moshcode install <e>
mcp("install", "https://mcp.sentry.dev/mcp");
agents("claude"); // drop into an autonomous sessionAdd a shebang and it runs like any shell script:
#!/usr/bin/env moshscript
install("claude");
agents("claude");chmod +x deploy.mosh && ./deploy.mosh --dry-run stagingSome verbs need an account — notify() and ask() reach a human through app.moshcode.sh, save()/load() sync your settings. Say so once instead of failing one call at a time:
const me = await requireLogin(); // verifies; runs the login flow if it must
say(`signed in as ${me.email} (${me.credits} credits)`);| verb | what it does |
|---|---|
await requireLogin() | the gate — verifies, logs in if needed, throws if it can't |
await login({ device, force }) | idempotent; returns { ok, email, already } |
await whoami() | the account as a value: { status, verified, user } |
logout() | forget this machine's credentials |
The flow follows the machine: the browser/loopback flow locally, the device-code flow over SSH or on a headless box — where a 127.0.0.1 callback would land on the browser's machine and never arrive. Credentials live in ~/.moshcode/credentials.json at mode 0600, the same ones moshcode login writes, so one login covers the CLI, the pit, and every script.
stocks report NVDA prints a table. A script usually wants the number — so the read verbs hand back parsed data from the same layer the printed commands render from:
const report = await stocksRead("report", "NVDA"); // JSON, or null
const btc = await cryptoRead("quote", "BTC/USD");
const items = await newsRead({ limit: 5 }); // [{ title, link, source, date }]
const screen = herdRead("api", { lines: 20 }); // a live agent's terminal
const roster = herdList(); // [{ name, engine, state }]A failed lookup returns null rather than throwing, so one bad symbol doesn't take the whole briefing down.
The pit keeps named shortcuts for the lines you retype. Scripts read and write the same store, so your vocabulary and moshcode's stay one thing:
alias("gs", "git status --short"); // define — reserved names are refused, not shadowed
alias("cc", "/agents claude"); // a leading `/` is a moshcode command
runAlias("gs", "--branch"); // → git status --short --branchExpansion follows the pit's rule: a leading / routes to the moshcode command of that name, anything else is a shell line, and arguments are appended rather than substituted — exactly how a shell alias behaves.
Sessions that outlive the script, driven in parallel:
herdStart("claude", { name: "api" });
herdStart("codex", { name: "web" });
herdPrompt("api", "port the auth routes");
herdPrompt("web", "port the dashboard");
await herdWait("api");
await herdWait("web");
say(herdRead("api", { lines: 20 }));notify("deploy finished 🤘"); // fire and forget
const next = await ask("ship it, or roll back?"); // BLOCKS for a real answerask() pings you across whatever channels you've configured — email, SMS, Slack, Telegram, push — surfaces a link at app.moshcode.sh/approve/:id, and blocks until you open it, read the context, and type back. The script resumes with your words.
CLI verbs and shell() return { ok, code } instead of throwing, so a long-running loop survives a single bad step:
const test = shell("pnpm -r test");
if (!test.ok) notify(`tests failed (exit ${test.code})`);And --dry-run narrates every action without performing any of it — no engine spawns, no installs, no network calls, no writes:
$ moshcode run deploy.mosh --dry-run
🎸 moshcode — running moshscript (dry run)
🔒 requireLogin() → would require an authenticated account
▶ install(claude) → would run: moshcode install claude
💬 ready to mosh with 2 engines
▶ agents(claude) → would run: moshcode agents claude
✓ 0 loop(s) — no bugs, only features. 🤘Every one of these ships in the repo and is safe to run with --dry-run first.
| example | what it shows |
|---|---|
alive.mosh | the starter loop |
scripting-the-cli.mosh | every CLI verb is just moshcode <cmd> … |
account.mosh | log in, gate on credits, sync settings, ask a human |
aliases.mosh | define and run the pit's shortcuts |
research-desk.mosh | stocksRead/cryptoRead/newsRead → one digest |
team-secrets.mosh | pull encrypted team secrets, then start coding |
moshcode run examples/research-desk.mosh --dry-run NVDA AMDJoin free, grab your link, and earn 80% commission on fees from everyone you refer. 90-day cookie — you get credited if they convert within 90 days.