@usepowerplant/cli 0.4.11 → 0.4.12

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
@@ -226,11 +226,12 @@ npm install -g --prefix ~/.powerplant-staging @usepowerplant/cli@next
226
226
  ~/.powerplant-staging/bin/powerplant init --env staging
227
227
  ```
228
228
 
229
- `init --env staging` also points each selected agent's shared `powerplant`
230
- MCP connection at staging. Separate npm prefixes do not isolate that entry.
231
- After testing, run `powerplant connect` from your production install to
232
- restore the agents' production connection; the staging sync service keeps
233
- running. Include `--env staging` on every staging command.
229
+ `init --env staging` registers its own MCP entry, `powerplant-staging`, in each
230
+ selected agent, beside the production `powerplant` entry. Agents see its tools
231
+ as `mcp__powerplant-staging__*`. A CLI older than this change registered
232
+ staging as `powerplant`: if that entry still points at staging, run a bare
233
+ `powerplant connect` from your production install once to restore it. Include
234
+ `--env staging` on every staging command.
234
235
 
235
236
  The `@next` pin matters: a bare install resolves `latest`, the production
236
237
  channel, so the new staging copy would start on the promoted release and
package/dist/cli.js CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env node
2
+
3
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="7d73a97a-938c-5f0f-8b6b-c4076b82abad")}catch(e){}}();
2
4
  const __cliNodeMajor = Number(process.versions.node.split('.')[0]);
3
5
  if (__cliNodeMajor < 20) {
4
6
  console.error(`powerplant needs Node 20 or newer — this is Node ${process.versions.node}.`);
@@ -56698,12 +56700,13 @@ function boundRedactedText(raw, maxBytes) {
56698
56700
  return boundText(redactSecrets(raw), maxBytes);
56699
56701
  }
56700
56702
  function boundText(redacted, maxBytes) {
56701
- const bytes = utf8Encoder.encode(redacted);
56703
+ const text = redacted.includes("\0") ? redacted.replaceAll("\0", "\uFFFD") : redacted;
56704
+ const bytes = utf8Encoder.encode(text);
56702
56705
  if (bytes.length <= maxBytes) {
56703
- return { text: redacted, truncated: false, originalBytes: bytes.length };
56706
+ return { text, truncated: false, originalBytes: bytes.length };
56704
56707
  }
56705
- const text = utf8Decoder.decode(bytes.subarray(0, maxBytes)).replace(/�+$/u, "");
56706
- return { text, truncated: true, originalBytes: bytes.length };
56708
+ const cut = utf8Decoder.decode(bytes.subarray(0, maxBytes)).replace(/�+$/u, "");
56709
+ return { text: cut, truncated: true, originalBytes: bytes.length };
56707
56710
  }
56708
56711
 
56709
56712
  // ../core/src/codeHost.ts
@@ -56755,18 +56758,35 @@ function pullRequestRefsInText(text) {
56755
56758
  }
56756
56759
  return refs;
56757
56760
  }
56761
+ var PULL_REQUEST_ALREADY_EXISTS = /a pull request for branch \S+ into branch \S+ already exists:/;
56762
+ function pullRequestAlreadyExistsInText(text) {
56763
+ return PULL_REQUEST_ALREADY_EXISTS.test(text);
56764
+ }
56758
56765
  function pullRequestRefFromUrl(url2) {
56759
56766
  return pullRequestRefsInText(url2)[0] ?? null;
56760
56767
  }
56761
56768
  var MAY_TOUCH_HOST = /\b(?:git|gh)\b/;
56762
56769
  function readCodeHostCommands(command, currentBranch) {
56770
+ return readCommands(command, currentBranch, 0);
56771
+ }
56772
+ var SUBSTITUTION_DEPTH_MAX = 4;
56773
+ function readCommands(command, currentBranch, depth) {
56763
56774
  const result = { pushes: [], pullRequestCreates: [] };
56764
56775
  if (!MAY_TOUCH_HOST.test(command)) return result;
56765
56776
  const chain = shellCommands(command);
56766
56777
  let branch = currentBranch;
56767
56778
  chain.forEach((simple, i) => {
56768
56779
  const program = programOf(simple.words);
56769
- if (program === null) return;
56780
+ if (program === null) {
56781
+ if (depth >= SUBSTITUTION_DEPTH_MAX) return;
56782
+ const alone = simple.words.length === 1 && simple.next === null;
56783
+ for (const substituted of simple.assignedSubstitutions) {
56784
+ for (const create of readCommands(substituted, branch, depth + 1).pullRequestCreates) {
56785
+ result.pullRequestCreates.push({ ...create, ownExitStatus: create.ownExitStatus && alone });
56786
+ }
56787
+ }
56788
+ return;
56789
+ }
56770
56790
  if (program.name === "git") {
56771
56791
  const git = gitSubcommand(program.args);
56772
56792
  if (git === null) return;
@@ -56799,6 +56819,7 @@ function pushOutcomeInText(text, branch) {
56799
56819
  return /Everything up-to-date/.test(text) ? true : null;
56800
56820
  }
56801
56821
  var ENV_ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/;
56822
+ var ASSIGNMENT_NAME = /^[A-Za-z_][A-Za-z0-9_]*=$/;
56802
56823
  function programOf(words) {
56803
56824
  let i = 0;
56804
56825
  while (i < words.length && (ENV_ASSIGNMENT.test(words[i]) || words[i] === "env")) i += 1;
@@ -56895,6 +56916,7 @@ function exitStatusIsOwn(chain, index) {
56895
56916
  function shellCommands(text) {
56896
56917
  const commands = [];
56897
56918
  let words = [];
56919
+ let assignedSubstitutions = [];
56898
56920
  let word = "";
56899
56921
  let inWord = false;
56900
56922
  let quote = null;
@@ -56906,8 +56928,9 @@ function shellCommands(text) {
56906
56928
  };
56907
56929
  const endCommand = (next) => {
56908
56930
  endWord();
56909
- if (words.length > 0) commands.push({ words: withoutRedirections(words), next });
56931
+ if (words.length > 0) commands.push({ words: withoutRedirections(words), assignedSubstitutions, next });
56910
56932
  words = [];
56933
+ assignedSubstitutions = [];
56911
56934
  };
56912
56935
  for (let i = 0; i < text.length; i++) {
56913
56936
  const ch = text[i];
@@ -56927,6 +56950,11 @@ function shellCommands(text) {
56927
56950
  inWord = true;
56928
56951
  } else if (ch === "$" && text[i + 1] === "(" || ch === "`") {
56929
56952
  const end = substitutionEnd(text, i);
56953
+ const after = text[end + 1];
56954
+ const endsWord = after === void 0 || /[\s;&|)]/.test(after);
56955
+ if (ch === "$" && text[end] === ")" && ASSIGNMENT_NAME.test(word) && endsWord) {
56956
+ assignedSubstitutions.push(text.slice(i + 2, end));
56957
+ }
56930
56958
  word += text.slice(i, end + 1);
56931
56959
  inWord = true;
