moshcode 0.57.0 → 0.58.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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/tui.mjs +37 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moshcode",
3
- "version": "0.57.0",
3
+ "version": "0.58.0",
4
4
  "type": "module",
5
5
  "description": "moshcode — a metal wrapper for coding engines and native UGig/CoinPay workflow CLIs, with OpenPRD and moshscript",
6
6
  "repository": {
package/src/tui.mjs CHANGED
@@ -464,6 +464,35 @@ let activeMirror = null;
464
464
  */
465
465
  const childSink = () => (activeMirror ? (chunk) => activeMirror?.write(chunk) : undefined);
466
466
 
467
+ /**
468
+ * How fast an exit has to be before it is worth remarking on.
469
+ *
470
+ * A person who opens an agent and immediately quits takes longer than this.
471
+ * Nothing that actually started a session lands under it.
472
+ */
473
+ const INSTANT_EXIT_MS = 1500;
474
+
475
+ /**
476
+ * The note for a hand-off that ended the moment it began, or null.
477
+ *
478
+ * A CLI that exits 0 without doing anything is indistinguishable, from out
479
+ * here, from one the operator opened and closed — both are "exited (code 0)",
480
+ * which reads as success and sent somebody looking for the bug in the wrong
481
+ * program. It happens for real: @serjm/deepseek-code 0.5.0 compares
482
+ * `resolve(process.argv[1])` against `import.meta.url` to decide whether it is
483
+ * the entrypoint, and npm installs every global bin as a symlink, so the
484
+ * comparison fails and the whole CLI silently runs nothing.
485
+ *
486
+ * Timing is all we have — the child owns the terminal, so its output is not
487
+ * ours to inspect — and it is enough to say "this looks wrong" without
488
+ * claiming to know why.
489
+ */
490
+ export function instantExitNote({ key, bin, code, ms }) {
491
+ if (code !== 0 || ms >= INSTANT_EXIT_MS) return null;
492
+ return `${key} exited instantly without running — that usually means a broken install, not a clean session.`
493
+ + ` check it directly with \`${bin} --version\`, and reinstall with /install ${key} if that prints nothing.`;
494
+ }
495
+
467
496
  async function openEngine(key, engine, args, { agentMode = false } = {}) {
468
497
  if (!engine.installed && !args.length) {
469
498
  console.log(info(`${key} isn't installed — try ${acid("/install " + key)} first.`));
@@ -479,7 +508,9 @@ async function openEngine(key, engine, args, { agentMode = false } = {}) {
479
508
  console.log(info(`opening ${bone(key)}${agentMode ? " autonomously" : " raw"} — hand-off to its CLI, exit it to come back…`));
480
509
  console.log(hr());
481
510
  activeMirror?.setEngine(key);
511
+ const startedAt = Date.now();
482
512
  const r = await openSession(engine, agentMode ? agentLaunchArgs(engine, args) : args, { onOutput: childSink() });
513
+ const elapsed = Date.now() - startedAt;
483
514
  activeMirror?.setEngine(null);
484
515
  console.log(hr());
485
516
  if (!r.ok) {
@@ -488,6 +519,8 @@ async function openEngine(key, engine, args, { agentMode = false } = {}) {
488
519
  : err(`couldn't launch ${key}: ${r.error?.message || r.error}`));
489
520
  } else {
490
521
  console.log(info(`${key} exited${r.code != null ? ` (code ${r.code})` : ""}. back in the pit.`));
522
+ const note = instantExitNote({ key, bin: engine.bin, code: r.code, ms: elapsed });
523
+ if (note) console.log(warn(note));
491
524
  }
492
525
  }
493
526
 
@@ -497,7 +530,9 @@ async function openWorkflowTool(key, tool, args) {
497
530
  }
498
531
  console.log(info(`opening ${bone(key)} — native CLI owns the terminal until it exits…`));
499
532
  console.log(hr());
533
+ const toolStartedAt = Date.now();
500
534
  const result = await openTool(tool, args, { onOutput: childSink() });
535
+ const toolElapsed = Date.now() - toolStartedAt;
501
536
  console.log(hr());
502
537
  if (!result.ok) {
503
538
  console.log(result.error?.code === "ENOENT"
@@ -505,6 +540,8 @@ async function openWorkflowTool(key, tool, args) {
505
540
  : err(`couldn't launch ${key}: ${result.error?.message || result.error}`));
506
541
  } else {
507
542
  console.log(info(`${key} exited${result.code != null ? ` (code ${result.code})` : result.signal ? ` (${result.signal})` : ""}. back in the pit.`));
543
+ const note = instantExitNote({ key, bin: tool.bin, code: result.code, ms: toolElapsed });
544
+ if (note) console.log(warn(note));
508
545
  }
509
546
  }
510
547