numux 2.14.1 → 2.16.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/README.md CHANGED
@@ -245,7 +245,9 @@ Auto-exits when all processes finish. Exit code 1 if any process failed.
245
245
 
246
246
  ## Logging
247
247
 
248
- numux writes per-process log files (ANSI-stripped) when `--log-dir` is set or `logDir` is configured. Each session creates a timestamped subdirectory with a `latest` symlink pointing to the most recent run.
248
+ numux writes per-process log files (ANSI-stripped) for every run. By default they go to `<tmpdir>/numux/<project-name>/`, or to `--log-dir` / the `logDir` config when set. Each session creates a timestamped subdirectory with a `latest` symlink pointing to the most recent run.
249
+
250
+ The TUI prints `Logs saved to: <session-dir>` on exit (Ctrl+C or signal). Multiple numux instances in the same project share the default base dir, so each session has its own timestamped folder but the `latest` symlink will point at whichever started most recently. If `numux logs` falls back to the default and a `latest` symlink is in use, it prints a warning. To avoid the collision, set `logDir` per config or pass `--log-dir`.
249
251
 
250
252
  <!-- generated:logging-usage -->
251
253
  ```sh
package/dist/man/numux.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH "NUMUX" "1" "April 2026" "2.14.1" "numux manual"
1
+ .TH "NUMUX" "1" "May 2026" "2.16.0" "numux manual"
2
2
  .SH "NAME"
3
3
  \fBnumux\fR
4
4
  .P
@@ -380,7 +380,9 @@ numux \-\-prefix
380
380
  Auto\-exits when all processes finish\. Exit code 1 if any process failed\.
381
381
  .SH Logging
382
382
  .P
383
- numux writes per\-process log files (ANSI\-stripped) when \fB\-\-log\-dir\fP is set or \fBlogDir\fP is configured\. Each session creates a timestamped subdirectory with a \fBlatest\fP symlink pointing to the most recent run\.
383
+ numux writes per\-process log files (ANSI\-stripped) for every run\. By default they go to \fB<tmpdir>/numux/<project\-name>/\fP, or to \fB\-\-log\-dir\fP / the \fBlogDir\fP config when set\. Each session creates a timestamped subdirectory with a \fBlatest\fP symlink pointing to the most recent run\.
384
+ .P
385
+ The TUI prints \fBLogs saved to: <session\-dir>\fP on exit (Ctrl+C or signal)\. Multiple numux instances in the same project share the default base dir, so each session has its own timestamped folder but the \fBlatest\fP symlink will point at whichever started most recently\. If \fBnumux logs\fP falls back to the default and a \fBlatest\fP symlink is in use, it prints a warning\. To avoid the collision, set \fBlogDir\fP per config or pass \fB\-\-log\-dir\fP\|\.
384
386
  <!\-\- generated:logging\-usage \-\->
385
387
  .RS 2
386
388
  .nf
package/dist/numux.js CHANGED
@@ -257,7 +257,9 @@ numux --prefix
257
257
  \`\`\`
258
258
 
259
259
  Auto-exits when all processes finish. Exit code 1 if any process failed.` },
