⚡ powered by #moshcoding

moshscript.com

moshscript IS ALL JS

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

Install

curl -fsSL https://moshcode.sh/install.sh | sh

moshscript 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 detail

Secretly all JS is legal

The simple surface stays dead simple:

while (alive) {
  code();
  mosh();
  notify();
  repeat();
} // no bugs, only features

Underneath 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 session

Add 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 staging

Sign in, once, at the top

Some 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)`);
verbwhat 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.

Values, not screen-scraping

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.

Your shortcuts, from a script

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 --branch

Expansion 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.

Fan out a herd, then read what came back

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 }));

Put a human in the loop

notify("deploy finished 🤘");                  // fire and forget
const next = await ask("ship it, or roll back?");  // BLOCKS for a real answer

ask() 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.

Nothing throws that doesn't have to

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. 🤘

Worked examples

Every one of these ships in the repo and is safe to run with --dry-run first.

examplewhat it shows
alive.moshthe starter loop
scripting-the-cli.moshevery CLI verb is just moshcode <cmd> …
account.moshlog in, gate on credits, sync settings, ask a human
aliases.moshdefine and run the pit's shortcuts
research-desk.moshstocksRead/cryptoRead/newsRead → one digest
team-secrets.moshpull encrypted team secrets, then start coding
moshcode run examples/research-desk.mosh --dry-run NVDA AMD

Promote moshscript.com — earn 80%

Join 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.

💰 Bid on this domain