memtrace 0.3.42 → 0.3.43

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/bin/memtrace.js CHANGED
@@ -138,12 +138,17 @@ if (args[0] === "install" || args[0] === "update" || args[0] === "upgrade") {
138
138
  const memtraceCmd = platformBinary("memtrace", process.platform);
139
139
 
140
140
  process.stdout.write("memtrace: fetching latest from npm registry…\n");
141
+ // Tell the postinstall RTK opt-in to stay silent — *we* own the
142
+ // prompt and have a clean TTY (npm's progress spinner shares the
143
+ // user's terminal with the postinstall, which makes its prompt
144
+ // unreadable and readline picks up a phantom EOF, default-yes
145
+ // installing RTK without the user seeing the question).
141
146
  const installResult = spawnSync(
142
147
  npmCmd,
143
148
  ["install", "-g", "memtrace@latest"],
144
149
  spawnOptionsForPlatform(process.platform, {
145
150
  stdio: "inherit",
146
- env: process.env,
151
+ env: { ...process.env, MEMTRACE_INSTALL_PARENT: "1" },
147
152
  })
148
153
  );
149
154
 
package/install.js CHANGED
@@ -197,6 +197,14 @@ if (require.main === module) {
197
197
  const readline = require("readline");
198
198
  const rtk = require("./lib/rtk-integration");
199
199
 
200
+ // Nested under `memtrace install` shim? The parent owns the prompt
201
+ // and has a clean TTY. Two prompt paths racing over the same
202
+ // terminal means npm's progress spinner overwrites the question
203
+ // line and readline observes a phantom EOF — user gets RTK
204
+ // installed without ever seeing or answering Y/n. Bail out here;
205
+ // bin/memtrace.js will run the prompt cleanly after npm exits.
206
+ if (rtk.isPostinstallNestedUnderShim({ env: process.env })) return;
207
+
200
208
  let alreadyInstalled = false;
201
209
  try { alreadyInstalled = rtk.detectRtk(); } catch (_) { /* skip */ }
202
210
 
@@ -53,6 +53,23 @@ function isRtkAutoInstall(input = {}) {
53
53
  return isTruthyEnv(env.MEMTRACE_AUTO_INSTALL_RTK);
54
54
  }
55
55
 
56
+ /**
57
+ * True when this postinstall is running nested under the `memtrace
58
+ * install` shim — i.e. the parent process is bin/memtrace.js spawning
59
+ * `npm install -g memtrace@latest`. The parent owns user interaction
60
+ * (it has a clean TTY, and it runs its own RTK opt-in flow after npm
61
+ * exits). The nested postinstall must therefore stay completely silent
62
+ * for RTK — otherwise both prompt paths race over the same terminal,
63
+ * the spinner overwrites the prompt, and readline picks up a phantom
64
+ * EOF.
65
+ *
66
+ * Coordinated via `MEMTRACE_INSTALL_PARENT=1` in the spawn env.
67
+ */
68
+ function isPostinstallNestedUnderShim(input = {}) {
69
+ const env = input.env || {};
70
+ return isTruthyEnv(env.MEMTRACE_INSTALL_PARENT);
71
+ }
72
+
56
73
  // ── Decision helpers ────────────────────────────────────────────────
57
74
 
58
75
  /**
@@ -275,19 +292,27 @@ function openTtyStreams(opts = {}) {
275
292
  const fs = opts.fs || require("fs");
276
293
  const tty = opts.tty || require("tty");
277
294
 
278
- let fd;
295
+ // Open TWO fds — one for read, one for write. Sharing a single
296
+ // r+ fd between ReadStream and WriteStream causes readline to
297
+ // immediately observe EOF on some setups (notably when a parent
298
+ // process is also writing progress to the same controlling tty,
299
+ // e.g. npm's spinner during `memtrace install`).
300
+ let inFd, outFd;
279
301
  try {
280
- fd = fs.openSync("/dev/tty", "r+");
302
+ inFd = fs.openSync("/dev/tty", "r");
303
+ outFd = fs.openSync("/dev/tty", "w");
281
304
  } catch (_) {
305
+ if (inFd != null) { try { fs.closeSync(inFd); } catch (_) {} }
282
306
  return null; // no controlling tty (Docker, CI, etc.)
283
307
  }
284
308
 
285
309
  let input, output;
286
310
  try {
287
- input = new tty.ReadStream(fd);
288
- output = new tty.WriteStream(fd);
311
+ input = new tty.ReadStream(inFd);
312
+ output = new tty.WriteStream(outFd);
289
313
  } catch (e) {
290
- try { fs.closeSync(fd); } catch (_) { /* best-effort */ }
314
+ try { fs.closeSync(inFd); } catch (_) {}
315
+ try { fs.closeSync(outFd); } catch (_) {}
291
316
  return null;
292
317
  }
293
318
 
@@ -341,6 +366,7 @@ module.exports = {
341
366
  // Pure helpers
342
367
  isRtkPromptDisabled,
343
368
  isRtkAutoInstall,
369
+ isPostinstallNestedUnderShim,
344
370
  shouldPromptForRtk,
345
371
  effectiveRtkAction,
346
372
  chooseInstallStrategy,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memtrace",
3
- "version": "0.3.42",
3
+ "version": "0.3.43",
4
4
  "description": "Code intelligence graph — MCP server + AI agent skills + visualization UI",
5
5
  "keywords": [
6
6
  "mcp",