omegon 0.6.15 → 0.6.17

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.
@@ -105,7 +105,11 @@ export const DEPS: Dep[] = [
105
105
  tier: "core",
106
106
  check: () => hasCmd("nix"),
107
107
  install: [
108
- { platform: "any", cmd: "curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install --no-confirm" },
108
+ // --no-confirm: headless (stdin is closed)
109
+ // --init none: skip systemd/launchd service setup — required on immutable
110
+ // distros (Bazzite/Silverblue) where systemd-reload fails, and harmless
111
+ // elsewhere (nix daemon is started on demand).
112
+ { platform: "any", cmd: "curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install --no-confirm --init none" },
109
113
  ],
110
114
  url: "https://zero-to-nix.com",
111
115
  },
@@ -432,28 +432,66 @@ export default function (pi: ExtensionAPI) {
432
432
  /**
433
433
  * Replace the current Omegon process with a fresh instance.
434
434
  *
435
- * Spawns a new detached Omegon process with inherited stdio, then exits
436
- * the current process. The user sees the terminal briefly reset and the
437
- * new session starts automatically no manual re-launch needed.
435
+ * Resets terminal state (exits raw mode, alternate screen, mouse capture),
436
+ * then uses shell `exec` to replace the process in-place. This avoids the
437
+ * race between old-process TUI teardown and new-process TUI startup that
438
+ * causes ANSI garbage when using detach+exit.
438
439
  */
439
440
  function restartOmegon(): never {
440
441
  const { command, argvPrefix } = resolveOmegonSubprocess();
441
- // Pass through any user-facing args from the original invocation
442
- // (skip argv[0]=node, argv[1]=omegon.mjs which argvPrefix covers)
443
442
  const userArgs = process.argv.slice(2).filter(a =>
444
- // Strip injected resource flags — the new process injects its own
445
443
  !a.startsWith("--extensions-dir=") &&
446
444
  !a.startsWith("--themes-dir=") &&
447
445
  !a.startsWith("--skills-dir=") &&
448
- !a.startsWith("--prompts-dir=")
446
+ !a.startsWith("--prompts-dir=") &&
447
+ !a.startsWith("--extension=") && !a.startsWith("--extension ") &&
448
+ !a.startsWith("--skill=") && !a.startsWith("--skill ") &&
449
+ !a.startsWith("--prompt-template=") && !a.startsWith("--prompt-template ") &&
450
+ !a.startsWith("--theme=") && !a.startsWith("--theme ") &&
451
+ !a.startsWith("--no-skills") &&
452
+ !a.startsWith("--no-prompt-templates") &&
453
+ !a.startsWith("--no-themes") &&
454
+ !a.startsWith("--no-extensions")
449
455
  );
450
- const child = spawn(command, [...argvPrefix, ...userArgs], {
456
+
457
+ // Reset terminal to sane state before exec replaces us
458
+ const reset = [
459
+ "\x1b[?1049l", // exit alternate screen buffer
460
+ "\x1b[?1000l", // disable mouse click tracking
461
+ "\x1b[?1002l", // disable mouse drag tracking
462
+ "\x1b[?1006l", // disable SGR mouse mode
463
+ "\x1b[?25h", // show cursor
464
+ "\x1b[0m", // reset attributes
465
+ "\x1bc", // full terminal reset (RIS)
466
+ ].join("");
467
+ process.stdout.write(reset);
468
+
469
+ // Exit raw mode if active
470
+ if (process.stdin.isTTY && process.stdin.isRaw) {
471
+ process.stdin.setRawMode(false);
472
+ }
473
+
474
+ // Build the exec command — shell `exec` replaces the process entirely,
475
+ // no orphan child, no race between old and new TUI.
476
+ const parts = [command, ...argvPrefix, ...userArgs].map(shellEscape);
477
+ const child = spawn("sh", ["-c", `exec ${parts.join(" ")}`], {
451
478
  stdio: "inherit",
452
- detached: true,
453
479
  env: process.env,
454
480
  });
481
+ // If exec fails for some reason, exit when the child does
482
+ child.on("close", (code) => process.exit(code ?? 1));
483
+ // Prevent Node from keeping the event loop alive for other reasons
455
484
  child.unref();
456
- process.exit(0);
485
+
486
+ // The sh -c exec should have replaced us. If we're still here,
487
+ // just wait for the child.
488
+ return undefined as never;
489
+ }
490
+
491
+ /** Escape a string for POSIX shell */
492
+ function shellEscape(s: string): string {
493
+ if (/^[a-zA-Z0-9_./:=-]+$/.test(s)) return s;
494
+ return `'${s.replace(/'/g, "'\\''")}'`;
457
495
  }
458
496
 
459
497
  /** Run a command, collect stdout+stderr, resolve with exit code. */
@@ -1065,12 +1103,17 @@ async function installDeps(ctx: CommandContext, deps: DepStatus[]): Promise<void
1065
1103
  continue;
1066
1104
  }
1067
1105
 
1068
- ctx.ui.notify(`\n${step} 📦 Installing ${dep.name}…`);
1069
- ctx.ui.notify(` → \`${cmd}\``);
1106
+ ctx.ui.notify(`\n${step} 📦 Installing ${dep.name}…\n → \`${cmd}\``);
1070
1107
 
1108
+ // Collect output lines — show progress inline but also keep them
1109
+ // so we can dump a readable block on failure.
1110
+ const outputLines: string[] = [];
1071
1111
  const exitCode = await runAsync(
1072
1112
  cmd,
1073
- (line) => ctx.ui.notify(line),
1113
+ (line) => {
1114
+ outputLines.push(line);
1115
+ ctx.ui.notify(line);
1116
+ },
1074
1117
  );
1075
1118
 
1076
1119
  // Patch PATH immediately after installing bootstrapping deps so the rest
@@ -1086,13 +1129,22 @@ async function installDeps(ctx: CommandContext, deps: DepStatus[]): Promise<void
1086
1129
  ctx.ui.notify(`${step} ✅ ${dep.name} installed successfully`);
1087
1130
  } else if (exitCode === 124) {
1088
1131
  ctx.ui.notify(`${step} ❌ ${dep.name} install timed out (10 min limit)`);
1089
- } else if (exitCode === 0) {
1090
- ctx.ui.notify(`${step} ⚠️ Command succeeded but ${dep.name} not found on PATH — you may need to open a new shell.`);
1091
1132
  } else {
1092
- ctx.ui.notify(`${step} Failed to install ${dep.name} (exit ${exitCode})`);
1093
- const hints = dep.install.filter((o) => o.cmd !== cmd);
1094
- if (hints.length > 0) ctx.ui.notify(` Alternative: \`${hints[0]!.cmd}\``);
1095
- if (dep.url) ctx.ui.notify(` Manual install: ${dep.url}`);
1133
+ // Dump the last N lines of output so the operator can see what went wrong
1134
+ const tail = outputLines.slice(-20).join("\n");
1135
+ const status = exitCode === 0
1136
+ ? `Command succeeded but ${dep.name} not found on PATH`
1137
+ : `Failed to install ${dep.name} (exit ${exitCode})`;
1138
+ const block = [
1139
+ `${step} ❌ ${status}`,
1140
+ "",
1141
+ "Output (last 20 lines):",
1142
+ "```",
1143
+ tail,
1144
+ "```",
1145
+ ...(dep.url ? [`Manual install: ${dep.url}`] : []),
1146
+ ].join("\n");
1147
+ ctx.ui.notify(block);
1096
1148
  }
1097
1149
  }
1098
1150
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omegon",
3
- "version": "0.6.15",
3
+ "version": "0.6.17",
4
4
  "description": "Omegon — an opinionated distribution of pi (by Mario Zechner) with extensions for lifecycle management, memory, orchestration, and visualization",
5
5
  "bin": {
6
6
  "omegon": "bin/omegon.mjs",