56932
56960
  i = end;
@@ -57037,6 +57065,9 @@ var PrEvidenceCollector = class {
57037
57065
  evidence = [];
57038
57066
  seen = /* @__PURE__ */ new Set();
57039
57067
  watched = /* @__PURE__ */ new Map();
57068
+ /** By process id: what a watched call left unsettled when its result said
57069
+ * the process still runs. */
57070
+ running = /* @__PURE__ */ new Map();
57040
57071
  dropped = 0;
57041
57072
  /** The harness recorded the pull request for this session. Repeats (a
57042
57073
  * record per turn while a PR stays bound) keep the first time. */
@@ -57045,15 +57076,50 @@ var PrEvidenceCollector = class {
57045
57076
  }
57046
57077
  /** A shell command the session ran: watched until its result arrives when
57047
57078
  * it pushes a branch or creates a pull request, ignored otherwise. */
57048
- command(callId, command, currentBranch) {
57049
- this.watch(callId, readCodeHostCommands(command, currentBranch));
57079
+ command(callId, command, currentBranch, startedAt = null) {
57080
+ this.watch(callId, readCodeHostCommands(command, currentBranch), startedAt);
57050
57081
  }
57051
57082
  /** A tool call that would push or create a pull request, in the code host
57052
57083
  * adapter's terms (a harness's own pull request tool, for example). */
57053
- watch(callId, commands) {
57084
+ watch(callId, commands, startedAt = null) {
57054
57085
  if (commands.pushes.length === 0 && commands.pullRequestCreates.length === 0) return;
57055
- this.watched.set(callId, commands);
57056
- if (this.watched.size > WATCHED_CALLS_MAX) this.watched.delete(this.watched.keys().next().value);
57086
+ keepBounded(this.watched, callId, { ...commands, startedAt });
57087
+ }
57088
+ /** A call that reads more output of a running process (Codex's
57089
+ * write_stdin poll; Claude Code's TaskOutput, or a read of a background
57090
+ * task's output file): its result is read as the rest of the watched call
57091
+ * that started the process. */
57092
+ follow(callId, process2) {
57093
+ const unsettled = this.running.get(process2);
57094
+ if (unsettled === void 0) return;
57095
+ this.running.delete(process2);
57096
+ this.watch(callId, unsettled, unsettled.startedAt);
57097
+ }
57098
+ /** Whether any process is followed, so a parser reads an unwatched
57099
+ * call's output only while it could matter. */
57100
+ following() {
57101
+ return this.running.size > 0;
57102
+ }
57103
+ /** Another call now runs a process under this id: a later poll of the id
57104
+ * is no longer the rest of the watched call that left it running. */
57105
+ forget(process2) {
57106
+ this.running.delete(process2);
57107
+ }
57108
+ /** Watches several watched calls as one, in the order given: the parts of
57109
+ * a call whose output cannot be told apart, read once as a whole. The
57110
+ * parts are one call's, so they started together and the first part's
57111
+ * time is the whole's. */
57112
+ merge(callIds, into) {
57113
+ const parts = callIds.flatMap((callId) => this.watched.get(callId) ?? []);
57114
+ for (const callId of callIds) this.watched.delete(callId);
57115
+ this.watch(
57116
+ into,
57117
+ {
57118
+ pushes: parts.flatMap((part) => part.pushes),
57119
+ pullRequestCreates: parts.flatMap((part) => part.pullRequestCreates)
57120
+ },
57121
+ parts[0]?.startedAt ?? null
57122
+ );
57057
57123
  }
57058
57124
  watching(callId) {
57059
57125
  return this.watched.has(callId);
@@ -57070,7 +57136,16 @@ var PrEvidenceCollector = class {
57070
57136
  * result names the pull request: gh prints the created PR's URL last, so
57071
57137
  * a call with one creation takes the last URL and a call with several
57072
57138
  * only when the text names exactly as many; a failed call whose status
57073
- * is the creation's own counts nothing.
57139
+ * is the creation's own counts nothing, and neither does a call whose
57140
+ * text carries gh's refusal because the pull request exists (the URL it
57141
+ * prints is that pull request's, and a result read from a file states no
57142
+ * status). A text cut to the URL alone (`| tail -1`, a `grep` of the
57143
+ * output file) hides the refusal, and the URL then counts.
57144
+ *
57145
+ * When the result says one process still runs, whatever it settled
57146
+ * neither way waits for that process's later output (`follow`). A push
57147
+ * git rejected, or a command whose own status failed, is settled: a
57148
+ * later output never revives it.
57074
57149
  */
57075
57150
  result(callId, outcome) {
57076
57151
  const commands = this.watched.get(callId);
@@ -57079,35 +57154,45 @@ var PrEvidenceCollector = class {
57079
57154
  const destinations = pushDestinationsInText(outcome.text);
57080
57155
  const oneRemote = new Set(commands.pushes.map((push) => push.remote)).size === 1;
57081
57156
  const shared = oneRemote && new Set(destinations).size === 1 ? destinations[0] : null;
57157
+ const unsettled = { pushes: [], pullRequestCreates: [], startedAt: commands.startedAt };
57082
57158
  for (const [i, push] of commands.pushes.entries()) {
57083
57159
  if (push.branch === null) continue;
57084
- const pushed = pushOutcomeInText(outcome.text, push.branch) ?? (push.ownExitStatus && outcome.succeeded === true);
57085
- if (!pushed) continue;
57160
+ const pushed = pushOutcomeInText(outcome.text, push.branch) ?? (push.ownExitStatus ? outcome.succeeded : null);
57161
+ if (pushed === null) unsettled.pushes.push(push);
57162
+ if (pushed !== true) continue;
57086
57163
  const repository = destinations.length === commands.pushes.length ? destinations[i] : shared;
57087
57164
  this.addPush({ kind: "branch-pushed", at: outcome.at, prId: null, repository, branch: push.branch, line: outcome.line });
57088
57165
  }
57089
57166
  const creates = commands.pullRequestCreates;
57090
- if (creates.length === 0) return;
57091
57167
  const refs = pullRequestRefsInText(outcome.text);
57092
57168
  const paired = creates.length === 1 ? [refs.at(-1)] : refs.length === creates.length ? refs : [];
57169
+ const refused = pullRequestAlreadyExistsInText(outcome.text);
57093
57170
  creates.forEach((create, i) => {
57094
57171
  const ref = paired[i];
57095
- if (ref === void 0 || outcome.succeeded === false && create.ownExitStatus) return;
57172
+ if (refused || outcome.succeeded === false && create.ownExitStatus) return;
57173
+ if (ref === void 0) {
57174
+ unsettled.pullRequestCreates.push(create);
57175
+ return;
57176
+ }
57096
57177
  this.add({
57097
57178
  kind: "pull-request-created",
57098
57179
  at: outcome.at,
57099
57180
  prId: pullRequestId(ref),
57100
57181
  repository: null,
57101
57182
  branch: create.head,
57102
- line: outcome.line
57183
+ line: outcome.line,
57184
+ commandStartedAt: commands.startedAt
57103
57185
  });
57104
57186
  });
57187
+ if (outcome.running === null || unsettled.pushes.length === 0 && unsettled.pullRequestCreates.length === 0) return;
57188
+ keepBounded(this.running, outcome.running, unsettled);
57105
57189
  }
57106
57190
  /** The evidence collected, in transcript order, with pushes past the cap
57107
- * dropped oldest first. Calls still watched are dropped: their results
57108
- * never arrived. */
57191
+ * dropped oldest first. Calls still watched and processes still followed
57192
+ * are dropped: their results never arrived. */
57109
57193
  finish() {
57110
57194
  this.watched.clear();
57195
+ this.running.clear();
57111
57196
  const pushes = this.evidence.filter((item) => item.kind === "branch-pushed");
57112
57197
  if (pushes.length <= PUSH_EVIDENCE_MAX) return this.evidence;
57113
57198
  const cut = new Set(pushes.slice(0, pushes.length - PUSH_EVIDENCE_MAX));
@@ -57119,10 +57204,13 @@ var PrEvidenceCollector = class {
57119
57204
  droppedPushes() {
57120
57205
  return this.dropped;
57121
57206
  }
57122
- /** A link or a creation is one fact per pull request: repeats keep the
57123
- * first. */
57207
+ /** A link is one fact per pull request, and so is a creation per command:
57208
+ * repeats keep the first. Two commands that each named the pull request
57209
+ * are two facts (a refused retry read before the first command's own
57210
+ * output), because the match keeps only the one whose command can have
57211
+ * opened it. */
57124
57212
  add(item) {
57125
- const key = `${item.kind}|${item.prId ?? ""}|${item.branch ?? ""}`;
57213
+ const key = `${item.kind}|${item.prId ?? ""}|${item.branch ?? ""}|${item.commandStartedAt ?? ""}`;
57126
57214
  if (this.seen.has(key)) return;
57127
57215
  this.seen.add(key);
57128
57216
  this.evidence.push(item);
@@ -57136,6 +57224,11 @@ var PrEvidenceCollector = class {
57136
57224
  this.evidence.push(item);
57137
57225
  }
57138
57226
  };
57227
+ function keepBounded(map2, key, value) {
57228
+ map2.delete(key);
57229
+ map2.set(key, value);
57230
+ if (map2.size > WATCHED_CALLS_MAX) map2.delete(map2.keys().next().value);
57231
+ }
57139
57232
 
57140
57233
  // ../core/src/digest.ts
57141
57234
  var TOOL_CALL_INPUT_MAX_BYTES = 65536;
@@ -57155,6 +57248,7 @@ function createDigestTurnParser(options = {}) {
57155
57248
  let pendingEnvelopes = [];
57156
57249
  let currentEnvelope = null;
57157
57250
  const evidence = new PrEvidenceCollector();
57251
+ const backgroundTasks = { started: /* @__PURE__ */ new Map(), reads: /* @__PURE__ */ new Map() };
57158
57252
  let line = -1;
57159
57253
  const landPending = (turn) => {
57160
57254
  for (const envelope of pendingEnvelopes) {
@@ -57184,7 +57278,7 @@ function createDigestTurnParser(options = {}) {
57184
57278
  if (record2.isMeta === true) return;
57185
57279
  const at = recordTime(record2);
57186
57280
  if (record2.type === "user") {
57187
- toolResults(record2, evidence, line);
57281
+ toolResults(record2, evidence, backgroundTasks, line);
57188
57282
  const text = userText(record2);
57189
57283
  if (text) {
57190
57284
  const origin = claudeCodeMessageOrigin(originFacts(record2), text);
@@ -57233,8 +57327,9 @@ function createDigestTurnParser(options = {}) {
57233
57327
  }
57234
57328
  pendingTools.push(call);
57235
57329
  if (part.name === "Bash" && typeof part.id === "string" && typeof part.input?.command === "string") {
57236
- evidence.command(part.id, part.input.command, recordBranch(record2));
57330
+ evidence.command(part.id, part.input.command, recordBranch(record2), at);
57237
57331
  }
57332
+ if (typeof part.id === "string") followBackgroundTask(part.id, part.name, part.input, evidence, backgroundTasks);
57238
57333
  }
57239
57334
  if (part.type === "text" && typeof part.text === "string" && part.text.trim() !== "") {
57240
57335
  const origin = claudeCodeMessageOrigin(facts, part.text);
@@ -57311,16 +57406,38 @@ function isRecord(value) {
57311
57406
  function recordBranch(record2) {
57312
57407
  return typeof record2.gitBranch === "string" && record2.gitBranch !== "" ? record2.gitBranch : null;
57313
57408
  }
57314
- function toolResults(record2, evidence, line) {
57409
+ function followBackgroundTask(callId, tool, input2, evidence, tasks) {
57410
+ if (tasks.started.size === 0 || evidence.watching(callId)) return;
57411
+ const named = tool === "TaskOutput" ? input2?.task_id : tool === "Read" ? input2?.file_path : tool === "Bash" ? input2?.command : void 0;
57412
+ if (typeof named !== "string" || tool === "Bash" && RUNS_GH.test(named)) return;
57413
+ const read = new Set(
57414
+ [...tasks.started].filter(([task]) => tool === "TaskOutput" ? named === task : named.includes(`/${task}.output`)).map(([, followed2]) => followed2)
57415
+ );
57416
+ if (read.size !== 1) return;
57417
+ const [followed] = read;
57418
+ evidence.follow(callId, followed);
57419
+ if (evidence.watching(callId)) keepBounded(tasks.reads, callId, followed);
57420
+ }
57421
+ var RUNS_GH = /(?:^|[\s;&|(])gh\s/;
57422
+ function toolResults(record2, evidence, tasks, line) {
57315
57423
  const content = isRecord(record2.message) ? record2.message.content : void 0;
57316
57424
  if (!Array.isArray(content)) return;
57317
57425
  const result = isRecord(record2.toolUseResult) ? record2.toolUseResult : {};
57426
+ const file2 = isRecord(result.file) ? result.file : {};
57427
+ const task = isRecord(result.task) ? result.task : {};
57318
57428
  for (const block of content) {
57319
57429
  if (!isRecord(block) || block.type !== "tool_result" || typeof block.tool_use_id !== "string") continue;
57320
57430
  if (!evidence.watching(block.tool_use_id)) continue;
57431
+ const reads = tasks.reads.get(block.tool_use_id);
57432
+ tasks.reads.delete(block.tool_use_id);
57433
+ const started = typeof result.backgroundTaskId === "string" ? result.backgroundTaskId : null;
57434
+ if (started !== null) keepBounded(tasks.started, started, reads ?? started);
57435
+ const failed2 = block.is_error === true || result.interrupted === true;
57436
+ const taskExited = typeof task.exitCode === "number" && task.task_id === reads;
57437
+ const status = reads !== void 0 ? { succeeded: taskExited ? task.exitCode === 0 : null, running: taskExited ? null : reads } : { succeeded: failed2 ? false : started !== null ? null : true, running: started };
57321
57438
  evidence.result(block.tool_use_id, {
57322
- succeeded: block.is_error !== true && result.interrupted !== true && result.backgroundTaskId === void 0,
57323
- text: [result.stdout, result.stderr].filter((part) => typeof part === "string").concat(contentText(block.content)).join("\n"),
57439
+ ...status,
57440
+ text: [result.stdout, result.stderr, file2.content, task.output].filter((part) => typeof part === "string").concat(contentText(block.content)).join("\n"),
57324
57441
  at: recordTime(record2),
57325
57442
  line
57326
57443
  });
@@ -57988,6 +58105,8 @@ function createCodexTurnParser(options = {}) {
57988
58105
  let pendingTools = [];
57989
58106
  let pendingAt = null;
57990
58107
  const evidence = new PrEvidenceCollector();
58108
+ const scripts = /* @__PURE__ */ new Map();
58109
+ const runningScripts = /* @__PURE__ */ new Map();
57991
58110
  let line = -1;
57992
58111
  const envelopes = [];
57993
58112
  let pendingEnvelopes = [];
@@ -58047,9 +58166,24 @@ function createCodexTurnParser(options = {}) {
58047
58166
  const responseItem = item;
58048
58167
  const callId = typeof responseItem.call_id === "string" ? responseItem.call_id : null;
58049
58168
  if (callId !== null && isToolOutput(responseItem)) {
58050
- if (evidence.watching(callId)) {
58169
+ const script = scripts.get(callId);
58170
+ scripts.delete(callId);
58171
+ if (script !== void 0) {
58172
+ const [header = "", ...blocks] = outputBlocks(responseItem.output);
58173
+ const cell = SCRIPT_STILL_RUNNING.exec(header)?.[1];
58174
+ if (cell === void 0) scriptResult(evidence, script, header, blocks, at, line);
58175
+ else keepBounded(runningScripts, cell, script);
58176
+ } else if (evidence.watching(callId)) {
58051
58177
  const text2 = contentText(responseItem.output);
58052
- evidence.result(callId, { succeeded: codexExitStatus(text2), text: text2, at, line });
58178
+ evidence.result(callId, { succeeded: codexExitStatus(text2), text: text2, running: codexRunningProcess(text2), at, line });
58179
+ } else if (runningScripts.size > 0 || evidence.following()) {
58180
+ const [header = "", ...blocks] = outputBlocks(responseItem.output);
58181
+ const cell = SCRIPT_STILL_RUNNING.exec(header)?.[1];
58182
+ if (cell !== void 0) runningScripts.delete(cell);
58183
+ for (const text2 of [header, ...blocks]) {
58184
+ const process2 = codexRunningProcess(text2);
58185
+ if (process2 !== null) evidence.forget(process2);
58186
+ }
58053
58187
  }
58054
58188
  return;
58055
58189
  }
@@ -58062,7 +58196,10 @@ function createCodexTurnParser(options = {}) {
58062
58196
  }
58063
58197
  pendingTools.push(call);
58064
58198
  pendingAt ??= at;
58065
- if (callId !== null) watchCodexCall(evidence, callId, responseItem);
58199
+ if (callId !== null) {
58200
+ const script = watchCodexCall(evidence, callId, responseItem, at, runningScripts);
58201
+ if (script !== null) keepBounded(scripts, callId, script);
58202
+ }
58066
58203
  return;
58067
58204
  }
58068
58205
  if (responseItem.type !== "message") return;
@@ -58105,27 +58242,76 @@ function createCodexTurnParser(options = {}) {
58105
58242
  }
58106
58243
  };
58107
58244
  }
58108
- function watchCodexCall(evidence, callId, item) {
58245
+ var stepKey = (callId, step) => `${callId}#${step}`;
58246
+ function watchCodexCall(evidence, callId, item, at, runningScripts) {
58109
58247
  if (item.type === "custom_tool_call" && item.name === "exec" && typeof item.input === "string") {
58110
- evidence.command(callId, execCommandStrings(item.input).join("\n"), null);
58111
- return;
58248
+ const steps = execScriptSteps(item.input);
58249
+ const commands = [];
58250
+ let seen = { pushes: 0, pullRequestCreates: 0 };
58251
+ steps.forEach((step, i) => {
58252
+ if (step.polls !== null) evidence.follow(stepKey(callId, i), step.polls);
58253
+ if (step.command === null) return;
58254
+ commands.push(step.command);
58255
+ const read = readCodeHostCommands(commands.join("\n"), null);
58256
+ evidence.watch(
58257
+ stepKey(callId, i),
58258
+ {
58259
+ pushes: read.pushes.slice(seen.pushes),
58260
+ pullRequestCreates: read.pullRequestCreates.slice(seen.pullRequestCreates)
58261
+ },
58262
+ at
58263
+ );
58264
+ seen = { pushes: read.pushes.length, pullRequestCreates: read.pullRequestCreates.length };
58265
+ });
58266
+ const watched = steps.some((_, i) => evidence.watching(stepKey(callId, i)));
58267
+ return watched ? { callId, polls: steps.map((step) => step.polls), printsEachStep: printsEachStep(item.input, steps) } : null;
58112
58268
  }
58113
58269
  if (item.type === "local_shell_call") {
58114
58270
  const command = argvCommand(asRecord2(item.action)?.command);
58115
- if (command !== null) evidence.command(callId, command, null);
58116
- return;
58271
+ if (command !== null) evidence.command(callId, command, null, at);
58272
+ return null;
58117
58273
  }
58118
- if (item.type !== "function_call" || typeof item.arguments !== "string") return;
58274
+ if (item.type !== "function_call" || typeof item.arguments !== "string") return null;
58119
58275
  const args = safeJsonRecord(item.arguments);
58120
58276
  if (item.name === "exec_command" && typeof args?.cmd === "string") {
58121
- evidence.command(callId, args.cmd, null);
58277
+ evidence.command(callId, args.cmd, null, at);
58122
58278
  } else if (item.name === "shell") {
58123
58279
  const command = argvCommand(args?.command);
58124
- if (command !== null) evidence.command(callId, command, null);
58280
+ if (command !== null) evidence.command(callId, command, null, at);
58281
+ } else if (item.name === "write_stdin" && typeof args?.session_id === "number") {
58282
+ evidence.follow(callId, String(args.session_id));
58283
+ } else if (item.name === "wait" && (typeof args?.cell_id === "string" || typeof args?.cell_id === "number")) {
58284
+ const script = runningScripts.get(String(args.cell_id)) ?? null;
58285
+ runningScripts.delete(String(args.cell_id));
58286
+ return script;
58125
58287
  } else if (item.name === "_create_pull_request") {
58126
58288
  const head = typeof args?.head === "string" ? args.head : null;
58127
- evidence.watch(callId, { pushes: [], pullRequestCreates: [{ head, ownExitStatus: true }] });
58289
+ evidence.watch(callId, { pushes: [], pullRequestCreates: [{ head, ownExitStatus: true }] }, at);
58128
58290
  }
58291
+ return null;
58292
+ }
58293
+ function scriptResult(evidence, script, header, blocks, at, line) {
58294
+ const paired = script.printsEachStep && blocks.length === script.polls.length;
58295
+ const keys = script.polls.map((_, i) => stepKey(script.callId, i));
58296
+ if (!paired) {
58297
+ evidence.merge(keys.filter((_, i) => script.polls[i] === null), script.callId);
58298
+ evidence.result(script.callId, { succeeded: null, text: [header, ...blocks].join("\n"), running: null, at, line });
58299
+ }
58300
+ script.polls.forEach((polls, i) => {
58301
+ if (!evidence.watching(keys[i])) return;
58302
+ const text = paired ? blocks[i] : "";
58303
+ const succeeded = paired ? codexExitStatus(text) : null;
58304
+ const running = (paired ? codexRunningProcess(text) : null) ?? (succeeded === null ? polls : null);
58305
+ evidence.result(keys[i], { succeeded, text, running, at, line });
58306
+ });
58307
+ }
58308
+ function outputBlocks(output2) {
58309
+ if (typeof output2 === "string") return [output2];
58310
+ if (!Array.isArray(output2)) return [];
58311
+ return output2.map((block) => {
58312
+ const text = asRecord2(block)?.text;
58313
+ return typeof text === "string" ? text : "";
58314
+ });
58129
58315
  }
58130
58316
  function argvCommand(argv) {
58131
58317
  if (typeof argv === "string") return argv;
@@ -58134,22 +58320,56 @@ function argvCommand(argv) {
58134
58320
  if (argv.length === 3 && /(?:^|\/)(?:ba|z|da|k)?sh$/.test(shell) && /^-[a-z]*c[a-z]*$/.test(flag)) return script;
58135
58321
  return argv.map((word) => /^[A-Za-z0-9._/:=@+-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`).join(" ");
58136
58322
  }
58137
- var EXEC_COMMAND_CALL = /exec_command\(\{\s*"?cmd"?\s*:\s*("(?:[^"\\]|\\.)*")/g;
58138
- function execCommandStrings(script) {
58139
- const commands = [];
58140
- for (const match of script.matchAll(EXEC_COMMAND_CALL)) {
58141
- try {
58142
- const parsed = JSON.parse(match[1]);
58143
- if (typeof parsed === "string") commands.push(parsed);
58144
- } catch {
58145
- continue;
58146
- }
58323
+ var SCRIPT_TOOL_CALL = /tools\.(\w+)\(/g;
58324
+ var COMMAND_ARGUMENT = /\{\s*"?cmd"?\s*:\s*("(?:[^"\\]|\\.)*")/y;
58325
+ var SESSION_ARGUMENT = /\{\s*"?session_id"?\s*:\s*(\d+)/y;
58326
+ function execScriptSteps(script) {
58327
+ const steps = [];
58328
+ const call = new RegExp(SCRIPT_TOOL_CALL);
58329
+ for (let match = call.exec(script); match !== null; match = call.exec(script)) {
58330
+ const argument = match[1] === "exec_command" ? COMMAND_ARGUMENT : match[1] === "write_stdin" ? SESSION_ARGUMENT : null;
58331
+ if (argument !== null) argument.lastIndex = call.lastIndex;
58332
+ const value = argument?.exec(script) ?? null;
58333
+ if (value !== null) call.lastIndex += value[0].length;
58334
+ steps.push({
58335
+ command: value !== null && match[1] === "exec_command" ? jsonString(value[1]) : null,
58336
+ polls: value !== null && match[1] === "write_stdin" ? value[1] : null
58337
+ });
58338
+ }
58339
+ return steps;
58340
+ }
58341
+ var STRING_LITERAL = /"(?:[^"\\]|\\.)*"/g;
58342
+ var TEXT_CALL = /\btext\(/g;
58343
+ var TEXT_OF_TOOL_CALL = /\btext\(\s*await\s+tools\.\w+\(/g;
58344
+ var DEFERRED_CODE = /\bfunction\b|=>|\.then\(/;
58345
+ function printsEachStep(script, steps) {
58346
+ if (steps.length === 1) return true;
58347
+ const code = script.replace(STRING_LITERAL, '""');
58348
+ if (DEFERRED_CODE.test(code)) return false;
58349
+ const printed = [...code.matchAll(TEXT_OF_TOOL_CALL)].length;
58350
+ return printed === steps.length && [...code.matchAll(TEXT_CALL)].length === steps.length;
58351
+ }
58352
+ function jsonString(literal2) {
58353
+ try {
58354
+ const parsed = JSON.parse(literal2);
58355
+ return typeof parsed === "string" ? parsed : null;
58356
+ } catch {
58357
+ return null;
58147
58358
  }
58148
- return commands;
58149
58359
  }
58150
58360
  function isToolOutput(item) {
58151
58361
  return item.type === "custom_tool_call_output" || item.type === "function_call_output" || item.type === "local_shell_call_output";
58152
58362
  }
58363
+ var SCRIPT_STILL_RUNNING = /^Script running with cell ID (\d+)/;
58364
+ var RUNNING_CHUNK = /^\s*\{[^\n]*?"session_id"\s*:\s*(\d+)/;
58365
+ var RUNNING_PROCESS_LINE = /^Process running with session ID (\d+)$/m;
58366
+ function codexRunningProcess(output2) {
58367
+ const chunk = RUNNING_CHUNK.exec(output2);
58368
+ if (chunk !== null) return chunk[1];
58369
+ const outputLine = output2.search(/^Output:/m);
58370
+ const header = outputLine === -1 ? output2 : output2.slice(0, outputLine);
58371
+ return RUNNING_PROCESS_LINE.exec(header)?.[1] ?? null;
58372
+ }
58153
58373
  var EXIT_STATUS = /"exit_code"\s*:\s*(\d+)|Process exited with code (\d+)/g;
58154
58374
  function codexExitStatus(output2) {
58155
58375
  const codes = [...output2.matchAll(EXIT_STATUS)].map((match) => Number(match[1] ?? match[2]));
@@ -85730,7 +85950,7 @@ import { createHmac } from "node:crypto";
85730
85950
  // ../../apps/collector/src/http.ts
85731
85951
  var import_undici = __toESM(require_undici(), 1);
85732
85952
  var CLI_VERSION_HEADER = "x-powerplant-cli-version";
85733
- var cliVersion = true ? "0.4.11" : "0.0.0-checkout";
85953
+ var cliVersion = true ? "0.4.12" : "0.0.0-checkout";
85734
85954
  function cliVersionHeaders() {
85735
85955
  return { [CLI_VERSION_HEADER]: cliVersion, "user-agent": `powerplant-cli/${cliVersion}` };
85736
85956
  }
@@ -86575,9 +86795,9 @@ var MergedSpoolWriter = class {
86575
86795
  tmpPath;
86576
86796
  fd;
86577
86797
  bytes = 0;
86578
- append(pageText) {
86798
+ append(pageText2) {
86579
86799
  if (this.fd === void 0) throw new Error("merged transcript already closed");
86580
- const text = pageText.endsWith("\n") ? pageText : `${pageText}
86800
+ const text = pageText2.endsWith("\n") ? pageText2 : `${pageText2}
86581
86801
  `;
86582
86802
  this.bytes += writeSync(this.fd, text);
86583
86803
  }
@@ -91861,7 +92081,7 @@ function belowVersionFloor(client, versionText) {
91861
92081
  }
91862
92082
 
91863
92083
  // src/connect.ts
91864
- var MCP_SERVER_NAME = "powerplant";
92084
+ var mcpServerNameFor = (appEnv) => appEnv === "production" ? "powerplant" : `powerplant-${appEnv}`;
91865
92085
  var AGENT_DISPLAY_NAMES = {
91866
92086
  claude: "Claude Code",
91867
92087
  codex: "Codex",
@@ -92298,7 +92518,7 @@ function realCheckboxPrompt(question, choices, seams = {}) {
92298
92518
  function claudeAddArgs(deps) {
92299
92519
  const official = deps.officialClients?.["claude-code"];
92300
92520
  const credentialArgs = official ? ["--client-id", official.clientId, "--callback-port", String(official.callbackPort)] : [];
92301
- return ["mcp", "add", "--transport", "http", "--scope", "user", ...credentialArgs, MCP_SERVER_NAME, deps.mcpUrl];
92521
+ return ["mcp", "add", "--transport", "http", "--scope", "user", ...credentialArgs, deps.serverName, deps.mcpUrl];
92302
92522
  }
92303
92523
  function parseClaudeProbe(result) {
92304
92524
  const output2 = result.stdout + result.stderr;
@@ -92319,7 +92539,7 @@ function claudeEnv(client) {
92319
92539
  function connectClaude(client, name, deps) {
92320
92540
  const { bin } = client;
92321
92541
  const env = claudeEnv(client);
92322
- const probe = parseClaudeProbe(deps.run(bin, ["mcp", "get", MCP_SERVER_NAME], env));
92542
+ const probe = parseClaudeProbe(deps.run(bin, ["mcp", "get", deps.serverName], env));
92323
92543
  if (probe.broken) {
92324
92544
  return failed(`probing for the Powerplant MCP failed, ${probe.detail}`);
92325
92545
  }
@@ -92327,7 +92547,7 @@ function connectClaude(client, name, deps) {
92327
92547
  if (missing || registeredUrl !== void 0 && registeredUrl !== deps.mcpUrl) {
92328
92548
  if (!missing) {
92329
92549
  deps.log(`${name}: registered against ${registeredUrl}, re-registering for ${deps.mcpUrl}.`);
92330
- deps.run(bin, ["mcp", "remove", "--scope", "user", MCP_SERVER_NAME], env);
92550
+ deps.run(bin, ["mcp", "remove", "--scope", "user", deps.serverName], env);
92331
92551
  }
92332
92552
  const add = deps.run(bin, claudeAddArgs(deps), env);
92333
92553
  if (add.status !== 0) {
@@ -92339,7 +92559,7 @@ function connectClaude(client, name, deps) {
92339
92559
  return { state: "connected", unchanged: true };
92340
92560
  } else if (deps.officialClients?.["claude-code"]) {
92341
92561
  deps.log(`${name}: re-registering with the pre-registered client before sign-in.`);
92342
- deps.run(bin, ["mcp", "remove", "--scope", "user", MCP_SERVER_NAME], env);
92562
+ deps.run(bin, ["mcp", "remove", "--scope", "user", deps.serverName], env);
92343
92563
  const add = deps.run(bin, claudeAddArgs(deps), env);
92344
92564
  if (add.status !== 0) {
92345
92565
  return failed(`registering the Powerplant MCP failed, ${describeFailure(add)}`);
@@ -92347,7 +92567,7 @@ function connectClaude(client, name, deps) {
92347
92567
  } else {
92348
92568
  deps.log(`${name}: Powerplant MCP already registered.`);
92349
92569
  }
92350
- return login(bin, name, ["mcp", "login", MCP_SERVER_NAME], deps, env);
92570
+ return login(bin, name, ["mcp", "login", deps.serverName], deps, env);
92351
92571
  }
92352
92572
  function codexOauthHeaderPattern(serverName) {
92353
92573
  return new RegExp(
@@ -92514,7 +92734,7 @@ function parseCodexServers(stdout) {
92514
92734
  }
92515
92735
  }
92516
92736
  var CODEX_AUTHENTICATED_STATUSES = /* @__PURE__ */ new Set(["o_auth", "bearer_token"]);
92517
- function parseCodexProbe(result) {
92737
+ function parseCodexProbe(result, serverName) {
92518
92738
  if (result.status !== 0) {
92519
92739
  return {
92520
92740
  broken: true,
@@ -92532,7 +92752,7 @@ function parseCodexProbe(result) {
92532
92752
  authenticated: false
92533
92753
  };
92534
92754
  }
92535
- const existing = servers.find((server) => server.name === MCP_SERVER_NAME);
92755
+ const existing = servers.find((server) => server.name === serverName);
92536
92756
  if (!existing) return { broken: false, missing: true, authenticated: false };
92537
92757
  return {
92538
92758
  broken: false,
@@ -92544,7 +92764,7 @@ function parseCodexProbe(result) {
92544
92764
  function codexAddArgs(deps, options) {
92545
92765
  const official = deps.officialClients?.codex;
92546
92766
  const credentialArgs = options.withOfficialClientId && official ? ["--oauth-client-id", official.clientId] : [];
92547
- return ["mcp", "add", MCP_SERVER_NAME, ...credentialArgs, "--url", deps.mcpUrl];
92767
+ return ["mcp", "add", deps.serverName, ...credentialArgs, "--url", deps.mcpUrl];
92548
92768
  }
92549
92769
  function codexAddLogsIn(bin, deps) {
92550
92770
  const help = deps.run(bin, ["mcp", "add", "--help"]);
@@ -92559,9 +92779,9 @@ function codexServerEntryPattern(serverName) {
92559
92779
  function healOrphanCodexOauthTable(name, deps) {
92560
92780
  const existing = deps.readTextFile(deps.codexConfigPath);
92561
92781
  if (existing === null) return;
92562
- if (!codexOauthHeaderPattern(MCP_SERVER_NAME).test(existing)) return;
92563
- if (codexServerEntryPattern(MCP_SERVER_NAME).test(existing)) return;
92564
- const { content, changed } = unpatchCodexConfigToml(existing, MCP_SERVER_NAME);
92782
+ if (!codexOauthHeaderPattern(deps.serverName).test(existing)) return;
92783
+ if (codexServerEntryPattern(deps.serverName).test(existing)) return;
92784
+ const { content, changed } = unpatchCodexConfigToml(existing, deps.serverName);
92565
92785
  if (!changed) return;
92566
92786
  deps.writeTextFile(deps.codexConfigPath, content);
92567
92787
  deps.log(
@@ -92570,7 +92790,7 @@ function healOrphanCodexOauthTable(name, deps) {
92570
92790
  }
92571
92791
  function connectCodex(bin, name, deps) {
92572
92792
  healOrphanCodexOauthTable(name, deps);
92573
- const probe = parseCodexProbe(deps.run(bin, ["mcp", "list", "--json"]));
92793
+ const probe = parseCodexProbe(deps.run(bin, ["mcp", "list", "--json"]), deps.serverName);
92574
92794
  if (probe.broken) {
92575
92795
  return failed(probe.detail ?? "probing for the Powerplant MCP failed");
92576
92796
  }
@@ -92597,7 +92817,7 @@ function connectCodex(bin, name, deps) {
92597
92817
  }
92598
92818
  ensureCodexOfficialClient(name, deps);
92599
92819
  if (alreadyConnected) return { state: "connected", unchanged: true };
92600
- return login(bin, name, ["mcp", "login", MCP_SERVER_NAME], deps);
92820
+ return login(bin, name, ["mcp", "login", deps.serverName], deps);
92601
92821
  }
92602
92822
  function applyCodexConfigPatch(name, deps, patch, successLine) {
92603
92823
  if (patch.conflict !== void 0) {
@@ -92629,7 +92849,7 @@ function ensureCodexOfficialClient(name, deps) {
92629
92849
  deps,
92630
92850
  patchCodexConfigToml(
92631
92851
  deps.readTextFile(deps.codexConfigPath),
92632
- MCP_SERVER_NAME,
92852
+ deps.serverName,
92633
92853
  official.clientId,
92634
92854
  official.callbackPort
92635
92855
  ),
@@ -92640,7 +92860,7 @@ function connectCursor(mcpJsonPath, name, deps) {
92640
92860
  const official = deps.officialClients?.cursor;
92641
92861
  const patch = patchCursorMcpJson(
92642
92862
  deps.readTextFile(mcpJsonPath),
92643
- MCP_SERVER_NAME,
92863
+ deps.serverName,
92644
92864
  deps.mcpUrl,
92645
92865
  official?.clientId ?? null
92646
92866
  );
@@ -92651,12 +92871,12 @@ function connectCursor(mcpJsonPath, name, deps) {
92651
92871
  }
92652
92872
  if (patch.changed) deps.writeTextFile(mcpJsonPath, patch.content);
92653
92873
  const configured = patch.changed ? "configured" : "already configured";
92654
- if (deps.openUrl(cursorInstallDeeplink(MCP_SERVER_NAME, patch.entry))) {
92874
+ if (deps.openUrl(cursorInstallDeeplink(deps.serverName, patch.entry))) {
92655
92875
  deps.log(`\u2192 ${name} ${configured}, approve inside Cursor when it asks.`);
92656
92876
  return { state: "configured", unchanged: !patch.changed };
92657
92877
  }
92658
92878
  deps.log(
92659
- `\u2192 ${name} ${configured}, could not open Cursor; in Cursor's MCP settings, enable the powerplant server and sign in.`
92879
+ `\u2192 ${name} ${configured}, could not open Cursor; in Cursor's MCP settings, enable the ${deps.serverName} server and sign in.`
92660
92880
  );
92661
92881
  return { state: "pending", unchanged: false };
92662
92882
  }
@@ -92717,7 +92937,7 @@ function disconnectClaude(client, deps) {
92717
92937
  const { bin } = client;
92718
92938
  const name = describeClaudeTarget(client);
92719
92939
  const env = claudeEnv(client);
92720
- const get = deps.run(bin, ["mcp", "get", MCP_SERVER_NAME], env);
92940
+ const get = deps.run(bin, ["mcp", "get", deps.serverName], env);
92721
92941
  const output2 = get.stdout + get.stderr;
92722
92942
  if (/No MCP server named/i.test(output2)) {
92723
92943
  deps.log(`${name}: not connected.`);
@@ -92727,7 +92947,7 @@ function disconnectClaude(client, deps) {
92727
92947
  deps.log(`\u2717 ${name}: probing for the Powerplant MCP failed, ${describeFailure(get)}`);
92728
92948
  return "failed";
92729
92949
  }
92730
- const remove = deps.run(bin, ["mcp", "remove", "--scope", "user", MCP_SERVER_NAME], env);
92950
+ const remove = deps.run(bin, ["mcp", "remove", "--scope", "user", deps.serverName], env);
92731
92951
  if (remove.status !== 0) {
92732
92952
  deps.log(`\u2717 ${name}: removing the Powerplant MCP failed, ${describeFailure(remove)}`);
92733
92953
  return "failed";
@@ -92747,15 +92967,15 @@ function disconnectCodex(bin, deps) {
92747
92967
  deps.log(`\u2717 ${name}: could not read its MCP server list, is this codex version very old?`);
92748
92968
  return "failed";
92749
92969
  }
92750
- const registered = servers.some((server) => server.name === MCP_SERVER_NAME);
92751
- const patch = unpatchCodexConfigToml(deps.readTextFile(deps.codexConfigPath), MCP_SERVER_NAME);
92970
+ const registered = servers.some((server) => server.name === deps.serverName);
92971
+ const patch = unpatchCodexConfigToml(deps.readTextFile(deps.codexConfigPath), deps.serverName);
92752
92972
  if (patch.changed) deps.writeTextFile(deps.codexConfigPath, patch.content);
92753
92973
  if (!registered) {
92754
92974
  deps.log(`${name}: not connected.`);
92755
92975
  return "notConnected";
92756
92976
  }
92757
- deps.run(bin, ["mcp", "logout", MCP_SERVER_NAME]);
92758
- const remove = deps.run(bin, ["mcp", "remove", MCP_SERVER_NAME]);
92977
+ deps.run(bin, ["mcp", "logout", deps.serverName]);
92978
+ const remove = deps.run(bin, ["mcp", "remove", deps.serverName]);
92759
92979
  if (remove.status !== 0) {
92760
92980
  deps.log(`\u2717 ${name}: removing the Powerplant MCP failed, ${describeFailure(remove)}`);
92761
92981
  return "failed";
@@ -92765,9 +92985,9 @@ function disconnectCodex(bin, deps) {
92765
92985
  }
92766
92986
  function disconnectCursor(mcpJsonPath, deps) {
92767
92987
  const name = `Cursor (${mcpJsonPath.replace(os20.homedir(), "~")})`;
92768
- const patch = unpatchCursorMcpJson(deps.readTextFile(mcpJsonPath), MCP_SERVER_NAME);
92988
+ const patch = unpatchCursorMcpJson(deps.readTextFile(mcpJsonPath), deps.serverName);
92769
92989
  if (patch.error !== void 0) {
92770
- deps.log(`\u2717 ${name}: mcp.json is ${patch.error}, fix it by hand, then re-run powerplant disconnect.`);
92990
+ deps.log(`\u2717 ${name}: mcp.json is ${patch.error}, fix it by hand, then re-run ${deps.disconnectCommand}.`);
92771
92991
  return "failed";
92772
92992
  }
92773
92993
  if (!patch.changed) {
@@ -92781,15 +93001,15 @@ function disconnectCursor(mcpJsonPath, deps) {
92781
93001
  function anyClientStillConnected(clients, deps) {
92782
93002
  return clients.some((client) => {
92783
93003
  if (client.client === "claude") {
92784
- const get = deps.run(client.bin, ["mcp", "get", MCP_SERVER_NAME], claudeEnv(client));
93004
+ const get = deps.run(client.bin, ["mcp", "get", deps.serverName], claudeEnv(client));
92785
93005
  return get.status === 0 && !/No MCP server named/i.test(get.stdout + get.stderr);
92786
93006
  }
92787
93007
  if (client.client === "codex") {
92788
93008
  const list = deps.run(client.bin, ["mcp", "list", "--json"]);
92789
93009
  if (list.status !== 0) return false;
92790
- return parseCodexServers(list.stdout)?.some((server) => server.name === MCP_SERVER_NAME) ?? false;
93010
+ return parseCodexServers(list.stdout)?.some((server) => server.name === deps.serverName) ?? false;
92791
93011
  }
92792
- const patch = unpatchCursorMcpJson(deps.readTextFile(client.bin), MCP_SERVER_NAME);
93012
+ const patch = unpatchCursorMcpJson(deps.readTextFile(client.bin), deps.serverName);
92793
93013
  return patch.error === void 0 && patch.changed;
92794
93014
  });
92795
93015
  }
@@ -92974,10 +93194,10 @@ function probeClient(client, deps) {
92974
93194
  return report(
92975
93195
  kind,
92976
93196
  kind === "claude" ? parseClaudeProbe(
92977
- deps.run(bin, ["mcp", "get", MCP_SERVER_NAME], {
93197
+ deps.run(bin, ["mcp", "get", deps.serverName], {
92978
93198
  CLAUDE_CONFIG_DIR: client.configDir
92979
93199
  })
92980
- ) : parseCodexProbe(deps.run(bin, ["mcp", "list", "--json"])),
93200
+ ) : parseCodexProbe(deps.run(bin, ["mcp", "list", "--json"]), deps.serverName),
92981
93201
  deps.mcpUrl
92982
93202
  );
92983
93203
  }
@@ -93000,7 +93220,7 @@ function probeCursor(mcpJsonPath, deps) {
93000
93220
  detail: error63 instanceof Error ? error63.message : String(error63)
93001
93221
  };
93002
93222
  }
93003
- const registration = readCursorRegistration(raw, MCP_SERVER_NAME);
93223
+ const registration = readCursorRegistration(raw, deps.serverName);
93004
93224
  if (registration.error !== void 0) {
93005
93225
  return { client: "cursor", state: "check-failed", detail: `mcp.json is ${registration.error}` };
93006
93226
  }
@@ -93158,7 +93378,7 @@ import { accessSync, constants as constants2, readFileSync as readFileSync12 } f
93158
93378
  import path25 from "node:path";
93159
93379
 
93160
93380
  // src/version.ts
93161
- var CLI_VERSION = true ? "0.4.11" : "0.0.0-checkout";
93381
+ var CLI_VERSION = true ? "0.4.12" : "0.0.0-checkout";
93162
93382
  var CLI_RELEASE_BUILD = true ? true : false;
93163
93383
  var CLI_PACKAGE_NAME = "@usepowerplant/cli";
93164
93384
 
@@ -95569,6 +95789,7 @@ ${USAGE}`);
95569
95789
  log: log3
95570
95790
  });
95571
95791
  const connectCommand = withEnv("powerplant connect");
95792
+ const disconnectCommand = withEnv("powerplant disconnect");
95572
95793
  const clientFilter = parseClientFilter(values.client);
95573
95794
  const pickerOn = pickerAllowed({
95574
95795
  yes: values.yes,
@@ -95618,6 +95839,7 @@ ${USAGE}`);
95618
95839
  }
95619
95840
  const outcome = runConnect(clients, {
95620
95841
  mcpUrl: `${config2.apiUrl}/mcp`,
95842
+ serverName: mcpServerNameFor(config2.appEnv),
95621
95843
  connectCommand,
95622
95844
  officialClients: await fetchOfficialClients(config2.apiUrl, log3),
95623
95845
  log: log3,
@@ -95705,6 +95927,8 @@ ${USAGE}`);
95705
95927
  }
95706
95928
  const outcome = runDisconnect(clients, {
95707
95929
  log: console.log,
95930
+ serverName: mcpServerNameFor(config2.appEnv),
95931
+ disconnectCommand,
95708
95932
  run: realRun,
95709
95933
  readTextFile: realReadTextFileStrict,
95710
95934
  writeTextFile: realWriteTextFile,
@@ -95837,6 +96061,7 @@ The access you granted these agents stays on your Powerplant account until you r
95837
96061
  oldestSupportedVersion,
95838
96062
  connections: probeConnections(clients, {
95839
96063
  mcpUrl: `${config2.apiUrl}/mcp`,
96064
+ serverName: mcpServerNameFor(config2.appEnv),
95840
96065
  run: runWithPath(probePath, budget),
95841
96066
  readTextFile: realReadTextFileStrict,
95842
96067
  grantedClientIds
@@ -95984,9 +96209,13 @@ The access you granted these agents stays on your Powerplant account until you r
95984
96209
  "Credentials kept, `powerplant init` re-installs without re-enrolling. (--forget deletes them.)"
95985
96210
  );
95986
96211
  }
95987
- if (anyClientStillConnected(detectClients(), { run: realRun, readTextFile: realReadTextFile })) {
96212
+ if (anyClientStillConnected(detectClients(), {
96213
+ run: realRun,
96214
+ readTextFile: realReadTextFile,
96215
+ serverName: mcpServerNameFor(config2.appEnv)
96216
+ })) {
95988
96217
  console.log(
95989
- "Your coding agents are still connected to the Powerplant MCP server, remove those connections with: powerplant disconnect"
96218
+ `Your coding agents are still connected to the Powerplant MCP server, remove those connections with: ${disconnectCommand}`
95990
96219
  );
95991
96220
  }
95992
96221
  return;
@@ -96168,3 +96397,5 @@ undici/lib/web/fetch/body.js:
96168
96397
  undici/lib/web/websocket/frame.js:
96169
96398
  (*! ws. MIT License. Einar Otto Stangvik <einaros@gmail.com> *)
96170
96399
  */
96400
+
96401
+ //# debugId=7d73a97a-938c-5f0f-8b6b-c4076b82abad
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usepowerplant/cli",
3
- "version": "0.4.11",
3
+ "version": "0.4.12",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -15,7 +15,8 @@
15
15
  "powerplant": "dist/cli.js"
16
16
  },
17
17
  "files": [
18
- "dist"
18
+ "dist",
19
+ "!dist/**/*.map"
19
20
  ],
20
21
  "engines": {
21
22
  "node": ">=20"