Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ moshcode run examples/alive.mosh # run a script
moshcode run deploy.mosh --dry-run # narrate without executing
moshcode run alive.mosh --max 5 # bound the while loop (default 3)
moshcode run deploy.mosh staging --fast # extra args reach the script as argv
moshcode run deploy.mosh -- --max 5 # -- preserves option-like script args
moshcode run - < script.mosh # pipe/paste from stdin
moshcode commands # list the full vocabulary
moshcode commands --json # machine-readable command metadata
Expand Down
12 changes: 8 additions & 4 deletions bin/moshcode.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -404,19 +404,23 @@ async function main() {
if (cmd === "logout") { logout(); return; }
if (cmd === "run") {
let max = 3, dryRun = false;
let optionsEnded = false;
const positional = []; // first is the file; the rest reach the script as argv
for (let k = 0; k < rest.length; k++) {
const a = rest[k];
if (a === "--max" || a === "-n") {
if (!optionsEnded && a === "--") {
optionsEnded = true;
}
else if (!optionsEnded && (a === "--max" || a === "-n")) {
try { max = parseMax(rest[++k]); }
catch (e) { console.error(String(e.message || e)); process.exit(1); }
}
else if (a.startsWith("--max=")) {
else if (!optionsEnded && a.startsWith("--max=")) {
try { max = parseMax(a.slice("--max=".length)); }
catch (e) { console.error(String(e.message || e)); process.exit(1); }
}
else if (a === "--dry-run") dryRun = true;
else if (a !== "-" && a.startsWith("-") && positional.length === 0) {
else if (!optionsEnded && a === "--dry-run") dryRun = true;
else if (!optionsEnded && a !== "-" && a.startsWith("-") && positional.length === 0) {
console.error(`moshcode run: unknown option ${a}`);
process.exit(1);
}
Expand Down
11 changes: 7 additions & 4 deletions src/tui.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -325,20 +325,23 @@ function printPrds() {
async function runFile(args) {
// Parse /run options the same way the CLI does (R3: two entrypoints agree).
let max, dryRun = false, file = null;
let optionsEnded = false;
const argv = [];
for (let i = 0; i < args.length; i++) {
const a = args[i];
if (a === "--max" || a === "-n") {
if (!optionsEnded && a === "--") {
optionsEnded = true;
} else if (!optionsEnded && (a === "--max" || a === "-n")) {
const v = Number(args[++i]);
if (!Number.isSafeInteger(v) || v < 1) { console.log(err(`--max needs a positive integer`)); return; }
max = v;
} else if (a.startsWith("--max=")) {
} else if (!optionsEnded && a.startsWith("--max=")) {
const v = Number(a.slice("--max=".length));
if (!Number.isSafeInteger(v) || v < 1) { console.log(err(`--max needs a positive integer`)); return; }
max = v;
} else if (a === "--dry-run") {
} else if (!optionsEnded && a === "--dry-run") {
dryRun = true;
} else if (a.startsWith("-") && !file) {
} else if (!optionsEnded && a.startsWith("-") && !file) {
console.log(err(`unknown option ${a}`));
return;
} else if (!file) {
Expand Down
12 changes: 12 additions & 0 deletions test/run-options.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,18 @@ test("positional args after the script file reach the script as argv", async ()
assert.match(result.stdout, /--fast/);
});

test("run preserves option-like script args after --", async () => {
const dir = mkdtempSync(join(tmpdir(), "moshcode-run-separator-"));
const script = join(dir, "argv.mosh");
writeFileSync(script, "say(JSON.stringify(argv));\n");

const result = await run([script, "--", "--max", "2", "--dry-run", "-n"]);

assert.equal(result.status, 0);
assert.doesNotMatch(result.stdout, /running moshscript \(dry run\)/);
assert.match(result.stdout, /\["--max","2","--dry-run","-n"\]/);
});

test("run reports missing script files without a stack trace", async () => {
const missing = join(tmpdir(), "moshcode-missing-script.mosh");
const result = await run([missing, "--dry-run"]);
Expand Down
21 changes: 21 additions & 0 deletions test/tui.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,27 @@ test("TUI /run rejects unsafe iteration limits", async () => {
assert.doesNotMatch(result.stdout, /usage: \/run/);
});

test("TUI /run preserves option-like script args after --", () => {
const dir = mkdtempSync(join(tmpdir(), "moshcode-tui-separator-"));
const script = join(dir, "argv.mosh");
writeFileSync(script, "say(JSON.stringify(argv));\n");
const scriptArg = script.replaceAll("\\", "/");

const result = spawnSync(
process.execPath,
["bin/moshcode.mjs"],
{
cwd: join(import.meta.dirname, ".."),
input: `/run "${scriptArg}" -- --max 2 --dry-run -n\n/quit\n`,
encoding: "utf8",
},
);

assert.equal(result.status, 0, result.stderr || result.stdout);
assert.doesNotMatch(result.stdout, /dry run .* narrating/);
assert.match(result.stdout, /\["--max","2","--dry-run","-n"\]/);
});

test("TUI /run passes positional args through to moshscript argv", () => {
const dir = mkdtempSync(join(tmpdir(), "moshcode-tui-"));
mkdirSync(join(dir, "space dir"));
Expand Down
Loading