moshcode 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +580 -0
  3. package/bin/moshcode.mjs +674 -0
  4. package/bin/moshscript.mjs +29 -0
  5. package/examples/alive.mosh +6 -0
  6. package/examples/scripting-the-cli.mosh +21 -0
  7. package/examples/team-secrets.mosh +20 -0
  8. package/examples/templates/bun-caddy-sqlite/.env.example +14 -0
  9. package/examples/templates/bun-caddy-sqlite/Caddyfile +18 -0
  10. package/examples/templates/bun-caddy-sqlite/README.md +97 -0
  11. package/examples/templates/bun-caddy-sqlite/deploy/moshcode-dns.service +39 -0
  12. package/examples/templates/bun-caddy-sqlite/deploy/moshpit-service.service +38 -0
  13. package/examples/templates/bun-caddy-sqlite/package.json +15 -0
  14. package/examples/templates/bun-caddy-sqlite/src/db.ts +47 -0
  15. package/examples/templates/bun-caddy-sqlite/src/server.ts +44 -0
  16. package/examples/templates/bun-caddy-sqlite/template.json +10 -0
  17. package/examples/templates/caddy-proxy/Caddyfile +36 -0
  18. package/examples/templates/caddy-proxy/README.md +104 -0
  19. package/examples/templates/caddy-proxy/deploy/moshcode-dns.service +39 -0
  20. package/examples/templates/caddy-proxy/template.json +8 -0
  21. package/examples/templates/caddy-static/Caddyfile +16 -0
  22. package/examples/templates/caddy-static/README.md +90 -0
  23. package/examples/templates/caddy-static/deploy/moshcode-dns.service +39 -0
  24. package/examples/templates/caddy-static/site/index.html +11 -0
  25. package/examples/templates/caddy-static/template.json +8 -0
  26. package/install.sh +194 -0
  27. package/package.json +28 -0
  28. package/prd/0000-template.md +49 -0
  29. package/prd/0001-wrap-ugig-and-coinpay-clis.md +121 -0
  30. package/prd/0002-separate-agent-and-raw-engine-launches.md +113 -0
  31. package/prd/0003-cross-engine-mcp-and-skill-installation.md +165 -0
  32. package/prd/0004-moshscript-run-programmable-moshcode.md +344 -0
  33. package/prd/0005-hosted-moshpit-resolver.md +192 -0
  34. package/prd/0006-help.md +359 -0
  35. package/prd/0007-profullstack-site-init.md +1183 -0
  36. package/prd/README.md +26 -0
  37. package/src/ads.mjs +58 -0
  38. package/src/auth.mjs +193 -0
  39. package/src/cli-schema.mjs +533 -0
  40. package/src/cli.mjs +118 -0
  41. package/src/commands.mjs +259 -0
  42. package/src/completion.mjs +594 -0
  43. package/src/console.mjs +244 -0
  44. package/src/dns-system.mjs +404 -0
  45. package/src/dns.mjs +2872 -0
  46. package/src/doh-server.mjs +256 -0
  47. package/src/doh.mjs +218 -0
  48. package/src/engines.mjs +385 -0
  49. package/src/escalate.mjs +85 -0
  50. package/src/help.mjs +443 -0
  51. package/src/integrations.mjs +265 -0
  52. package/src/mcp-catalog.mjs +50 -0
  53. package/src/mcp.mjs +155 -0
  54. package/src/mirror.mjs +187 -0
  55. package/src/notify.mjs +86 -0
  56. package/src/open-url.mjs +34 -0
  57. package/src/parking-http.mjs +65 -0
  58. package/src/pins.mjs +190 -0
  59. package/src/pit-url.mjs +13 -0
  60. package/src/prd.mjs +341 -0
  61. package/src/pty.mjs +176 -0
  62. package/src/pwd.mjs +103 -0
  63. package/src/registry.mjs +37 -0
  64. package/src/release-install.mjs +191 -0
  65. package/src/runtime.mjs +161 -0
  66. package/src/selfupdate.mjs +215 -0
  67. package/src/serve.mjs +502 -0
  68. package/src/skills.mjs +93 -0
  69. package/src/tabs.mjs +144 -0
  70. package/src/templates.mjs +456 -0
  71. package/src/tools.mjs +231 -0
  72. package/src/trade.mjs +137 -0
  73. package/src/trust.mjs +712 -0
  74. package/src/tui.mjs +736 -0
  75. package/src/ui.mjs +49 -0
  76. package/src/uninstall.mjs +113 -0
  77. package/src/upgrade.mjs +217 -0
