@mutmutco/cli 2.36.0 → 2.38.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
@@ -55,6 +55,7 @@ mmi-cli doctor --json
55
55
  - `mmi-cli rcand`, `release`, and `hotfix` render guarded train plans; product trains trigger the Hub's central tenant deployer, while MMI-Hub releases directly from `development` to `main`.
56
56
  - `mmi-cli bootstrap`, `bootstrap verify`, and `bootstrap apply` plan, audit, and seed repo onboarding.
57
57
  - `mmi-cli access audit` checks collaborator roles and train-branch allowlists.
58
+ - `mmi-cli throttle report` reads `.mmi/throttle/trace.jsonl` and prints gate denial stats from the org Scrooge v2 Read/Shell gates (Claude Code + Cursor IDE) — files ≥50 KB require an explicit positive Read `limit`; output separates `denied (block)` vs `would-block (observe)`.
58
59
  - `mmi-cli doctor` checks GitHub auth, repo config, CLI availability, plugin install/config/version state, and stale MMI plugin cache dirs, auto-repairing the safe gaps.
59
60
 
60
61
  Hub API calls do not send the raw GitHub token on every request. The CLI exchanges it at `/auth/session`
package/dist/index.cjs CHANGED
@@ -60,26 +60,61 @@ var init_client_version = __esm({
60
60
  function setInjectedStdin(payload) {
61
61
  injectedStdin = payload;
62
62
  }
63
- function stdinHasPipedInput(statFd = () => (0, import_node_fs2.fstatSync)(0)) {
63
+ function stdinHasPipedInput(statFd = () => (0, import_node_fs2.fstatSync)(0), getIsTTY = () => process.stdin.isTTY) {
64
64
  try {
65
65
  const stat = statFd();
66
- return stat.isFIFO() || stat.isFile();
66
+ if (stat.isFIFO() || stat.isFile()) return true;
67
+ if (stat.isCharacterDevice()) return false;
68
+ if (stat.isSocket()) return false;
69
+ if (getIsTTY() === true) return false;
70
+ return true;
67
71
  } catch {
68
72
  return false;
69
73
  }
70
74
  }
71
- async function readStdin() {
75
+ async function readStdin(opts = {}) {
72
76
  if (injectedStdin !== void 0) return injectedStdin;
73
77
  if (!stdinHasPipedInput()) return "";
78
+ const maxBytes = opts.maxBytes ?? STDIN_MAX_BYTES;
79
+ const timeoutMs = opts.timeoutMs ?? STDIN_DRAIN_TIMEOUT_MS;
74
80
  const chunks = [];
75
- for await (const chunk of process.stdin) chunks.push(chunk);
81
+ let total = 0;
82
+ const drain = (async () => {
83
+ for await (const chunk of process.stdin) {
84
+ const buf = chunk;
85
+ const room = maxBytes - total;
86
+ if (buf.length >= room) {
87
+ chunks.push(buf.subarray(0, room));
88
+ total = maxBytes;
89
+ break;
90
+ }
91
+ chunks.push(buf);
92
+ total += buf.length;
93
+ }
94
+ })().catch(() => {
95
+ });
96
+ let timer;
97
+ const timeout = new Promise((resolve) => {
98
+ timer = setTimeout(resolve, timeoutMs);
99
+ });
100
+ try {
101
+ await Promise.race([drain, timeout]);
102
+ } finally {
103
+ if (timer) clearTimeout(timer);
104
+ try {
105
+ process.stdin.unref();
106
+ } catch {
107
+ }
108
+ }
76
109
  return Buffer.concat(chunks).toString("utf8");
77
110
  }
78
- var import_node_fs2, injectedStdin;
111
+ var import_node_fs2, injectedStdin, STDIN_MAX_BYTES, STDIN_DRAIN_TIMEOUT_MS;
79
112
  var init_stdin_inject = __esm({
80
113
  "src/stdin-inject.ts"() {
81
114
  "use strict";
82
115
  import_node_fs2 = require("node:fs");
116
+ STDIN_MAX_BYTES = 8 * 1024 * 1024;
117
+ STDIN_DRAIN_TIMEOUT_MS = 5e3;
83
118
  }
84
119
  });
85
120