Skip to content

Commit c912386

Browse files
fix(engines): stop a nested moshcode child opening a second TUI
A CLI verb called from a script (run(), install(), agents(), upgrade()) spawns `moshcode <verb>` with stdio inherited, so the child sees a TTY whenever a human runs the script from a terminal. At the end of the verb the child called backToPit(), printed the banner and blocked on its own `mosh >` prompt, so the parent script never resumed. Mark spawned children with MOSHCODE_NESTED=1 and treat that like a non-TTY in backToPit, so a nested run exits and hands control back. Same fix for the moshscript shim, which made an executable .mosh file land in a TUI instead of returning to the user's shell.
1 parent dcf0195 commit c912386

4 files changed

Lines changed: 27 additions & 3 deletions

File tree

bin/moshcode.mjs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,10 @@ function parseMax(value) {
5656
// interactive. Piped / non-TTY invocations (scripts, CI, `… | moshcode run -`)
5757
// keep the old behaviour: exit with the child's code.
5858
function backToPit(label, code, signal) {
59-
if (!process.stdin.isTTY) process.exit(code ?? 0);
59+
// A nested invocation (moshscript shim, or a CLI verb called from a script)
60+
// must hand control back to its parent, not open a second mosh pit on the
61+
// shared TTY.
62+
if (!process.stdin.isTTY || process.env.MOSHCODE_NESTED === "1") process.exit(code ?? 0);
6063
const how = signal ? ` (${signal})` : code != null ? ` (code ${code})` : "";
6164
console.log(`\n↩ ${label} exited${how} — back in the mosh pit. /quit to leave.\n`);
6265
return tui();

bin/moshscript.mjs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ import { fileURLToPath } from "node:url";
1414
const BIN = fileURLToPath(new URL("moshcode.mjs", import.meta.url));
1515
const args = process.argv.slice(2); // everything after `moshscript`
1616

17-
const child = spawn(process.execPath, [BIN, "run", ...args], { stdio: "inherit" });
17+
const child = spawn(process.execPath, [BIN, "run", ...args], {
18+
stdio: "inherit",
19+
env: { ...process.env, MOSHCODE_NESTED: "1" },
20+
});
1821
child.on("error", (e) => { console.error(`moshscript: ${e.message}`); process.exit(1); });
1922
child.on("exit", (code, signal) => {
2023
if (signal) {

src/cli.mjs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ export function runMoshcode(cmd, args, ctx) {
4141
}
4242

4343
ctx.out(` ▶ ${printable}`);
44-
const res = spawnSync(process.execPath, [MOSHCODE_BIN, ...argv], { stdio: "inherit" });
44+
const res = spawnSync(process.execPath, [MOSHCODE_BIN, ...argv], {
45+
stdio: "inherit",
46+
env: { ...process.env, MOSHCODE_NESTED: "1" },
47+
});
4548
if (res.error) throw res.error; // truly fatal: spawn itself failed (ENOENT etc.)
4649

4750
const code = res.status ?? 1;

test/run-options.test.mjs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,21 @@ test("run accepts equals-form max option", async () => {
4949
assert.match(result.stdout, /1 loop\(s\)/);
5050
});
5151

52+
test("run() marks the nested moshcode child as nested", async () => {
53+
const dir = mkdtempSync(join(tmpdir(), "moshcode-nested-"));
54+
const child = join(dir, "child.mosh");
55+
const parent = join(dir, "parent.mosh");
56+
writeFileSync(child, 'shell("echo NESTEDVAL=$MOSHCODE_NESTED");\n');
57+
writeFileSync(parent, `run(${JSON.stringify(child)});\n`);
58+
59+
const result = await run([parent]);
60+
61+
assert.equal(result.status, 0);
62+
// Without MOSHCODE_NESTED the child drops into its own TUI on a shared TTY
63+
// instead of returning control to the parent script.
64+
assert.match(result.stdout, /NESTEDVAL=1/);
65+
});
66+
5267
test("run() includes another .mosh file, in order", async () => {
5368
const dir = mkdtempSync(join(tmpdir(), "moshcode-include-"));
5469
const child = join(dir, "child.mosh");

0 commit comments

Comments
 (0)