@@ -0,0 +1,165 @@
1
+ ---
2
+ openprd: "0.2"
3
+ id: "0003"
4
+ title: Install MCP servers and skills across every engine at once
5
+ status: Accepted
6
+ authors:
7
+ - anthony@chovy.com
8
+ created: 2026-07-13
9
+ updated: 2026-07-13
10
+ repo: https://github.com/moshcoder/moshcode
11
+ discussion:
12
+ implementation: src/mcp.mjs, src/skills.mjs, src/integrations.mjs
13
+ tags:
14
+ - cli
15
+ - mcp
16
+ - skills
17
+ - integrations
18
+ - orchestration
19
+ supersedes:
20
+ superseded-by:
21
+ ---
22
+
23
+ ## Problem
24
+
25
+ Every coding engine MoshCode conducts (Claude, Gemini, Codex, OpenCode, Aider)
26
+ maintains its own registry of MCP (Model Context Protocol) servers, each with a
27
+ different config location, format, and `mcp add` flag surface. A user who wants
28
+ the same MCP server available in all of their agents has to run four different
29
+ commands and remember four different flag dialects. The same fragmentation is
30
+ starting to appear for Agent Skills. MoshCode already conducts these CLIs' native
31
+ command lines (see [[0001-wrap-ugig-and-coinpay-clis]]); it is the natural place
32
+ to register a server or skill *once* and fan it out to every engine that can use
33
+ it.
34
+
35
+ ## Goals
36
+
37
+ - Register an MCP server across every installed MCP-capable engine with one
38
+ command, defaulting to global (user) scope.
39
+ - Translate a single canonical server definition into each engine's native
40
+ `mcp add` invocation, rather than hand-writing four config formats.
41
+ - Report per engine what happened: added, skipped (can't express it), failed, or
42
+ not installed — and flag servers that still need per-engine authentication.
43
+ - Offer the equivalent one-shot install for Agent Skills to the engines that have
44
+ a skills primitive.
45
+ - Never silently drop an engine: an engine that cannot express a requested server
46
+ MUST be reported, not omitted.
47
+
48
+ ## Non-Goals
49
+
50
+ - Implementing MCP or skills for engines that lack the primitive (Aider has
51
+ neither; Codex/OpenCode have no skills concept).
52
+ - Performing per-engine OAuth/login flows on the user's behalf (MoshCode registers
53
+ the server; the user authenticates in each engine).
54
+ - Editing engine config files directly — MoshCode drives each engine's own
55
+ `mcp add` / `skills install` so the engine owns its config format.
56
+ - Managing MCP server processes/runtime, health, or tool routing.
57
+ - Reconciling or de-duplicating servers an engine already has.
58
+
59
+ ## Users
60
+
61
+ - Developers who run more than one coding agent and want the same MCP tools
62
+ (Sentry, GitHub, a company internal server) available in all of them.
63
+ - Teams standardizing a set of skills/servers across engines.
64
+ - Scripts that need the same fan-out outside the MoshCode TUI.
65
+
66
+ ## Requirements
67
+
68
+ - R1 [P0] A new command MUST register an MCP server across every installed
69
+ MCP-capable engine: `/mcp install <commandOrUrl>` and
70
+ `/mcp add <name> <commandOrUrl> [args…]` in the TUI, and `moshcode mcp …`
71
+ outside it.
72
+ - R2 [P0] The MCP-capable engine set MUST be Claude, Gemini, Codex, and OpenCode.
73
+ Aider MUST be treated as having no MCP support.
74
+ - R3 [P0] A canonical server definition — name, target (URL or stdio command +
75
+ args), transport, env vars, and headers — MUST be translated per engine into
76
+ its native `mcp add` argv:
77
+ - Claude: `claude mcp add -s user [-t <transport>] [-e K=V…] [-H "K: V"…] <name> <commandOrUrl> [args…]`
78
+ - Gemini: `gemini mcp add -s user [-t <transport>] [-e K=V…] [-H "K: V"…] <name> <commandOrUrl> [args…]`
79
+ - Codex: `codex mcp add <name> (--url <url> | -- <command> [args…]) [--env K=V…]`
80
+ - OpenCode: `opencode mcp add <name> --url <url> [--env K=V…] [--header K=V…]`
81
+ - R4 [P0] Registration MUST default to global/user scope on every engine
82
+ (`-s user` where the engine offers it; the engine's user-level config
83
+ otherwise).
84
+ - R5 [P0] `/mcp install <url>` MUST accept a bare remote URL, derive a sane
85
+ default server name from its host, and default the transport to `http`.
86
+ An explicit `--name` MUST override the derived name.
87
+ - R6 [P0] When an engine cannot express the requested server (e.g. OpenCode with
88
+ a stdio command, or Codex with literal headers), MoshCode MUST skip that engine
89
+ with a stated reason and still register the others.
90
+ - R7 [P0] Header syntax MUST be normalized per engine: Claude/Gemini `"Key: Value"`,
91
+ OpenCode `Key=Value`. Codex, which supports only a bearer-token env var, MUST be
92
+ skipped-with-reason when literal headers are supplied.
93
+ - R8 [P1] A `/skill install <git-url|path>` command MUST fan a skill out to every
94
+ installed engine that has a skills primitive: Gemini via
95
+ `gemini skills install <source> --scope user`, and Claude by cloning the source
96
+ into `~/.claude/skills/<name>`. Engines without a skills primitive MUST be
97
+ reported as skipped.
98
+ - R9 [P1] Every fan-out MUST print a per-engine summary (added / skipped+reason /
99
+ failed / not-installed) and MUST call out any server that still requires
100
+ authentication in a given engine.
101
+ - R10 [P1] A removal path (`/mcp remove <name>`, `moshcode mcp remove <name>`)
102
+ MUST fan the corresponding native `mcp remove` out to the same engines.
103
+ - R11 [P1] `/mcp` and `/skill` with no arguments MUST list the target engines and
104
+ their MCP/skills support + install status.
105
+ - R12 [P1] The registration plan (engine → argv or skip-reason) MUST be a pure,
106
+ unit-tested function, mirroring the existing upgrade-plan tests, so adapters are
107
+ verified without spawning real engines.
108
+
109
+ ## UX Notes
110
+
111
+ ```sh
112
+ # Remote HTTP server, global, everywhere that supports MCP:
113
+ moshcode mcp install https://mcp.sentry.dev/mcp
114
+ # ✓ claude added (-s user -t http)
115
+ # ✓ gemini added
116
+ # ✓ codex added (--url)
117
+ # ✓ opencode added (--url)
118
+ # · aider skipped — no MCP support
119
+
120
+ # Explicit name + stdio command server:
121
+ moshcode mcp add my-tools -- npx -y my-mcp-server
122
+ # ✓ claude added
123
+ # ✓ gemini added
124
+ # ✓ codex added
125
+ # · opencode skipped — CLI adds only remote (--url) servers non-interactively
126
+
127
+ # A skill, to the engines that have skills:
128
+ moshcode skill install https://github.com/acme/some-skill
129
+ # ✓ claude cloned into ~/.claude/skills/some-skill
130
+ # ✓ gemini installed (--scope user)
131
+ # · codex/opencode/aider skipped — no skills primitive
132
+ ```
133
+
134
+ TUI equivalents are `/mcp install …`, `/mcp add …`, `/mcp remove …`, and
135
+ `/skill install …`. Bare `/mcp` and `/skill` print the support matrix.
136
+
137
+ The per-engine translation is intentionally not uniform: Claude and Gemini share
138
+ a `mcp add <name> <commandOrUrl>` shape with `-t`/`-H`; Codex distinguishes
139
+ remote from stdio with `--url` vs `-- command` and only carries a bearer-token env
140
+ var; OpenCode registers remote servers by `--url` and uses `Key=Value` headers.
141
+
142
+ ## Success Metrics
143
+
144
+ - One `moshcode mcp install <url>` registers the server, at user scope, in every
145
+ installed MCP-capable engine, with a per-engine result line.
146
+ - No engine is silently omitted: unsupported combinations are reported with a
147
+ reason.
148
+ - The plan function returns the correct native argv (or skip reason) for each
149
+ engine and combination, verified by tests without spawning engines.
150
+ - The full MoshCode test suite passes.
151
+
152
+ ## Risks & Open Questions
153
+
154
+ - Engine `mcp add` flags can change between releases; their own `--help` remains
155
+ authoritative and the adapters need maintenance when a CLI renames a flag.
156
+ - Secrets (env values, `Authorization` headers, bearer env-var names) flow through
157
+ MoshCode into each engine's config; MoshCode MUST NOT log their values.
158
+ - Claude skill install by `git clone` assumes the source repo is a skill (a
159
+ `SKILL.md` at the root or a known sub-path); non-skill repos are out of scope
160
+ for v1.
161
+ - Should MoshCode detect and skip servers an engine already has, or let the native
162
+ `mcp add` error surface? v1 surfaces the native result; de-dup is deferred.
163
+ - OAuth-gated servers register but remain unauthenticated until the user runs the
164
+ engine's own auth (`opencode mcp auth`, `codex mcp login`); MoshCode only points
165
+ this out.
@@ -0,0 +1,344 @@
1
+ ---
2
+ openprd: "0.2"
3
+ id: "0004"
4
+ title: moshscript — a scriptable /run for driving all of moshcode programmatically
5
+ status: Accepted
6
+ authors:
7
+ - anthony@chovy.com
8
+ created: 2026-07-13
9
+ updated: 2026-07-13
10
+ repo: https://github.com/moshcoder/moshcode
11
+ discussion:
12
+ implementation: src/runtime.mjs, src/registry.mjs, src/commands.mjs, src/cli.mjs, src/tui.mjs, bin/moshcode.mjs
13
+ tags:
14
+ - moshscript
15
+ - cli
16
+ - scripting
17
+ - automation
18
+ - dsl
19
+ supersedes:
20
+ superseded-by:
21
+ ---
22
+
23
+ ## Problem
24
+
25
+ moshcode already ships a `/run <file.mosh>` command and a tiny moshscript
26
+ runner (`src/interpreter.mjs` + `src/commands.mjs`), but the language is a toy.
27
+ Its grammar is one construct — `while (alive) { … }` — and its vocabulary is
28
+ seven cosmetic builtins (`code()`, `mosh()`, `notify()`, `repeat()`, `say()`,
29
+ `sleep()`, `stop()`). None of them actually *do* anything moshcode does: a
30
+ script cannot launch an engine, install a tool, publish a PRD, or fan an MCP
31
+ server across engines. Everything moshcode is good at — conducting Claude,
32
+ Codex, Gemini, OpenCode and Aider, and the ugig/coinpay/c0mpute workflow CLIs
33
+ (see [[0001-wrap-ugig-and-coinpay-clis]], [[0002-separate-agent-and-raw-engine-launches]],
34
+ [[0003-cross-engine-mcp-and-skill-installation]]) — is only reachable by a human
35
+ typing at the mosh pit prompt.
36
+
37
+ The pitch, in the user's words: *"very simple scripting language (although
38
+ secretly all js is legal), but very basic commands like `code(); mosh();
39
+ notify(); repeat();` — we can basically do everything moshcode cli supports but
40
+ programmatically."* Two things have to become true that aren't today:
41
+
42
+ 1. **moshscript must be able to drive moshcode's real capabilities**, not just
43
+ print flavor text.
44
+ 2. **moshscript must accept real JavaScript** so that "very simple" scripts keep
45
+ working while power users can reach for loops, conditionals, variables, and
46
+ expressions without hitting a wall in the hand-rolled tokenizer
47
+ (`interpreter.mjs` throws on any character outside `(){};,` + string/number/
48
+ ident).
49
+
50
+ ## Goals
51
+
52
+ - Evolve `/run <file.mosh>` from a cosmetic loop into a programmable interface to
53
+ the whole moshcode CLI: any verb reachable from the pit or `bin/moshcode.mjs`
54
+ is callable as a moshscript function.
55
+ - Keep the "no bugs, only features" starter script — `while (alive) { code();
56
+ mosh(); notify(); repeat(); }` — running unchanged. The simple surface is the
57
+ brand; it must not regress.
58
+ - Make "secretly all JS is legal" real: a `.mosh` file is executed as JavaScript
59
+ with the moshscript command vocabulary injected as globals, so `if`, `for`,
60
+ `const`, template strings, `await`, and arithmetic all Just Work.
61
+ - Give scripts programmatic access to engines (`agents`, `start`, `install`),
62
+ workflow tools (`ugig`, `coinpay`, `c0mpute`), cross-engine fan-out (`mcp`,
63
+ `skill`), publishing (`prd`), and system verbs (`upgrade`, `shell`, `pwd`).
64
+ - Preserve `--dry-run` as a first-class, honored mode: a dry run narrates every
65
+ action (spawn argv, network target) without executing it.
66
+ - Keep it lean: zero new runtime dependencies, still Node 18+ ESM.
67
+
68
+ ## Non-Goals
69
+
70
+ - Sandboxing untrusted scripts. A `.mosh` file runs with the user's full
71
+ privileges by design (it can spawn engines and shell out); moshscript is an
72
+ automation tool for its author, not a safe host for third-party code.
73
+ - A bespoke parser/type-checker for the "simple" dialect. We reach JS-legality by
74
+ executing as JS, not by growing the custom tokenizer into a language.
75
+ - Reimplementing engine/tool behavior. moshscript verbs call the *same* exported
76
+ functions the TUI and CLI call; it adds no new capability, only a new caller.
77
+ - A package/module system, imports between `.mosh` files, or a stdlib beyond the
78
+ moshcode command vocabulary. (Deferred; see Open Questions.)
79
+ - Long-lived daemon/scheduler semantics. `/loop`-style scheduling stays a harness
80
+ concern; moshscript runs to completion.
81
+
82
+ ## Users
83
+
84
+ - **The starter user** who copies the `while (alive) { … }` snippet from the
85
+ README, runs `/run alive.mosh`, and gets the metal feedback loop. They never
86
+ learn it's JavaScript.
87
+ - **The automator** who scripts a real chore: "install claude + codex, register
88
+ our Sentry MCP server across both, then open an agent session" as a handful of
89
+ `.mosh` lines committed to a repo.
90
+ - **CI / cron** invoking `moshcode run pipeline.mosh --dry-run` in a check, and
91
+ `moshcode run pipeline.mosh` on the runner, with the same file.
92
+ - **Skill/PRD authors** who want `prd("idea")` or a fan-out callable from a
93
+ script rather than retyped interactively.
94
+
95
+ ## Requirements
96
+
97
+ - R1 [P0] A `.mosh` file MUST execute as JavaScript. The existing starter script
98
+ (`while (alive) { code(); mosh(); notify(); repeat(); }`) MUST run unchanged
99
+ and produce equivalent output. The custom `interpreter.mjs` grammar is retired
100
+ as the execution path (kept only if needed as a compatibility shim; see R11).
101
+ - R2 [P0] The moshscript command vocabulary MUST be injected as callable globals
102
+ into the script scope, so `code()`, `mosh()`, `notify(...)`, etc. resolve
103
+ without imports. `alive` MUST be a mutable global initialized to `true`, and
104
+ `stop()` MUST set it `false` so `while (alive)` terminates.
105
+ - R3 [P0] Execution MUST be bounded. A `--max <n>` iteration/step budget MUST be
106
+ honored to prevent a runaway `while (alive)` from hanging, and the two
107
+ entrypoints MUST agree on the default (today CLI `run` defaults `max=3` while
108
+ TUI `runFile` hard-codes `100000` — this inconsistency MUST be resolved to a
109
+ single documented default).
110
+ - R4 [P0] CLI verbs MUST be implemented by shelling out to the moshcode CLI
111
+ itself — `agents("claude")` runs `moshcode agents claude`, `install("codex")`
112
+ runs `moshcode install codex` — NOT by reaching into `src/engines.mjs` /
113
+ internals. There is one implementation of every capability (the CLI) and
114
+ moshscript is a second caller of it. This shell-out MUST be synchronous and
115
+ blocking (spawnSync with inherited stdio) so that (a) the simple no-`await`
116
+ style runs verbs in order — `install("claude"); agents("claude");` — and
117
+ (b) interactive engine sessions own the real terminal and hand control back to
118
+ the script on exit. The engine/agent verbs are: `agents(engine, ...args)`,
119
+ `start(engine, ...args)`, `install(target)`.
120
+ - R5 [P0] The workflow-tool verbs MUST likewise shell out: `ugig(...args)`,
121
+ `coinpay(...args)`, `c0mpute(...args)` → `moshcode ugig …` etc., with
122
+ byte-transparent stdio so JSON pipelines survive.
123
+ - R6 [P0] The fan-out, publishing, and system verbs MUST also shell out:
124
+ `mcp(...args)`, `skill(...args)`, `prd(idea?)`, `upgrade(...targets)`,
125
+ `pwd()`. Local moshscript-only verbs with no CLI equivalent
126
+ (`mosh`, `code`, `say`, `notify`, `sleep`, `stop`, `repeat`) are implemented
127
+ in-process; `mosh()` is the reference example of a local verb.
128
+ - R7 [P0] `--dry-run` MUST be honored by every side-effecting verb: no engine
129
+ spawns, no installs, no network POSTs, no PRD writes. Each MUST instead narrate
130
+ what it *would* do (the resolved argv or request target). `notify()` and
131
+ `mosh()`'s browser-open already gate on `ctx.dryRun`; all new verbs MUST too.
132
+ - R8 [P0] A verb that fails (missing engine, non-zero child exit, network error)
133
+ MUST surface a clear, metal-toned error and MUST NOT crash the interpreter mid
134
+ script unless the script chose to let it throw; a documented convention (e.g.
135
+ verbs resolve to a `{ ok, ... }`-style result and only throw on truly fatal
136
+ misuse) MUST be specified so scripts can branch on outcomes.
137
+ - R9 [P1] The command vocabulary MUST remain open for extension: a script (or a
138
+ host embedding moshscript) MUST be able to register additional verbs, matching
139
+ today's documented `ctx.commands[name] = fn` seam.
140
+ - R10 [P1] `moshcode commands` MUST list the full, current vocabulary (engine,
141
+ tool, fan-out, system, and flavor verbs) with one-line descriptions, and the
142
+ README + `/help` MUST reflect that `.mosh` is JS-legal.
143
+ - R11 [P1] Backward compatibility: any `.mosh` that ran under the old
144
+ tokenizer MUST still run (the old grammar is a strict subset of JS, so
145
+ executing as JS should suffice; if any construct differs, a shim MUST cover
146
+ it). Existing tests (`interpreter.test.mjs`, `commands.test.mjs`,
147
+ `run-options.test.mjs`) MUST pass or be migrated with equivalent coverage.
148
+ - R12 [P1] The verb→argv mapping for each engine/tool MUST be a pure, unit-tested
149
+ function (mirroring the MCP plan tests from
150
+ [[0003-cross-engine-mcp-and-skill-installation]]), so `agents("claude")` and
151
+ friends are verified without spawning real engines.
152
+ - R13 [P2] Ergonomics: a `.mosh` file MAY use `await` at top level (the runner
153
+ executes in an async context) and MAY read `argv`/env for parameterization, so
154
+ the same script can be reused across targets.
155
+ - R14 [P1] moshscript files MUST be directly executable like shell scripts. A
156
+ leading shebang line (`#!…`) MUST be ignored by the runner (stripped before
157
+ execution, exactly as `sh`/`node` ignore their own shebang), so a file starting
158
+ with `#!/usr/bin/env moshscript` still parses. moshcode MUST provide a
159
+ `moshscript` executable on PATH — a thin alias for `moshcode run` — so that
160
+ `#!/usr/bin/env moshscript` resolves, a `chmod +x script.mosh` file runs as
161
+ `./script.mosh`, and any positional args after the file reach the script (see
162
+ R13's `argv`). The `moshscript` bin and its install/PATH wiring MUST match how
163
+ the `moshcode` bin is already published (`bin` map in `package.json` +
164
+ `install.sh`).
165
+ - R15 [P1] Human-in-the-loop, two verbs:
166
+ - `notify(msg)` — fire-and-forget. Pings the operator across their configured
167
+ channels (email / SMS / Telegram / Slack / any webhook, fanned out by
168
+ moshcoding.com) and surfaces an approval link `app.moshcode.sh/approve/:id`.
169
+ Returns `{ id, url }` so the script can hand the link off. Non-blocking.
170
+ - `ask(prompt)` — the blocking gate. Delivers the same ping + link, then
171
+ BLOCKS until the operator opens `app.moshcode.sh/approve/:id`, reads the context,
172
+ types instructions, and hits submit; resolves with their text (or null on
173
+ timeout). This is what lets an unattended `while (alive)` loop pause for a
174
+ human and resume from the reply. `ask()` awaits a network long-poll, so it
175
+ is used with `await`.
176
+ Client contract (in this repo): create id → deliver `{ message, approval:{id,url} }`
177
+ → long-poll `GET {API}/api/approvals/:id` until `{ status:"submitted", response }`.
178
+ The `app.moshcode.sh/approve/:id` page, the approvals inbox it submits to,
179
+ channel fan-out, auth, and billing are a SEPARATE approvals web app
180
+ (app.moshcode.sh) — OUT OF SCOPE for this PRD. The client degrades gracefully
181
+ (prints the link; the poll simply never resolves) until that server ships.
182
+ - R16 [P1] `run(file, ...args)` MUST compose scripts: a `.mosh` file can include
183
+ another by calling `run("other.mosh")`, which is exactly `moshcode run other.mosh`.
184
+ Because CLI verbs block (spawnSync), includes execute in order and finish before
185
+ the caller continues. This gives moshscript an include/compose system for free —
186
+ developers factor setup/teardown into reusable `.mosh` files. (Recursion/cycle
187
+ protection is a developer concern for v1; `--max` does not bound include depth.)
188
+ - R17 [P1] **Shortcuts** — higher-level sugar verbs composed from the CLI. Because
189
+ moshscript already batch-scripts moshcode, calling an engine is *technically*
190
+ possible today via `agents("claude")`; shortcuts make the common moves ergonomic
191
+ and, crucially, *return values scripts can use*. The flagship shortcut is
192
+ **`ai(prompt, opts?)`** — run a coding engine on a prompt and RETURN its output
193
+ as a string, so a script can branch on what the AI said:
194
+ ```js
195
+ const summary = ai("summarize the git diff in one line");
196
+ notify(summary);
197
+ ```
198
+ - `ai(prompt)` MUST run the default/available engine (claude, else codex /
199
+ opencode / gemini / aider) headlessly and return stdout as a string.
200
+ `ai(prompt, { engine: "codex" })` MUST target a specific engine.
201
+ - This requires a NON-interactive capture mode distinct from `agents()`/`start()`
202
+ (which hand over the TTY with inherited stdio and return no value): `ai()`
203
+ spawns the engine in its headless/print mode with stdout **captured** (piped,
204
+ not inherited) and returns it. `agents()`/`start()` are unchanged.
205
+ - `ai()` MUST honor `--dry-run` (narrate the engine + prompt, return "" ) and be
206
+ bounded like other verbs. Engine headless invocation (e.g. `claude -p`,
207
+ `codex exec`) MUST be defined per engine in a pure, tested mapping (cf. R12).
208
+ - Shortcuts are a namespace to grow (e.g. `ask()` is itself a human shortcut);
209
+ `ai()` is the first. Each shortcut MUST be documented in `moshcode commands`.
210
+ - **`ai()` composes with `ask()` as the escalation gate.** The intended pattern
211
+ is an autonomous loop that uses `ai()` to do the work AND to decide when a
212
+ human is actually needed, then calls `ask()` only at that point — so the
213
+ operator is pinged (slack/email/sms → a link with the full context + the
214
+ question) exactly when their judgment matters, not on every iteration:
215
+ ```js
216
+ while (alive) {
217
+ const plan = ai("given the failing test, propose a fix. If you're unsure or it's risky, reply exactly NEEDS_HUMAN.");
218
+ const go = plan.includes("NEEDS_HUMAN") ? await ask(`unsure — ${plan}`) : plan;
219
+ if (go) ai(`apply this: ${go}`);
220
+ repeat();
221
+ }
222
+ ```
223
+ This is the through-line for the approvals web app (app.moshcode.sh): its whole
224
+ job is to render that context + question at the link and route the human's
225
+ reply back into the waiting `ask()`.
226
+
227
+ ## UX Notes
228
+
229
+ The simple surface — unchanged, still the README hero:
230
+
231
+ ```js
232
+ // moshscript toolkit
233
+ while (alive) {
234
+ code();
235
+ mosh();
236
+ notify();
237
+ repeat();
238
+ } // no bugs, only features
239
+ ```
240
+
241
+ The secret that it's all JS — now legal, no new syntax to learn:
242
+
243
+ ```js
244
+ // deploy-agents.mosh — real work, still reads like the toy
245
+ const engines = ["claude", "codex"];
246
+ for (const e of engines) {
247
+ install(e); // idempotent: installs if missing
248
+ }
249
+ mcp("install", "https://mcp.sentry.dev/mcp"); // fan out across engines
250
+ say(`ready to mosh with ${engines.length} engines 🤘`);
251
+ agents("claude"); // drop into an autonomous session
252
+ ```
253
+
254
+ Invocation — unchanged, plus a shebang path so `.mosh` files run themselves:
255
+
256
+ ```sh
257
+ moshcode run deploy-agents.mosh # do it
258
+ moshcode run deploy-agents.mosh --dry-run # narrate the argv, touch nothing
259
+ moshcode run alive.mosh --max 3 # bounded metal loop
260
+ ```
261
+
262
+ ```js
263
+ #!/usr/bin/env moshscript
264
+ // deploy-agents.mosh — chmod +x it and run it like any shell script
265
+ install("claude");
266
+ agents("claude");
267
+ ```
268
+
269
+ ```sh
270
+ chmod +x deploy-agents.mosh
271
+ ./deploy-agents.mosh # shebang → `moshscript` → `moshcode run`
272
+ ./deploy-agents.mosh --dry-run staging # args after the file reach the script
273
+ ```
274
+
275
+ The leading `#!…` line is stripped before execution (like `sh`/`node`), so it
276
+ never confuses the parser.
277
+
278
+ Dry-run output narrates instead of acting:
279
+
280
+ ```
281
+ 🎸 moshcode — running moshscript (dry run)
282
+ ⌨ install(claude) → would run: npm i -g @anthropic-ai/claude-code
283
+ 🤘 mcp(install …) → would fan out to claude, codex (2 engines)
284
+ 💬 ready to mosh with 2 engines 🤘
285
+ 🎤 agents(claude) → would launch: claude --dangerously-skip-permissions
286
+ ✓ no bugs, only features. 🤘
287
+ ```
288
+
289
+ Voice stays irreverent-metal throughout (`the pit`, `no bugs only features`,
290
+ 🤘). `mosh()` already blasts the moshcoding Spotify playlist and opens it in a
291
+ desktop browser — scripts inherit that.
292
+
293
+ ## Success Metrics
294
+
295
+ - The unchanged starter `while (alive) { code(); mosh(); notify(); repeat(); }`
296
+ runs under the new engine with equivalent output.
297
+ - A single `.mosh` file can install an engine, register an MCP server across
298
+ engines, and launch an agent session — actions that previously required typing
299
+ three separate pit commands.
300
+ - `--dry-run` performs zero side effects (verified by tests: no spawns, no
301
+ network) while narrating every intended action.
302
+ - Engine/tool argv mappings are covered by pure unit tests with no real spawns.
303
+ - The full moshcode test suite passes; the two entrypoints share one default
304
+ iteration budget.
305
+
306
+ ## Risks & Open Questions
307
+
308
+ Several early questions were settled while building the base architecture
309
+ (`src/runtime.mjs`, `src/registry.mjs`, `src/cli.mjs`); recorded here as
310
+ decisions:
311
+
312
+ - **RESOLVED — execution model.** JS runs via `new AsyncFunction(...)` with the
313
+ vocabulary injected through a `with (proxy)` scope. The proxy owns only the
314
+ vocabulary + `alive`/`argv`/`env`; everything else (locals, `console`, `Math`)
315
+ falls through to normal scoping, so full JS works with zero dependencies and no
316
+ sandbox promised.
317
+ - **RESOLVED — one registry, and verbs shell out to the CLI.** Rather than share
318
+ the two dispatch ladders, CLI verbs simply run `moshcode <cmd> …` (spawnSync).
319
+ The CLI stays the single implementation; the moshscript vocabulary is a
320
+ `createRegistry()` of `{name, run}` commands, so it can't silently diverge from
321
+ the capability it wraps.
322
+ - **RESOLVED — blocking semantics.** CLI verbs and `sleep()` are synchronous and
323
+ blocking, so the simple no-`await` style (`install(…); agents(…);`) runs in
324
+ order; fire-and-forget async verbs (`notify`) are drained before the runner
325
+ returns, so a bare `notify()` still delivers.
326
+ - **RESOLVED — iteration budget.** `--max` counts reads of `alive`, so a
327
+ `while (alive)` loop runs at most `--max` times (shared default **3** across
328
+ both entrypoints); straight-line scripts and plain `for` loops are unbounded.
329
+ - **Error convention** (R8): CLI verbs currently throw on a non-zero exit (fail
330
+ loud). Whether a non-zero passthrough should instead return a result the script
331
+ can branch on is still open; a throwing model can make `while (alive)` loops
332
+ brittle.
333
+ - **Interactive verbs in non-interactive runs** (R15): with spawnSync+inherited
334
+ stdio, `agents()`/`start()` are fully interactive in a real terminal, but in
335
+ CI/cron (no TTY) they can hang or error. The intended answer is R15's
336
+ `notify()` bridge — a headless script pings the operator and waits on a
337
+ moshcode.sh reply instead of blocking on a dead TTY. The reply-inbox endpoint
338
+ that feeds a response back into a waiting script is not yet built.
339
+ - **`--dry-run` for the shell-out.** Under dry-run, CLI verbs narrate
340
+ `would run: moshcode …` and never spawn; the parent CLI they'd invoke doesn't
341
+ re-parse `--dry-run`, so nested dry-run is a runtime concern, handled in
342
+ `src/cli.mjs`, not passed through.
343
+ - Should `.mosh` files support `import`/module reuse later? Deferred; the
344
+ AsyncFunction model doesn't foreclose it.