adhdev 0.8.15 → 0.8.16

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/dist/cli/index.js CHANGED
@@ -6721,6 +6721,40 @@ function normalizeScreenSnapshot(text) {
6721
6721
  function normalizeComparableMessageContent(text) {
6722
6722
  return String(text || "").replace(/\s+/g, " ").trim();
6723
6723
  }
6724
+ function trimPromptEchoPrefix(text, promptText) {
6725
+ const prompt = normalizeComparableMessageContent(String(promptText || ""));
6726
+ if (!prompt) return String(text || "");
6727
+ const lines = String(text || "").split(/\r\n|\n|\r/g);
6728
+ let dropCount = 0;
6729
+ for (let index = 0; index < Math.min(lines.length, 6); index += 1) {
6730
+ const fragment = normalizeComparableMessageContent(lines[index].replace(/^[.…]+\s*/, ""));
6731
+ if (!fragment) {
6732
+ if (dropCount === index) dropCount = index + 1;
6733
+ continue;
6734
+ }
6735
+ const fragmentWordCount = fragment ? fragment.split(/\s+/).filter(Boolean).length : 0;
6736
+ const canBePromptEcho = fragment.length >= 16 || fragmentWordCount >= 4;
6737
+ if (canBePromptEcho && prompt.includes(fragment)) {
6738
+ dropCount = index + 1;
6739
+ continue;
6740
+ }
6741
+ break;
6742
+ }
6743
+ return lines.slice(dropCount).join("\n").trim();
6744
+ }
6745
+ function getLastUserPromptText(messages) {
6746
+ const items = Array.isArray(messages) ? messages : [];
6747
+ for (let index = items.length - 1; index >= 0; index -= 1) {
6748
+ const message = items[index];
6749
+ if (message?.role === "user" && typeof message.content === "string" && message.content.trim()) {
6750
+ return message.content;
6751
+ }
6752
+ }
6753
+ return "";
6754
+ }
6755
+ function looksLikeConfirmOnlyLabel(label) {
6756
+ return /^(?:continue|confirm|ok|yes|trust|proceed|enter)$/i.test(String(label || "").trim());
6757
+ }
6724
6758
  function parsePatternEntry(x) {
6725
6759
  if (x instanceof RegExp) return x;
6726
6760
  if (x && typeof x === "object" && typeof x.source === "string") {
@@ -6857,6 +6891,8 @@ var init_provider_cli_adapter = __esm({
6857
6891
  submitRetryUsed = false;
6858
6892
  submitRetryPromptSnippet = "";
6859
6893
  idleFinishCandidate = null;
6894
+ finishRetryTimer = null;
6895
+ finishRetryCount = 0;
6860
6896
  // Resize redraw suppression
6861
6897
  resizeSuppressUntil = 0;
6862
6898
  // Debug: status transition history
@@ -6878,6 +6914,8 @@ var init_provider_cli_adapter = __esm({
6878
6914
  static MAX_TRACE_ENTRIES = 250;
6879
6915
  providerResolutionMeta;
6880
6916
  static IDLE_FINISH_CONFIRM_MS = 900;
6917
+ static FINISH_RETRY_DELAY_MS = 300;
6918
+ static MAX_FINISH_RETRIES = 2;
6881
6919
  syncMessageViews() {
6882
6920
  this.messages = [...this.committedMessages];
6883
6921
  this.structuredMessages = [...this.committedMessages];
@@ -6937,7 +6975,8 @@ var init_provider_cli_adapter = __esm({
6937
6975
  recentBuffer: buffer.slice(-1e3) || this.recentOutputBuffer,
6938
6976
  screenText: this.terminalScreen.getText(),
6939
6977
  messages: [...baseMessages],
6940
- partialResponse
6978
+ partialResponse,
6979
+ promptText: scope?.prompt || ""
6941
6980
  };
6942
6981
  }
6943
6982
  setStatus(status, trigger) {
@@ -7180,6 +7219,11 @@ var init_provider_cli_adapter = __esm({
7180
7219
  this.terminalScreen.reset(24, 80);
7181
7220
  this.pendingTerminalQueryTail = "";
7182
7221
  this.currentTurnScope = null;
7222
+ this.finishRetryCount = 0;
7223
+ if (this.finishRetryTimer) {
7224
+ clearTimeout(this.finishRetryTimer);
7225
+ this.finishRetryTimer = null;
7226
+ }
7183
7227
  this.ready = false;
7184
7228
  await this.ptyProcess.ready;
7185
7229
  this.recordTrace("ready", {
@@ -7226,11 +7270,12 @@ var init_provider_cli_adapter = __esm({
7226
7270
  if (this.startupParseGate) {
7227
7271
  this.startupBuffer += cleanData;
7228
7272
  const elapsed = Date.now() - this.spawnAt;
7229
- const scriptStatus = this.runDetectStatus(this.startupBuffer);
7230
7273
  const screenText = this.terminalScreen.getText() || "";
7274
+ const startupModal = this.getStartupConfirmationModal(screenText);
7275
+ const scriptStatus = startupModal ? "waiting_approval" : this.runDetectStatus(this.startupBuffer);
7231
7276
  const hasInteractivePrompt = this.looksLikeVisibleIdlePrompt(screenText);
7232
7277
  const startupStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
7233
- const isReady = (scriptStatus === "idle" || scriptStatus === "waiting_approval") && hasInteractivePrompt && startupStableMs >= 700 || elapsed > 8e3 || this.startupBuffer.length > 12e3;
7278
+ const isReady = (scriptStatus === "idle" || scriptStatus === "waiting_approval") && hasInteractivePrompt && startupStableMs >= 700 || !!startupModal && startupStableMs >= 700 || elapsed > 8e3 || this.startupBuffer.length > 12e3;
7234
7279
  if (isReady) {
7235
7280
  this.startupParseGate = false;
7236
7281
  this.ready = true;
@@ -7262,7 +7307,9 @@ var init_provider_cli_adapter = __esm({
7262
7307
  this.approvalExitTimeout = setTimeout(() => {
7263
7308
  if (this.currentStatus !== "waiting_approval") return;
7264
7309
  const tail = this.recentOutputBuffer;
7265
- const modal = this.runParseApproval(tail);
7310
+ const screenText = this.terminalScreen.getText() || "";
7311
+ const startupModal = this.getStartupConfirmationModal(screenText);
7312
+ const modal = this.runParseApproval(tail) || startupModal;
7266
7313
  const stillWaiting = this.runDetectStatus(tail) === "waiting_approval" || !!modal;
7267
7314
  if (stillWaiting) {
7268
7315
  this.activeModal = modal || this.activeModal || { message: "Approval required", buttons: ["Allow", "Deny"] };
@@ -7282,6 +7329,63 @@ var init_provider_cli_adapter = __esm({
7282
7329
  if (!text.trim()) return false;
7283
7330
  return /(^|\n)\s*[❯›>]\s*(?:\n|$)/m.test(text) || /⏎\s+send/i.test(text) || /\?\s*for\s*shortcuts/i.test(text) || /Type your message(?:\s+or\s+@path\/to\/file)?/i.test(text) || /workspace\s*\(\/directory\)/i.test(text) || /for\s*shortcuts/i.test(text);
7284
7331
  }
7332
+ looksLikeVisibleAssistantCandidate(screenText) {
7333
+ const lines = sanitizeTerminalText(String(screenText || "")).split(/\r\n|\n|\r/g);
7334
+ for (const line of lines) {
7335
+ const trimmed = String(line || "").trim();
7336
+ if (!trimmed) continue;
7337
+ if (/^➜\s+\S+/.test(trimmed)) continue;
7338
+ if (/^Update available!/i.test(trimmed)) continue;
7339
+ if (/Claude Code v\d/i.test(trimmed)) continue;
7340
+ if (/^⏵⏵\s+accept edits on/i.test(trimmed)) continue;
7341
+ if (/^[◐◑◒◓◴◵◶◷◸◹◺◿].*\/effort/i.test(trimmed)) continue;
7342
+ if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+$/.test(trimmed)) continue;
7343
+ if (/esc to (cancel|interrupt|stop)/i.test(trimmed)) continue;
7344
+ const assistantMatch = trimmed.match(/^⏺\s+(.+)$/);
7345
+ if (!assistantMatch) continue;
7346
+ const content = assistantMatch[1].trim();
7347
+ if (!content) continue;
7348
+ if (/^(?:Bash|Read|Write|Edit|MultiEdit|Task|Glob|Grep|LS|NotebookEdit)\(/.test(content)) continue;
7349
+ if (/This command requires approval|Do you want to proceed|Allow once|Always allow/i.test(content)) continue;
7350
+ return true;
7351
+ }
7352
+ return false;
7353
+ }
7354
+ shouldRetryFinishResponse(commitResult) {
7355
+ if (!this.currentTurnScope) return false;
7356
+ if (this.currentStatus === "waiting_approval" || this.activeModal) return false;
7357
+ if (this.finishRetryCount >= _ProviderCliAdapter.MAX_FINISH_RETRIES) return false;
7358
+ if (commitResult.hasAssistant && commitResult.assistantContent.trim()) return false;
7359
+ const screenText = this.terminalScreen.getText() || "";
7360
+ if (!this.looksLikeVisibleAssistantCandidate(screenText)) return false;
7361
+ const now = Date.now();
7362
+ const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
7363
+ const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
7364
+ return quietForMs < 1200 || screenStableMs < 1200 || !commitResult.hasAssistant;
7365
+ }
7366
+ getStartupConfirmationModal(screenText) {
7367
+ const text = sanitizeTerminalText(String(screenText || ""));
7368
+ if (!text.trim()) return null;
7369
+ if (this.cliType === "claude-cli") {
7370
+ const hasTrustPrompt = /Quick safety check/i.test(text) || /Is this a project you trust/i.test(text) || /Do you trust (?:this project|the contents of this directory|the files in this folder)/i.test(text);
7371
+ const hasConfirmFooter = /Press Enter to (?:continue|confirm)/i.test(text) || /Enter to confirm/i.test(text) || /Esc to (?:cancel|exit)/i.test(text);
7372
+ if (hasTrustPrompt || hasConfirmFooter && /trust/i.test(text)) {
7373
+ return {
7374
+ message: "Confirm Claude Code project trust",
7375
+ buttons: ["Continue"]
7376
+ };
7377
+ }
7378
+ }
7379
+ return null;
7380
+ }
7381
+ shouldResolveModalWithEnter(modal, buttonIndex) {
7382
+ if (!modal || buttonIndex !== 0) return false;
7383
+ const buttons = Array.isArray(modal.buttons) ? modal.buttons : [];
7384
+ if (buttons.length !== 1) return false;
7385
+ const buttonLabel = String(buttons[0] || "").trim();
7386
+ const modalText = `${modal.message || ""} ${buttonLabel}`.trim();
7387
+ return looksLikeConfirmOnlyLabel(buttonLabel) || /Quick safety check|project trust|trust (?:this project|the contents of this directory|the files in this folder)|Enter to confirm/i.test(modalText);
7388
+ }
7285
7389
  async waitForInteractivePrompt(maxWaitMs = 5e3) {
7286
7390
  const startedAt = Date.now();
7287
7391
  let loggedWait = false;
@@ -7331,9 +7435,10 @@ var init_provider_cli_adapter = __esm({
7331
7435
  }
7332
7436
  const tail = this.settledBuffer;
7333
7437
  const screenText = this.terminalScreen.getText() || "";
7334
- const modal = this.runParseApproval(tail);
7438
+ const startupModal = this.getStartupConfirmationModal(screenText);
7439
+ const modal = this.runParseApproval(tail) || startupModal;
7335
7440
  const rawScriptStatus = this.runDetectStatus(tail);
7336
- const scriptStatus = rawScriptStatus;
7441
+ const scriptStatus = startupModal ? "waiting_approval" : rawScriptStatus;
7337
7442
  const parsedTranscript = this.parseCurrentTranscript(
7338
7443
  this.committedMessages,
7339
7444
  this.responseBuffer,
@@ -7528,7 +7633,24 @@ var init_provider_cli_adapter = __esm({
7528
7633
  this.recordTrace("finish_response", {
7529
7634
  ...this.buildTraceParseSnapshot(this.currentTurnScope, this.responseBuffer)
7530
7635
  });
7531
- this.commitCurrentTranscript();
7636
+ const commitResult = this.commitCurrentTranscript();
7637
+ if (this.shouldRetryFinishResponse(commitResult)) {
7638
+ this.finishRetryCount += 1;
7639
+ this.recordTrace("finish_response_retry", {
7640
+ retryCount: this.finishRetryCount,
7641
+ retryDelayMs: _ProviderCliAdapter.FINISH_RETRY_DELAY_MS,
7642
+ assistantContent: this.summarizeTraceText(commitResult.assistantContent, 220),
7643
+ ...this.buildTraceParseSnapshot(this.currentTurnScope, this.responseBuffer)
7644
+ });
7645
+ if (this.finishRetryTimer) clearTimeout(this.finishRetryTimer);
7646
+ this.finishRetryTimer = setTimeout(() => {
7647
+ this.finishRetryTimer = null;
7648
+ if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
7649
+ this.finishResponse();
7650
+ }
7651
+ }, _ProviderCliAdapter.FINISH_RETRY_DELAY_MS);
7652
+ return;
7653
+ }
7532
7654
  if (this.responseTimeout) {
7533
7655
  clearTimeout(this.responseTimeout);
7534
7656
  this.responseTimeout = null;
@@ -7545,11 +7667,16 @@ var init_provider_cli_adapter = __esm({
7545
7667
  clearTimeout(this.submitRetryTimer);
7546
7668
  this.submitRetryTimer = null;
7547
7669
  }
7670
+ if (this.finishRetryTimer) {
7671
+ clearTimeout(this.finishRetryTimer);
7672
+ this.finishRetryTimer = null;
7673
+ }
7548
7674
  this.responseBuffer = "";
7549
7675
  this.isWaitingForResponse = false;
7550
7676
  this.responseSettleIgnoreUntil = 0;
7551
7677
  this.submitRetryUsed = false;
7552
7678
  this.submitRetryPromptSnippet = "";
7679
+ this.finishRetryCount = 0;
7553
7680
  this.currentTurnScope = null;
7554
7681
  this.activeModal = null;
7555
7682
  this.setStatus("idle", "response_finished");
@@ -7563,6 +7690,13 @@ var init_provider_cli_adapter = __esm({
7563
7690
  );
7564
7691
  if (parsed && Array.isArray(parsed.messages)) {
7565
7692
  this.committedMessages = this.normalizeParsedMessages(parsed.messages);
7693
+ const promptForTrim = this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages);
7694
+ if (promptForTrim) {
7695
+ const lastAssistantForTrim = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
7696
+ if (lastAssistantForTrim) {
7697
+ lastAssistantForTrim.content = trimPromptEchoPrefix(lastAssistantForTrim.content, promptForTrim);
7698
+ }
7699
+ }
7566
7700
  this.syncMessageViews();
7567
7701
  const lastAssistant = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
7568
7702
  this.recordTrace("commit_transcript", {
@@ -7578,7 +7712,15 @@ var init_provider_cli_adapter = __esm({
7578
7712
  `[${this.cliType}] Commit without assistant turn: prompt=${JSON.stringify(this.currentTurnScope.prompt).slice(0, 140)} responseBuffer=${JSON.stringify(this.summarizeTraceText(this.responseBuffer, 220)).slice(0, 260)} providerDir=${this.providerResolutionMeta.providerDir || "-"} scriptDir=${this.providerResolutionMeta.scriptDir || "-"} scriptsPath=${this.providerResolutionMeta.scriptsPath || "-"}`
7579
7713
  );
7580
7714
  }
7715
+ return {
7716
+ hasAssistant: !!lastAssistant,
7717
+ assistantContent: lastAssistant?.content || ""
7718
+ };
7581
7719
  }
7720
+ return {
7721
+ hasAssistant: false,
7722
+ assistantContent: ""
7723
+ };
7582
7724
  }
7583
7725
  // ─── Script Execution ──────────────────────────
7584
7726
  runDetectStatus(text) {
@@ -7657,7 +7799,15 @@ var init_provider_cli_adapter = __esm({
7657
7799
  if (!this.cliScripts?.parseOutput) return null;
7658
7800
  try {
7659
7801
  const input = this.buildParseInput(baseMessages, partialResponse, scope);
7660
- return this.cliScripts.parseOutput(input);
7802
+ const parsed = this.cliScripts.parseOutput(input);
7803
+ const promptForTrim = scope?.prompt || getLastUserPromptText(baseMessages);
7804
+ if (parsed && Array.isArray(parsed.messages) && promptForTrim) {
7805
+ const lastAssistant = [...parsed.messages].reverse().find((message) => message?.role === "assistant" && typeof message.content === "string");
7806
+ if (lastAssistant) {
7807
+ lastAssistant.content = trimPromptEchoPrefix(lastAssistant.content, promptForTrim);
7808
+ }
7809
+ }
7810
+ return parsed;
7661
7811
  } catch (e) {
7662
7812
  LOG.warn("CLI", `[${this.cliType}] parseOutput error: ${e.message}`);
7663
7813
  return null;
@@ -7702,10 +7852,19 @@ ${data.message || ""}`.trim();
7702
7852
  if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
7703
7853
  if (this.isWaitingForResponse) return;
7704
7854
  await this.waitForInteractivePrompt();
7855
+ const blockingModal = this.activeModal || this.getStartupConfirmationModal(this.terminalScreen.getText() || "");
7856
+ if (blockingModal || this.currentStatus === "waiting_approval") {
7857
+ throw new Error(`${this.cliName} is awaiting confirmation before it can accept a prompt`);
7858
+ }
7705
7859
  this.committedMessages.push({ role: "user", content: text, timestamp: Date.now() });
7706
7860
  this.syncMessageViews();
7707
7861
  this.isWaitingForResponse = true;
7708
7862
  this.responseBuffer = "";
7863
+ this.finishRetryCount = 0;
7864
+ if (this.finishRetryTimer) {
7865
+ clearTimeout(this.finishRetryTimer);
7866
+ this.finishRetryTimer = null;
7867
+ }
7709
7868
  this.clearIdleFinishCandidate("send_message");
7710
7869
  this.currentTurnScope = {
7711
7870
  prompt: text,
@@ -7933,6 +8092,10 @@ ${data.message || ""}`.trim();
7933
8092
  clearTimeout(this.submitRetryTimer);
7934
8093
  this.submitRetryTimer = null;
7935
8094
  }
8095
+ if (this.finishRetryTimer) {
8096
+ clearTimeout(this.finishRetryTimer);
8097
+ this.finishRetryTimer = null;
8098
+ }
7936
8099
  if (this.responseTimeout) {
7937
8100
  clearTimeout(this.responseTimeout);
7938
8101
  this.responseTimeout = null;
@@ -7956,6 +8119,7 @@ ${data.message || ""}`.trim();
7956
8119
  this.ptyOutputFlushTimer = null;
7957
8120
  }
7958
8121
  this.ptyOutputBuffer = "";
8122
+ this.finishRetryCount = 0;
7959
8123
  if (this.ptyProcess) {
7960
8124
  this.ptyProcess.write("");
7961
8125
  setTimeout(() => {
@@ -7986,6 +8150,10 @@ ${data.message || ""}`.trim();
7986
8150
  clearTimeout(this.submitRetryTimer);
7987
8151
  this.submitRetryTimer = null;
7988
8152
  }
8153
+ if (this.finishRetryTimer) {
8154
+ clearTimeout(this.finishRetryTimer);
8155
+ this.finishRetryTimer = null;
8156
+ }
7989
8157
  if (this.responseTimeout) {
7990
8158
  clearTimeout(this.responseTimeout);
7991
8159
  this.responseTimeout = null;
@@ -8009,6 +8177,7 @@ ${data.message || ""}`.trim();
8009
8177
  this.ptyOutputFlushTimer = null;
8010
8178
  }
8011
8179
  this.ptyOutputBuffer = "";
8180
+ this.finishRetryCount = 0;
8012
8181
  if (this.ptyProcess) {
8013
8182
  try {
8014
8183
  if (typeof this.ptyProcess.detach === "function") {
@@ -8045,6 +8214,11 @@ ${data.message || ""}`.trim();
8045
8214
  this.ptyOutputFlushTimer = null;
8046
8215
  }
8047
8216
  this.ptyOutputBuffer = "";
8217
+ if (this.finishRetryTimer) {
8218
+ clearTimeout(this.finishRetryTimer);
8219
+ this.finishRetryTimer = null;
8220
+ }
8221
+ this.finishRetryCount = 0;
8048
8222
  this.terminalScreen.reset();
8049
8223
  this.ptyProcess?.clearBuffer?.();
8050
8224
  this.onStatusChange?.();
@@ -8064,10 +8238,11 @@ ${data.message || ""}`.trim();
8064
8238
  }
8065
8239
  resolveModal(buttonIndex) {
8066
8240
  if (!this.ptyProcess || this.currentStatus !== "waiting_approval" && !this.activeModal) return;
8241
+ const modal = this.activeModal;
8067
8242
  this.clearIdleFinishCandidate("resolve_modal");
8068
8243
  this.recordTrace("resolve_modal", {
8069
8244
  buttonIndex,
8070
- activeModal: this.activeModal
8245
+ activeModal: modal
8071
8246
  });
8072
8247
  this.activeModal = null;
8073
8248
  this.lastApprovalResolvedAt = Date.now();
@@ -8078,7 +8253,9 @@ ${data.message || ""}`.trim();
8078
8253
  }
8079
8254
  this.setStatus("generating", "approval_resolved");
8080
8255
  this.onStatusChange?.();
8081
- if (buttonIndex in this.approvalKeys) {
8256
+ if (this.shouldResolveModalWithEnter(modal, buttonIndex)) {
8257
+ this.ptyProcess.write("\r");
8258
+ } else if (buttonIndex in this.approvalKeys) {
8082
8259
  this.ptyProcess.write(this.approvalKeys[buttonIndex]);
8083
8260
  } else {
8084
8261
  const DOWN = "\x1B[B";
@@ -30038,7 +30215,10 @@ function appendUpgradeLog(message) {
30038
30215
  }
30039
30216
  }
30040
30217
  function getNpmExecutable() {
30041
- return process.platform === "win32" ? "npm.cmd" : "npm";
30218
+ return "npm";
30219
+ }
30220
+ function getNpmExecOptions() {
30221
+ return { shell: process.platform === "win32" };
30042
30222
  }
30043
30223
  function killPid(pid) {
30044
30224
  try {
@@ -30100,9 +30280,10 @@ function removeDaemonPidFile() {
30100
30280
  }
30101
30281
  }
30102
30282
  function cleanupStaleGlobalInstallDirs(pkgName) {
30103
- const npmRoot = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["root", "-g"], { encoding: "utf8" }).trim();
30283
+ const npmExecOpts = getNpmExecOptions();
30284
+ const npmRoot = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["root", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
30104
30285
  if (!npmRoot) return;
30105
- const npmPrefix = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["prefix", "-g"], { encoding: "utf8" }).trim();
30286
+ const npmPrefix = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["prefix", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
30106
30287
  const binDir = process.platform === "win32" ? npmPrefix : path13.join(npmPrefix, "bin");
30107
30288
  const packageBaseName = pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName;
30108
30289
  const binNames = /* @__PURE__ */ new Set([packageBaseName]);
@@ -30163,7 +30344,8 @@ async function runDaemonUpgradeHelper(payload) {
30163
30344
  {
30164
30345
  encoding: "utf8",
30165
30346
  stdio: "pipe",
30166
- maxBuffer: 20 * 1024 * 1024
30347
+ maxBuffer: 20 * 1024 * 1024,
30348
+ ...getNpmExecOptions()
30167
30349
  }
30168
30350
  );
30169
30351
  if (installOutput.trim()) {
@@ -33486,6 +33668,53 @@ async function runCliExerciseInternal(ctx, body) {
33486
33668
  let lastModalKey = "";
33487
33669
  let idleSince = 0;
33488
33670
  let sawBusy = false;
33671
+ const noteStatus = (status) => {
33672
+ if (status !== lastStatus) {
33673
+ statusesSeen.push(status);
33674
+ lastStatus = status;
33675
+ }
33676
+ };
33677
+ const resolveActiveModalIfNeeded = (status, modal) => {
33678
+ if (!autoResolveApprovals || status !== "waiting_approval" || !modal || !Array.isArray(modal.buttons) || modal.buttons.length === 0) {
33679
+ return false;
33680
+ }
33681
+ const clampedIndex = Math.max(0, Math.min(Number(approvalButtonIndex) || 0, modal.buttons.length - 1));
33682
+ const modalKey = JSON.stringify({
33683
+ message: modal.message || "",
33684
+ buttons: modal.buttons,
33685
+ index: clampedIndex
33686
+ });
33687
+ if (modalKey === lastModalKey || typeof bundle?.adapter?.resolveModal !== "function") {
33688
+ return false;
33689
+ }
33690
+ lastModalKey = modalKey;
33691
+ approvalsResolved.push({
33692
+ at: Date.now(),
33693
+ buttonIndex: clampedIndex,
33694
+ label: modal.buttons[clampedIndex] || null
33695
+ });
33696
+ bundle.adapter.resolveModal(clampedIndex);
33697
+ return true;
33698
+ };
33699
+ const preflightStartedAt = Date.now();
33700
+ while (Date.now() - preflightStartedAt < Math.max(1e3, readyTimeoutMs)) {
33701
+ bundle = getCliTargetBundle(ctx, type, bundle.target.instanceId);
33702
+ if (!bundle) {
33703
+ throw new Error("CLI instance disappeared before exercise send");
33704
+ }
33705
+ const debug = typeof bundle.adapter.getDebugState === "function" ? bundle.adapter.getDebugState() : null;
33706
+ const trace = typeof bundle.adapter.getTraceState === "function" ? bundle.adapter.getTraceState(traceLimit) : null;
33707
+ const status = String(debug?.status || bundle.target.status || "unknown");
33708
+ const modal = debug?.activeModal || trace?.activeModal || null;
33709
+ noteStatus(status);
33710
+ if (resolveActiveModalIfNeeded(status, modal)) {
33711
+ await sleep(150);
33712
+ continue;
33713
+ }
33714
+ const startupParseGate = !!debug?.startupParseGate;
33715
+ if (status === "idle" && !startupParseGate) break;
33716
+ await sleep(150);
33717
+ }
33489
33718
  ctx.instanceManager.sendEvent(bundle.target.instanceId, "send_message", { text });
33490
33719
  while (Date.now() - startAt < Math.max(1e3, timeoutMs)) {
33491
33720
  await sleep(150);
@@ -33500,32 +33729,14 @@ async function runCliExerciseInternal(ctx, body) {
33500
33729
  const sawSendMessage = traceEntries.some((entry) => entry?.type === "send_message");
33501
33730
  const sawSubmitWrite = traceEntries.some((entry) => entry?.type === "submit_write");
33502
33731
  const hasTurnStarted = sawSendMessage || sawSubmitWrite || !!debug?.currentTurnScope;
33503
- if (status !== lastStatus) {
33504
- statusesSeen.push(status);
33505
- lastStatus = status;
33506
- }
33732
+ noteStatus(status);
33507
33733
  if (status === "generating" || status === "waiting_approval") {
33508
33734
  sawBusy = true;
33509
33735
  idleSince = 0;
33510
33736
  }
33511
33737
  const modal = debug?.activeModal || trace?.activeModal || null;
33512
- if (autoResolveApprovals && status === "waiting_approval" && modal && Array.isArray(modal.buttons) && modal.buttons.length > 0) {
33513
- const clampedIndex = Math.max(0, Math.min(Number(approvalButtonIndex) || 0, modal.buttons.length - 1));
33514
- const modalKey = JSON.stringify({
33515
- message: modal.message || "",
33516
- buttons: modal.buttons,
33517
- index: clampedIndex
33518
- });
33519
- if (modalKey !== lastModalKey && typeof bundle.adapter.resolveModal === "function") {
33520
- lastModalKey = modalKey;
33521
- approvalsResolved.push({
33522
- at: Date.now(),
33523
- buttonIndex: clampedIndex,
33524
- label: modal.buttons[clampedIndex] || null
33525
- });
33526
- bundle.adapter.resolveModal(clampedIndex);
33527
- continue;
33528
- }
33738
+ if (resolveActiveModalIfNeeded(status, modal)) {
33739
+ continue;
33529
33740
  }
33530
33741
  const traceCount = Number(trace?.entryCount || 0);
33531
33742
  const hasProgress = hasTurnStarted && (traceCount > preTraceCount || statusesSeen.length > 1 || approvalsResolved.length > 0);
@@ -35185,6 +35396,13 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
35185
35396
  lines.push("19. Literal string checks are allowed only for stable proper nouns or exact product chrome that cannot be expressed safely as a broader pattern. Everything else should generalize.");
35186
35397
  lines.push("20. When a bug comes from noisy PTY text, first normalize and classify the line family; do NOT just append another special-case substring to the parser.");
35187
35398
  lines.push("");
35399
+ if (verification?.focusAreas?.length) {
35400
+ lines.push("## Provider-Specific Focus Areas");
35401
+ for (const area of verification.focusAreas) {
35402
+ lines.push(`- ${area}`);
35403
+ }
35404
+ lines.push("");
35405
+ }
35188
35406
  lines.push("## Task");
35189
35407
  lines.push(`Edit files in \`${providerDir}\` to implement: **${functions.join(", ")}**`);
35190
35408
  lines.push("");
@@ -46292,7 +46510,7 @@ var init_adhdev_daemon = __esm({
46292
46510
  import_ws3 = require("ws");
46293
46511
  import_chalk2 = __toESM(require("chalk"));
46294
46512
  init_version();
46295
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.15" });
46513
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.16" });
46296
46514
  DANGEROUS_PATTERNS = [
46297
46515
  /\brm\s+(-[a-z]*f|-[a-z]*r|--force|--recursive)/i,
46298
46516
  /\bsudo\b/i,
@@ -48267,29 +48485,21 @@ function getDefaultAutoFixReference(category, type, providers) {
48267
48485
  function escapeRegex2(value) {
48268
48486
  return String(value || "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
48269
48487
  }
48270
- function getCliAutoFixVerification(type, providerDir) {
48271
- if (type !== "codex-cli") return null;
48272
- const normalizedDir = providerDir.replace(/\\/g, "/");
48273
- const escapedDir = escapeRegex2(normalizedDir);
48274
- return {
48488
+ var CLI_PROVIDER_FIX_PROMPT = 'Create a file at tmp/adhdev_provider_fix_test.py that prints the current working directory and the squares of 1 through 5, then run python3 tmp/adhdev_provider_fix_test.py and tell me the exact output. Start the final answer with "Exact output:" and include the working directory plus the command stdout verbatim.';
48489
+ var CLI_AUTO_FIX_VERIFICATION_PROFILES = {
48490
+ "codex-cli": {
48275
48491
  fixtureName: "codex-cli-provider-fix",
48276
- request: {
48277
- type,
48278
- workingDir: providerDir,
48279
- freshSession: true,
48280
- autoLaunch: true,
48281
- autoResolveApprovals: true,
48282
- approvalButtonIndex: 0,
48283
- timeoutMs: 9e4,
48284
- traceLimit: 200,
48285
- text: "Create a file at tmp/adhdev_provider_fix_test.py that prints the current working directory and the squares of 1 through 5, then run python3 tmp/adhdev_provider_fix_test.py and tell me the exact output."
48286
- },
48287
48492
  inspectFields: [
48288
48493
  "debug.messages",
48289
48494
  "trace.entries[].payload.parsedLastAssistant",
48290
48495
  "trace.entries[].payload.detectStatus",
48291
48496
  "trace.entries[].payload.parsedStatus"
48292
48497
  ],
48498
+ focusAreas: [
48499
+ "Startup or trust screens must be classified as approval/current chrome, not assistant transcript content.",
48500
+ "Prefer fixing provider.json submit/approval behavior before piling on parser heuristics.",
48501
+ "The final assistant transcript must preserve the exact stdout block and current working directory."
48502
+ ],
48293
48503
  lastAssistantMustContainAny: [
48294
48504
  "Exact output:",
48295
48505
  "1",
@@ -48298,16 +48508,81 @@ function getCliAutoFixVerification(type, providerDir) {
48298
48508
  "16",
48299
48509
  "25"
48300
48510
  ],
48301
- lastAssistantMustMatchAny: [
48302
- escapedDir
48303
- ],
48304
48511
  lastAssistantMustNotContainAny: [
48305
48512
  "Do you trust the contents of this directory?",
48306
48513
  "OpenAI Codex",
48307
48514
  "Tip: New",
48308
48515
  "Summarize recent commits"
48309
48516
  ],
48310
- description: "Codex CLI must classify startup/trust screens correctly, transition idle -> generating -> idle, and preserve the exact stdout block in the final assistant transcript."
48517
+ description: "Codex CLI must classify startup/trust screens correctly, transition idle -> generating -> idle, and preserve the exact stdout block in the final assistant transcript.",
48518
+ timeoutMs: 9e4
48519
+ },
48520
+ "claude-cli": {
48521
+ fixtureName: "claude-cli-provider-fix",
48522
+ inspectFields: [
48523
+ "statusesSeen",
48524
+ "approvalsResolved",
48525
+ "debug.messages",
48526
+ "trace.entries[].payload.detectStatus",
48527
+ "trace.entries[].payload.parsedStatus",
48528
+ "trace.entries[].payload.parsedLastAssistant",
48529
+ "trace.entries[].payload.approval"
48530
+ ],
48531
+ focusAreas: [
48532
+ "Some environments show a startup trust or safety confirmation before the first prompt; treat that screen as waiting_approval, not assistant content.",
48533
+ "Single-action startup confirmations should resolve with Enter/confirm handling instead of assuming numbered approval options.",
48534
+ "Do not leak footer chrome like shortcuts help, version banners, /effort text, or startup trust wording into the parsed transcript."
48535
+ ],
48536
+ lastAssistantMustContainAny: [
48537
+ "Exact output:",
48538
+ "1",
48539
+ "4",
48540
+ "9",
48541
+ "16",
48542
+ "25"
48543
+ ],
48544
+ lastAssistantMustNotContainAny: [
48545
+ "Quick safety check",
48546
+ "Is this a project you trust",
48547
+ "Enter to confirm",
48548
+ "Claude Code",
48549
+ "Type your message"
48550
+ ],
48551
+ description: "Claude CLI must survive startup trust/safety prompts, resolve them cleanly, transition back to generating/idle, and keep startup chrome out of the final assistant transcript.",
48552
+ timeoutMs: 12e4
48553
+ }
48554
+ };
48555
+ function getCliAutoFixVerification(type, providerDir) {
48556
+ const profile = CLI_AUTO_FIX_VERIFICATION_PROFILES[type];
48557
+ if (!profile) return null;
48558
+ const normalizedDir = providerDir.replace(/\\/g, "/");
48559
+ const escapedDir = escapeRegex2(normalizedDir);
48560
+ return {
48561
+ fixtureName: profile.fixtureName,
48562
+ request: {
48563
+ type,
48564
+ workingDir: providerDir,
48565
+ freshSession: true,
48566
+ autoLaunch: true,
48567
+ autoResolveApprovals: true,
48568
+ approvalButtonIndex: 0,
48569
+ timeoutMs: profile.timeoutMs ?? 9e4,
48570
+ traceLimit: 200,
48571
+ text: CLI_PROVIDER_FIX_PROMPT
48572
+ },
48573
+ inspectFields: profile.inspectFields || [
48574
+ "debug.messages",
48575
+ "trace.entries[].payload.parsedLastAssistant",
48576
+ "trace.entries[].payload.detectStatus",
48577
+ "trace.entries[].payload.parsedStatus"
48578
+ ],
48579
+ focusAreas: profile.focusAreas || [],
48580
+ lastAssistantMustContainAny: profile.lastAssistantMustContainAny || [],
48581
+ lastAssistantMustMatchAny: [
48582
+ escapedDir
48583
+ ],
48584
+ lastAssistantMustNotContainAny: profile.lastAssistantMustNotContainAny || [],
48585
+ description: profile.description
48311
48586
  };
48312
48587
  }
48313
48588
  function hideCommand2(command) {