260
- logging: { title: "Logging", body: `numux writes per-process log files (ANSI-stripped) when \`--log-dir\` is set or \`logDir\` is configured. Each session creates a timestamped subdirectory with a \`latest\` symlink pointing to the most recent run.
260
+ logging: { title: "Logging", body: `numux writes per-process log files (ANSI-stripped) for every run. By default they go to \`<tmpdir>/numux/<project-name>/\`, or to \`--log-dir\` / the \`logDir\` config when set. Each session creates a timestamped subdirectory with a \`latest\` symlink pointing to the most recent run.
261
+
262
+ The TUI prints \`Logs saved to: <session-dir>\` on exit (Ctrl+C or signal). Multiple numux instances in the same project share the default base dir, so each session has its own timestamped folder but the \`latest\` symlink will point at whichever started most recently. If \`numux logs\` falls back to the default and a \`latest\` symlink is in use, it prints a warning. To avoid the collision, set \`logDir\` per config or pass \`--log-dir\`.
261
263
 
262
264
  <!-- generated:logging-usage -->
263
265
  \`\`\`sh
@@ -546,7 +548,7 @@ var init_help = __esm(() => {
546
548
  var require_package = __commonJS((exports, module) => {
547
549
  module.exports = {
548
550
  name: "numux",
549
- version: "2.14.1",
551
+ version: "2.16.0",
550
552
  description: "Terminal multiplexer with dependency orchestration",
551
553
  type: "module",
552
554
  license: "MIT",
@@ -2900,6 +2902,53 @@ function evaluateCondition(condition) {
2900
2902
  // src/ui/app.ts
2901
2903
  import { BoxRenderable as BoxRenderable3, createCliRenderer } from "@opentui/core";
2902
2904
 
2905
+ // src/utils/shutdown.ts
2906
+ var finalized = false;
2907
+ function finalizeShutdown(logWriter, exitCode) {
2908
+ if (finalized)
2909
+ process.exit(exitCode);
2910
+ finalized = true;
2911
+ if (logWriter && !logWriter.isTemporary) {
2912
+ process.stderr.write(`Logs saved to: ${logWriter.getDirectory()}
2913
+ `);
2914
+ }
2915
+ logWriter?.cleanup();
2916
+ process.exit(exitCode);
2917
+ }
2918
+ function setupShutdownHandlers(app, logWriter) {
2919
+ let shuttingDown = false;
2920
+ const shutdown = () => {
2921
+ if (shuttingDown) {
2922
+ process.exit(1);
2923
+ }
2924
+ shuttingDown = true;
2925
+ app.shutdown().finally(() => {
2926
+ finalizeShutdown(logWriter, app.hasFailures() ? 1 : 0);
2927
+ });
2928
+ };
2929
+ process.on("SIGINT", shutdown);
2930
+ process.on("SIGTERM", shutdown);
2931
+ process.on("uncaughtException", (err) => {
2932
+ log("Uncaught exception:", err?.message ?? err);
2933
+ app.shutdown().finally(() => {
2934
+ process.stderr.write(`numux: unexpected error: ${err?.stack ?? err}
2935
+ `);
2936
+ logWriter?.cleanup();
2937
+ process.exit(1);
2938
+ });
2939
+ });
2940
+ process.on("unhandledRejection", (reason) => {
2941
+ const stack = reason instanceof Error ? reason.stack : String(reason);
2942
+ log("Unhandled rejection:", stack);
2943
+ app.shutdown().finally(() => {
2944
+ process.stderr.write(`numux: unhandled rejection: ${stack}
2945
+ `);
2946
+ logWriter?.cleanup();
2947
+ process.exit(1);
2948
+ });
2949
+ });
2950
+ }
2951
+
2903
2952
  // src/ui/help-overlay.ts
2904
2953
  import { BoxRenderable, TextRenderable } from "@opentui/core";
2905
2954
 
@@ -2921,7 +2970,7 @@ function toHintPair(hint) {
2921
2970
  }
2922
2971
  var STATUS_HINTS_COMPACT = [
2923
2972
  ["\u2190\u2192", "tabs"],
2924
- SHORTCUTS.search,
2973
+ SHORTCUTS.stopStart,
2925
2974
  SHORTCUTS.copy,
2926
2975
  ["Enter", "input"],
2927
2976
  ["H", "help"]
@@ -4274,9 +4323,6 @@ class TabBar {
4274
4323
  setSelectedIndex(index) {
4275
4324
  this.renderable.setSelectedIndex(index);
4276
4325
  }
4277
- focus() {
4278
- this.renderable.focus();
4279
- }
4280
4326
  }
4281
4327
 
4282
4328
  // src/ui/app.ts
@@ -4433,7 +4479,7 @@ class App {
4433
4479
  return;
4434
4480
  }
4435
4481
  this.shutdown().then(() => {
4436
- process.exit(this.hasFailures() ? 1 : 0);
4482
+ finalizeShutdown(this.logWriter, this.hasFailures() ? 1 : 0);
4437
4483
  });
4438
4484
  return;
4439
4485
  }
@@ -4569,7 +4615,6 @@ class App {
4569
4615
  });
4570
4616
  if (this.names.length > 0) {
4571
4617
  this.switchPane(this.names[0]);
4572
- this.tabBar.focus();
4573
4618
  }
4574
4619
  await this.manager.startAll(termCols, termRows);
4575
4620
  }
@@ -5155,46 +5200,6 @@ function defaultLogDir(cwd) {
5155
5200
  return join2(tmpdir2(), "numux", resolveProjectName(cwd));
5156
5201
  }
5157
5202
 
5158
- // src/utils/shutdown.ts
5159
- function setupShutdownHandlers(app, logWriter) {
5160
- let shuttingDown = false;
5161
- const shutdown = () => {
5162
- if (shuttingDown) {
5163
- process.exit(1);
5164
- }
5165
- shuttingDown = true;
5166
- app.shutdown().finally(() => {
5167
- if (logWriter && !logWriter.isTemporary) {
5168
- process.stderr.write(`Logs saved to: ${logWriter.getDirectory()}
5169
- `);
5170
- }
5171
- logWriter?.cleanup();
5172
- process.exit(app.hasFailures() ? 1 : 0);
5173
- });
5174
- };
5175
- process.on("SIGINT", shutdown);
5176
- process.on("SIGTERM", shutdown);
5177
- process.on("uncaughtException", (err) => {
5178
- log("Uncaught exception:", err?.message ?? err);
5179
- app.shutdown().finally(() => {
5180
- process.stderr.write(`numux: unexpected error: ${err?.stack ?? err}
5181
- `);
5182
- logWriter?.cleanup();
5183
- process.exit(1);
5184
- });
5185
- });
5186
- process.on("unhandledRejection", (reason) => {
5187
- const stack = reason instanceof Error ? reason.stack : String(reason);
5188
- log("Unhandled rejection:", stack);
5189
- app.shutdown().finally(() => {
5190
- process.stderr.write(`numux: unhandled rejection: ${stack}
5191
- `);
5192
- logWriter?.cleanup();
5193
- process.exit(1);
5194
- });
5195
- });
5196
- }
5197
-
5198
5203
  // src/index.ts
5199
5204
  var HELP = generateHelp();
5200
5205
  var INIT_TEMPLATE = `import { defineConfig } from 'numux'
@@ -5250,9 +5255,14 @@ async function main() {
5250
5255
  process.exit(0);
5251
5256
  }
5252
5257
  if (parsed.logs) {
5253
- const logDir2 = parsed.logDir ?? await resolveLogDir(parsed.configPath);
5258
+ const resolved = parsed.logDir ? { dir: parsed.logDir, explicit: true } : await resolveLogDir(parsed.configPath);
5259
+ const logDir2 = resolved.dir;
5254
5260
  const latestDir = resolve8(logDir2, "latest");
5255
- const target = existsSync6(latestDir) ? latestDir : logDir2;
5261
+ const usingLatest = existsSync6(latestDir);
5262
+ const target = usingLatest ? latestDir : logDir2;
5263
+ if (!resolved.explicit && usingLatest) {
5264
+ console.warn('Warning: using default log directory; "latest" may have been overwritten by another numux instance in this project.');
5265
+ }
5256
5266
  if (parsed.logsProcess) {
5257
5267
  const logFile2 = resolve8(target, `${parsed.logsProcess}.log`);
5258
5268
  if (!existsSync6(logFile2)) {
@@ -5451,10 +5461,10 @@ async function resolveLogDir(configPath) {
5451
5461
  try {
5452
5462
  const raw = await loadConfig(configPath);
5453
5463
  if (typeof raw.logDir === "string" && raw.logDir.trim()) {
5454
- return resolve8(raw.logDir.trim());
5464
+ return { dir: resolve8(raw.logDir.trim()), explicit: true };
5455
5465
  }
5456
5466
  } catch {}
5457
- return defaultLogDir(process.cwd());
5467
+ return { dir: defaultLogDir(process.cwd()), explicit: false };
5458
5468
  }
5459
5469
  main().catch((err) => {
5460
5470
  console.error(err instanceof Error ? err.message : err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "numux",
3
- "version": "2.14.1",
3
+ "version": "2.16.0",
4
4
  "description": "Terminal multiplexer with dependency orchestration",
5
5
  "type": "module",
6
6
  "license": "MIT",