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.
- package/LICENSE +21 -0
- package/README.md +580 -0
- package/bin/moshcode.mjs +674 -0
- package/bin/moshscript.mjs +29 -0
- package/examples/alive.mosh +6 -0
- package/examples/scripting-the-cli.mosh +21 -0
- package/examples/team-secrets.mosh +20 -0
- package/examples/templates/bun-caddy-sqlite/.env.example +14 -0
- package/examples/templates/bun-caddy-sqlite/Caddyfile +18 -0
- package/examples/templates/bun-caddy-sqlite/README.md +97 -0
- package/examples/templates/bun-caddy-sqlite/deploy/moshcode-dns.service +39 -0
- package/examples/templates/bun-caddy-sqlite/deploy/moshpit-service.service +38 -0
- package/examples/templates/bun-caddy-sqlite/package.json +15 -0
- package/examples/templates/bun-caddy-sqlite/src/db.ts +47 -0
- package/examples/templates/bun-caddy-sqlite/src/server.ts +44 -0
- package/examples/templates/bun-caddy-sqlite/template.json +10 -0
- package/examples/templates/caddy-proxy/Caddyfile +36 -0
- package/examples/templates/caddy-proxy/README.md +104 -0
- package/examples/templates/caddy-proxy/deploy/moshcode-dns.service +39 -0
- package/examples/templates/caddy-proxy/template.json +8 -0
- package/examples/templates/caddy-static/Caddyfile +16 -0
- package/examples/templates/caddy-static/README.md +90 -0
- package/examples/templates/caddy-static/deploy/moshcode-dns.service +39 -0
- package/examples/templates/caddy-static/site/index.html +11 -0
- package/examples/templates/caddy-static/template.json +8 -0
- package/install.sh +194 -0
- package/package.json +28 -0
- package/prd/0000-template.md +49 -0
- package/prd/0001-wrap-ugig-and-coinpay-clis.md +121 -0
- package/prd/0002-separate-agent-and-raw-engine-launches.md +113 -0
- package/prd/0003-cross-engine-mcp-and-skill-installation.md +165 -0
- package/prd/0004-moshscript-run-programmable-moshcode.md +344 -0
- package/prd/0005-hosted-moshpit-resolver.md +192 -0
- package/prd/0006-help.md +359 -0
- package/prd/0007-profullstack-site-init.md +1183 -0
- package/prd/README.md +26 -0
- package/src/ads.mjs +58 -0
- package/src/auth.mjs +193 -0
- package/src/cli-schema.mjs +533 -0
- package/src/cli.mjs +118 -0
- package/src/commands.mjs +259 -0
- package/src/completion.mjs +594 -0
- package/src/console.mjs +244 -0
- package/src/dns-system.mjs +404 -0
- package/src/dns.mjs +2872 -0
- package/src/doh-server.mjs +256 -0
- package/src/doh.mjs +218 -0
- package/src/engines.mjs +385 -0
- package/src/escalate.mjs +85 -0
- package/src/help.mjs +443 -0
- package/src/integrations.mjs +265 -0
- package/src/mcp-catalog.mjs +50 -0
- package/src/mcp.mjs +155 -0
- package/src/mirror.mjs +187 -0
- package/src/notify.mjs +86 -0
- package/src/open-url.mjs +34 -0
- package/src/parking-http.mjs +65 -0
- package/src/pins.mjs +190 -0
- package/src/pit-url.mjs +13 -0
- package/src/prd.mjs +341 -0
- package/src/pty.mjs +176 -0
- package/src/pwd.mjs +103 -0
- package/src/registry.mjs +37 -0
- package/src/release-install.mjs +191 -0
- package/src/runtime.mjs +161 -0
- package/src/selfupdate.mjs +215 -0
- package/src/serve.mjs +502 -0
- package/src/skills.mjs +93 -0
- package/src/tabs.mjs +144 -0
- package/src/templates.mjs +456 -0
- package/src/tools.mjs +231 -0
- package/src/trade.mjs +137 -0
- package/src/trust.mjs +712 -0
- package/src/tui.mjs +736 -0
- package/src/ui.mjs +49 -0
- package/src/uninstall.mjs +113 -0
- 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.
|