adhdev 0.8.14 → 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
@@ -6322,71 +6322,6 @@ var init_terminal_screen = __esm({
6322
6322
  }
6323
6323
  });
6324
6324
 
6325
- // ../../oss/packages/daemon-core/src/cli-adapters/pty-transport.ts
6326
- var os7, pty, NodePtyRuntimeTransport, NodePtyTransportFactory;
6327
- var init_pty_transport = __esm({
6328
- "../../oss/packages/daemon-core/src/cli-adapters/pty-transport.ts"() {
6329
- "use strict";
6330
- os7 = __toESM(require("os"));
6331
- try {
6332
- pty = require("node-pty");
6333
- } catch {
6334
- pty = null;
6335
- }
6336
- NodePtyRuntimeTransport = class {
6337
- constructor(handle) {
6338
- this.handle = handle;
6339
- }
6340
- ready = Promise.resolve();
6341
- terminalQueriesHandled = false;
6342
- get pid() {
6343
- return this.handle.pid;
6344
- }
6345
- write(data) {
6346
- this.handle.write(data);
6347
- }
6348
- resize(cols, rows) {
6349
- this.handle.resize(cols, rows);
6350
- }
6351
- kill() {
6352
- this.handle.kill();
6353
- }
6354
- getMetadata() {
6355
- return null;
6356
- }
6357
- onData(callback) {
6358
- this.handle.onData(callback);
6359
- }
6360
- onExit(callback) {
6361
- this.handle.onExit(callback);
6362
- }
6363
- };
6364
- NodePtyTransportFactory = class {
6365
- spawn(command, args, options) {
6366
- if (!pty) throw new Error("node-pty is not installed");
6367
- let cwd = options.cwd;
6368
- if (cwd) {
6369
- try {
6370
- const fs19 = require("fs");
6371
- const stat4 = fs19.statSync(cwd);
6372
- if (!stat4.isDirectory()) cwd = os7.homedir();
6373
- } catch {
6374
- cwd = os7.homedir();
6375
- }
6376
- }
6377
- const handle = pty.spawn(command, args, {
6378
- name: "xterm-256color",
6379
- cols: options.cols,
6380
- rows: options.rows,
6381
- cwd,
6382
- env: options.env
6383
- });
6384
- return new NodePtyRuntimeTransport(handle);
6385
- }
6386
- };
6387
- }
6388
- });
6389
-
6390
6325
  // ../../oss/packages/session-host-core/dist/index.mjs
6391
6326
  function getDefaultSessionHostEndpoint(appName = "adhdev") {
6392
6327
  if (process.platform === "win32") {
@@ -6397,7 +6332,7 @@ function getDefaultSessionHostEndpoint(appName = "adhdev") {
6397
6332
  }
6398
6333
  return {
6399
6334
  kind: "unix",
6400
- path: path22.join(os8.tmpdir(), `${appName}-session-host.sock`)
6335
+ path: path22.join(os7.tmpdir(), `${appName}-session-host.sock`)
6401
6336
  };
6402
6337
  }
6403
6338
  function serializeEnvelope(envelope) {
@@ -6462,11 +6397,11 @@ function ensureNodePtySpawnHelperPermissions(logFn) {
6462
6397
  } catch {
6463
6398
  }
6464
6399
  }
6465
- var os8, path22, net, import_crypto3, os22, path32, __require, SessionHostClient;
6400
+ var os7, path22, net, import_crypto3, os22, path32, __require, SessionHostClient;
6466
6401
  var init_dist = __esm({
6467
6402
  "../../oss/packages/session-host-core/dist/index.mjs"() {
6468
6403
  "use strict";
6469
- os8 = __toESM(require("os"), 1);
6404
+ os7 = __toESM(require("os"), 1);
6470
6405
  path22 = __toESM(require("path"), 1);
6471
6406
  net = __toESM(require("net"), 1);
6472
6407
  import_crypto3 = require("crypto");
@@ -6595,6 +6530,78 @@ var init_spawn_env = __esm({
6595
6530
  }
6596
6531
  });
6597
6532
 
6533
+ // ../../oss/packages/daemon-core/src/cli-adapters/pty-transport.ts
6534
+ function loadNodePty() {
6535
+ if (cachedPty !== void 0) return cachedPty;
6536
+ try {
6537
+ cachedPty = require("node-pty");
6538
+ ensureNodePtySpawnHelperPermissions();
6539
+ } catch {
6540
+ cachedPty = null;
6541
+ }
6542
+ return cachedPty;
6543
+ }
6544
+ var os8, cachedPty, NodePtyRuntimeTransport, NodePtyTransportFactory;
6545
+ var init_pty_transport = __esm({
6546
+ "../../oss/packages/daemon-core/src/cli-adapters/pty-transport.ts"() {
6547
+ "use strict";
6548
+ os8 = __toESM(require("os"));
6549
+ init_spawn_env();
6550
+ NodePtyRuntimeTransport = class {
6551
+ constructor(handle) {
6552
+ this.handle = handle;
6553
+ }
6554
+ ready = Promise.resolve();
6555
+ terminalQueriesHandled = false;
6556
+ get pid() {
6557
+ return this.handle.pid;
6558
+ }
6559
+ write(data) {
6560
+ this.handle.write(data);
6561
+ }
6562
+ resize(cols, rows) {
6563
+ this.handle.resize(cols, rows);
6564
+ }
6565
+ kill() {
6566
+ this.handle.kill();
6567
+ }
6568
+ getMetadata() {
6569
+ return null;
6570
+ }
6571
+ onData(callback) {
6572
+ this.handle.onData(callback);
6573
+ }
6574
+ onExit(callback) {
6575
+ this.handle.onExit(callback);
6576
+ }
6577
+ };
6578
+ NodePtyTransportFactory = class {
6579
+ spawn(command, args, options) {
6580
+ const pty = loadNodePty();
6581
+ if (!pty) throw new Error("node-pty is not installed");
6582
+ let cwd = options.cwd;
6583
+ if (cwd) {
6584
+ try {
6585
+ const fs19 = require("fs");
6586
+ const stat4 = fs19.statSync(cwd);
6587
+ if (!stat4.isDirectory()) cwd = os8.homedir();
6588
+ } catch {
6589
+ cwd = os8.homedir();
6590
+ }
6591
+ }
6592
+ const handle = pty.spawn(command, args, {
6593
+ name: "xterm-256color",
6594
+ cols: options.cols,
6595
+ rows: options.rows,
6596
+ cwd,
6597
+ env: options.env
6598
+ });
6599
+ return new NodePtyRuntimeTransport(handle);
6600
+ }
6601
+ };
6602
+ }
6603
+ });
6604
+
6598
6605
  // ../../oss/packages/daemon-core/src/cli-adapters/provider-cli-adapter.ts
6599
6606
  var provider_cli_adapter_exports = {};
6600
6607
  __export(provider_cli_adapter_exports, {
@@ -6714,6 +6721,40 @@ function normalizeScreenSnapshot(text) {
6714
6721
  function normalizeComparableMessageContent(text) {
6715
6722
  return String(text || "").replace(/\s+/g, " ").trim();
6716
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
+ }
6717
6758
  function parsePatternEntry(x) {
6718
6759
  if (x instanceof RegExp) return x;
6719
6760
  if (x && typeof x === "object" && typeof x.source === "string") {
@@ -6738,7 +6779,7 @@ function normalizeCliProviderForRuntime(raw) {
6738
6779
  }
6739
6780
  };
6740
6781
  }
6741
- var os9, path7, import_child_process4, pty2, buildCliSpawnEnv, ProviderCliAdapter;
6782
+ var os9, path7, import_child_process4, buildCliSpawnEnv, ProviderCliAdapter;
6742
6783
  var init_provider_cli_adapter = __esm({
6743
6784
  "../../oss/packages/daemon-core/src/cli-adapters/provider-cli-adapter.ts"() {
6744
6785
  "use strict";
@@ -6749,12 +6790,6 @@ var init_provider_cli_adapter = __esm({
6749
6790
  init_terminal_screen();
6750
6791
  init_pty_transport();
6751
6792
  init_spawn_env();
6752
- try {
6753
- pty2 = require("node-pty");
6754
- ensureNodePtySpawnHelperPermissions((msg) => LOG.info("CLI", msg));
6755
- } catch {
6756
- LOG.error("CLI", "[ProviderCliAdapter] node-pty not found. Terminal features disabled.");
6757
- }
6758
6793
  buildCliSpawnEnv = sanitizeSpawnEnv;
6759
6794
  ProviderCliAdapter = class _ProviderCliAdapter {
6760
6795
  constructor(provider, workingDir, extraArgs = [], transportFactory = new NodePtyTransportFactory()) {
@@ -6856,6 +6891,8 @@ var init_provider_cli_adapter = __esm({
6856
6891
  submitRetryUsed = false;
6857
6892
  submitRetryPromptSnippet = "";
6858
6893
  idleFinishCandidate = null;
6894
+ finishRetryTimer = null;
6895
+ finishRetryCount = 0;
6859
6896
  // Resize redraw suppression
6860
6897
  resizeSuppressUntil = 0;
6861
6898
  // Debug: status transition history
@@ -6877,6 +6914,8 @@ var init_provider_cli_adapter = __esm({
6877
6914
  static MAX_TRACE_ENTRIES = 250;
6878
6915
  providerResolutionMeta;
6879
6916
  static IDLE_FINISH_CONFIRM_MS = 900;
6917
+ static FINISH_RETRY_DELAY_MS = 300;
6918
+ static MAX_FINISH_RETRIES = 2;
6880
6919
  syncMessageViews() {
6881
6920
  this.messages = [...this.committedMessages];
6882
6921
  this.structuredMessages = [...this.committedMessages];
@@ -6936,7 +6975,8 @@ var init_provider_cli_adapter = __esm({
6936
6975
  recentBuffer: buffer.slice(-1e3) || this.recentOutputBuffer,
6937
6976
  screenText: this.terminalScreen.getText(),
6938
6977
  messages: [...baseMessages],
6939
- partialResponse
6978
+ partialResponse,
6979
+ promptText: scope?.prompt || ""
6940
6980
  };
6941
6981
  }
6942
6982
  setStatus(status, trigger) {
@@ -7179,6 +7219,11 @@ var init_provider_cli_adapter = __esm({
7179
7219
  this.terminalScreen.reset(24, 80);
7180
7220
  this.pendingTerminalQueryTail = "";
7181
7221
  this.currentTurnScope = null;
7222
+ this.finishRetryCount = 0;
7223
+ if (this.finishRetryTimer) {
7224
+ clearTimeout(this.finishRetryTimer);
7225
+ this.finishRetryTimer = null;
7226
+ }
7182
7227
  this.ready = false;
7183
7228
  await this.ptyProcess.ready;
7184
7229
  this.recordTrace("ready", {
@@ -7225,11 +7270,12 @@ var init_provider_cli_adapter = __esm({
7225
7270
  if (this.startupParseGate) {
7226
7271
  this.startupBuffer += cleanData;
7227
7272
  const elapsed = Date.now() - this.spawnAt;
7228
- const scriptStatus = this.runDetectStatus(this.startupBuffer);
7229
7273
  const screenText = this.terminalScreen.getText() || "";
7274
+ const startupModal = this.getStartupConfirmationModal(screenText);
7275
+ const scriptStatus = startupModal ? "waiting_approval" : this.runDetectStatus(this.startupBuffer);
7230
7276
  const hasInteractivePrompt = this.looksLikeVisibleIdlePrompt(screenText);
7231
7277
  const startupStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
7232
- 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;
7233
7279
  if (isReady) {
7234
7280
  this.startupParseGate = false;
7235
7281
  this.ready = true;
@@ -7261,7 +7307,9 @@ var init_provider_cli_adapter = __esm({
7261
7307
  this.approvalExitTimeout = setTimeout(() => {
7262
7308
  if (this.currentStatus !== "waiting_approval") return;
7263
7309
  const tail = this.recentOutputBuffer;
7264
- const modal = this.runParseApproval(tail);
7310
+ const screenText = this.terminalScreen.getText() || "";
7311
+ const startupModal = this.getStartupConfirmationModal(screenText);
7312
+ const modal = this.runParseApproval(tail) || startupModal;
7265
7313
  const stillWaiting = this.runDetectStatus(tail) === "waiting_approval" || !!modal;
7266
7314
  if (stillWaiting) {
7267
7315
  this.activeModal = modal || this.activeModal || { message: "Approval required", buttons: ["Allow", "Deny"] };
@@ -7281,6 +7329,63 @@ var init_provider_cli_adapter = __esm({
7281
7329
  if (!text.trim()) return false;
7282
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);
7283
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
+ }
7284
7389
  async waitForInteractivePrompt(maxWaitMs = 5e3) {
7285
7390
  const startedAt = Date.now();
7286
7391
  let loggedWait = false;
@@ -7330,9 +7435,10 @@ var init_provider_cli_adapter = __esm({
7330
7435
  }
7331
7436
  const tail = this.settledBuffer;
7332
7437
  const screenText = this.terminalScreen.getText() || "";
7333
- const modal = this.runParseApproval(tail);
7438
+ const startupModal = this.getStartupConfirmationModal(screenText);
7439
+ const modal = this.runParseApproval(tail) || startupModal;
7334
7440
  const rawScriptStatus = this.runDetectStatus(tail);
7335
- const scriptStatus = rawScriptStatus;
7441
+ const scriptStatus = startupModal ? "waiting_approval" : rawScriptStatus;
7336
7442
  const parsedTranscript = this.parseCurrentTranscript(
7337
7443
  this.committedMessages,
7338
7444
  this.responseBuffer,
@@ -7527,7 +7633,24 @@ var init_provider_cli_adapter = __esm({
7527
7633
  this.recordTrace("finish_response", {
7528
7634
  ...this.buildTraceParseSnapshot(this.currentTurnScope, this.responseBuffer)
7529
7635
  });
7530
- 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
+ }
7531
7654
  if (this.responseTimeout) {
7532
7655
  clearTimeout(this.responseTimeout);
7533
7656
  this.responseTimeout = null;
@@ -7544,11 +7667,16 @@ var init_provider_cli_adapter = __esm({
7544
7667
  clearTimeout(this.submitRetryTimer);
7545
7668
  this.submitRetryTimer = null;
7546
7669
  }
7670
+ if (this.finishRetryTimer) {
7671
+ clearTimeout(this.finishRetryTimer);
7672
+ this.finishRetryTimer = null;
7673
+ }
7547
7674
  this.responseBuffer = "";
7548
7675
  this.isWaitingForResponse = false;
7549
7676
  this.responseSettleIgnoreUntil = 0;
7550
7677
  this.submitRetryUsed = false;
7551
7678
  this.submitRetryPromptSnippet = "";
7679
+ this.finishRetryCount = 0;
7552
7680
  this.currentTurnScope = null;
7553
7681
  this.activeModal = null;
7554
7682
  this.setStatus("idle", "response_finished");
@@ -7562,6 +7690,13 @@ var init_provider_cli_adapter = __esm({
7562
7690
  );
7563
7691
  if (parsed && Array.isArray(parsed.messages)) {
7564
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
+ }
7565
7700
  this.syncMessageViews();
7566
7701
  const lastAssistant = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
7567
7702
  this.recordTrace("commit_transcript", {
@@ -7577,7 +7712,15 @@ var init_provider_cli_adapter = __esm({
7577
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 || "-"}`
7578
7713
  );
7579
7714
  }
7715
+ return {
7716
+ hasAssistant: !!lastAssistant,
7717
+ assistantContent: lastAssistant?.content || ""
7718
+ };
7580
7719
  }
7720
+ return {
7721
+ hasAssistant: false,
7722
+ assistantContent: ""
7723
+ };
7581
7724
  }
7582
7725
  // ─── Script Execution ──────────────────────────
7583
7726
  runDetectStatus(text) {
@@ -7656,7 +7799,15 @@ var init_provider_cli_adapter = __esm({
7656
7799
  if (!this.cliScripts?.parseOutput) return null;
7657
7800
  try {
7658
7801
  const input = this.buildParseInput(baseMessages, partialResponse, scope);
7659
- 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;
7660
7811
  } catch (e) {
7661
7812
  LOG.warn("CLI", `[${this.cliType}] parseOutput error: ${e.message}`);
7662
7813
  return null;
@@ -7701,10 +7852,19 @@ ${data.message || ""}`.trim();
7701
7852
  if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
7702
7853
  if (this.isWaitingForResponse) return;
7703
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
+ }
7704
7859
  this.committedMessages.push({ role: "user", content: text, timestamp: Date.now() });
7705
7860
  this.syncMessageViews();
7706
7861
  this.isWaitingForResponse = true;
7707
7862
  this.responseBuffer = "";
7863
+ this.finishRetryCount = 0;
7864
+ if (this.finishRetryTimer) {
7865
+ clearTimeout(this.finishRetryTimer);
7866
+ this.finishRetryTimer = null;
7867
+ }
7708
7868
  this.clearIdleFinishCandidate("send_message");
7709
7869
  this.currentTurnScope = {
7710
7870
  prompt: text,
@@ -7932,6 +8092,10 @@ ${data.message || ""}`.trim();
7932
8092
  clearTimeout(this.submitRetryTimer);
7933
8093
  this.submitRetryTimer = null;
7934
8094
  }
8095
+ if (this.finishRetryTimer) {
8096
+ clearTimeout(this.finishRetryTimer);
8097
+ this.finishRetryTimer = null;
8098
+ }
7935
8099
  if (this.responseTimeout) {
7936
8100
  clearTimeout(this.responseTimeout);
7937
8101
  this.responseTimeout = null;
@@ -7955,6 +8119,7 @@ ${data.message || ""}`.trim();
7955
8119
  this.ptyOutputFlushTimer = null;
7956
8120
  }
7957
8121
  this.ptyOutputBuffer = "";
8122
+ this.finishRetryCount = 0;
7958
8123
  if (this.ptyProcess) {
7959
8124
  this.ptyProcess.write("");
7960
8125
  setTimeout(() => {
@@ -7985,6 +8150,10 @@ ${data.message || ""}`.trim();
7985
8150
  clearTimeout(this.submitRetryTimer);
7986
8151
  this.submitRetryTimer = null;
7987
8152
  }
8153
+ if (this.finishRetryTimer) {
8154
+ clearTimeout(this.finishRetryTimer);
8155
+ this.finishRetryTimer = null;
8156
+ }
7988
8157
  if (this.responseTimeout) {
7989
8158
  clearTimeout(this.responseTimeout);
7990
8159
  this.responseTimeout = null;
@@ -8008,6 +8177,7 @@ ${data.message || ""}`.trim();
8008
8177
  this.ptyOutputFlushTimer = null;
8009
8178
  }
8010
8179
  this.ptyOutputBuffer = "";
8180
+ this.finishRetryCount = 0;
8011
8181
  if (this.ptyProcess) {
8012
8182
  try {
8013
8183
  if (typeof this.ptyProcess.detach === "function") {
@@ -8044,6 +8214,11 @@ ${data.message || ""}`.trim();
8044
8214
  this.ptyOutputFlushTimer = null;
8045
8215
  }
8046
8216
  this.ptyOutputBuffer = "";
8217
+ if (this.finishRetryTimer) {
8218
+ clearTimeout(this.finishRetryTimer);
8219
+ this.finishRetryTimer = null;
8220
+ }
8221
+ this.finishRetryCount = 0;
8047
8222
  this.terminalScreen.reset();
8048
8223
  this.ptyProcess?.clearBuffer?.();
8049
8224
  this.onStatusChange?.();
@@ -8063,10 +8238,11 @@ ${data.message || ""}`.trim();
8063
8238
  }
8064
8239
  resolveModal(buttonIndex) {
8065
8240
  if (!this.ptyProcess || this.currentStatus !== "waiting_approval" && !this.activeModal) return;
8241
+ const modal = this.activeModal;
8066
8242
  this.clearIdleFinishCandidate("resolve_modal");
8067
8243
  this.recordTrace("resolve_modal", {
8068
8244
  buttonIndex,
8069
- activeModal: this.activeModal
8245
+ activeModal: modal
8070
8246
  });
8071
8247
  this.activeModal = null;
8072
8248
  this.lastApprovalResolvedAt = Date.now();
@@ -8077,7 +8253,9 @@ ${data.message || ""}`.trim();
8077
8253
  }
8078
8254
  this.setStatus("generating", "approval_resolved");
8079
8255
  this.onStatusChange?.();
8080
- if (buttonIndex in this.approvalKeys) {
8256
+ if (this.shouldResolveModalWithEnter(modal, buttonIndex)) {
8257
+ this.ptyProcess.write("\r");
8258
+ } else if (buttonIndex in this.approvalKeys) {
8081
8259
  this.ptyProcess.write(this.approvalKeys[buttonIndex]);
8082
8260
  } else {
8083
8261
  const DOWN = "\x1B[B";
@@ -30037,7 +30215,10 @@ function appendUpgradeLog(message) {
30037
30215
  }
30038
30216
  }
30039
30217
  function getNpmExecutable() {
30040
- return process.platform === "win32" ? "npm.cmd" : "npm";
30218
+ return "npm";
30219
+ }
30220
+ function getNpmExecOptions() {
30221
+ return { shell: process.platform === "win32" };
30041
30222
  }
30042
30223
  function killPid(pid) {
30043
30224
  try {
@@ -30099,9 +30280,10 @@ function removeDaemonPidFile() {
30099
30280
  }
30100
30281
  }
30101
30282
  function cleanupStaleGlobalInstallDirs(pkgName) {
30102
- 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();
30103
30285
  if (!npmRoot) return;
30104
- 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();
30105
30287
  const binDir = process.platform === "win32" ? npmPrefix : path13.join(npmPrefix, "bin");
30106
30288
  const packageBaseName = pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName;
30107
30289
  const binNames = /* @__PURE__ */ new Set([packageBaseName]);
@@ -30162,7 +30344,8 @@ async function runDaemonUpgradeHelper(payload) {
30162
30344
  {
30163
30345
  encoding: "utf8",
30164
30346
  stdio: "pipe",
30165
- maxBuffer: 20 * 1024 * 1024
30347
+ maxBuffer: 20 * 1024 * 1024,
30348
+ ...getNpmExecOptions()
30166
30349
  }
30167
30350
  );
30168
30351
  if (installOutput.trim()) {
@@ -33485,6 +33668,53 @@ async function runCliExerciseInternal(ctx, body) {
33485
33668
  let lastModalKey = "";
33486
33669
  let idleSince = 0;
33487
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
+ }
33488
33718
  ctx.instanceManager.sendEvent(bundle.target.instanceId, "send_message", { text });
33489
33719
  while (Date.now() - startAt < Math.max(1e3, timeoutMs)) {
33490
33720
  await sleep(150);
@@ -33499,32 +33729,14 @@ async function runCliExerciseInternal(ctx, body) {
33499
33729
  const sawSendMessage = traceEntries.some((entry) => entry?.type === "send_message");
33500
33730
  const sawSubmitWrite = traceEntries.some((entry) => entry?.type === "submit_write");
33501
33731
  const hasTurnStarted = sawSendMessage || sawSubmitWrite || !!debug?.currentTurnScope;
33502
- if (status !== lastStatus) {
33503
- statusesSeen.push(status);
33504
- lastStatus = status;
33505
- }
33732
+ noteStatus(status);
33506
33733
  if (status === "generating" || status === "waiting_approval") {
33507
33734
  sawBusy = true;
33508
33735
  idleSince = 0;
33509
33736
  }
33510
33737
  const modal = debug?.activeModal || trace?.activeModal || null;
33511
- if (autoResolveApprovals && status === "waiting_approval" && modal && Array.isArray(modal.buttons) && modal.buttons.length > 0) {
33512
- const clampedIndex = Math.max(0, Math.min(Number(approvalButtonIndex) || 0, modal.buttons.length - 1));
33513
- const modalKey = JSON.stringify({
33514
- message: modal.message || "",
33515
- buttons: modal.buttons,
33516
- index: clampedIndex
33517
- });
33518
- if (modalKey !== lastModalKey && typeof bundle.adapter.resolveModal === "function") {
33519
- lastModalKey = modalKey;
33520
- approvalsResolved.push({
33521
- at: Date.now(),
33522
- buttonIndex: clampedIndex,
33523
- label: modal.buttons[clampedIndex] || null
33524
- });
33525
- bundle.adapter.resolveModal(clampedIndex);
33526
- continue;
33527
- }
33738
+ if (resolveActiveModalIfNeeded(status, modal)) {
33739
+ continue;
33528
33740
  }
33529
33741
  const traceCount = Number(trace?.entryCount || 0);
33530
33742
  const hasProgress = hasTurnStarted && (traceCount > preTraceCount || statusesSeen.length > 1 || approvalsResolved.length > 0);
@@ -34525,10 +34737,10 @@ async function handleAutoImplement(ctx, type, req, res) {
34525
34737
  let isPty = false;
34526
34738
  const { spawn: spawnFn } = await import("child_process");
34527
34739
  try {
34528
- const pty3 = require("node-pty");
34740
+ const pty = require("node-pty");
34529
34741
  ctx.log(`Auto-implement spawn (PTY): ${shellCmd}`);
34530
34742
  const isWin2 = os18.platform() === "win32";
34531
- child = pty3.spawn(isWin2 ? "cmd.exe" : process.env.SHELL || "/bin/zsh", [isWin2 ? "/c" : "-c", shellCmd], {
34743
+ child = pty.spawn(isWin2 ? "cmd.exe" : process.env.SHELL || "/bin/zsh", [isWin2 ? "/c" : "-c", shellCmd], {
34532
34744
  name: "xterm-256color",
34533
34745
  cols: 120,
34534
34746
  rows: 40,
@@ -35184,6 +35396,13 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
35184
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.");
35185
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.");
35186
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
+ }
35187
35406
  lines.push("## Task");
35188
35407
  lines.push(`Edit files in \`${providerDir}\` to implement: **${functions.join(", ")}**`);
35189
35408
  lines.push("");
@@ -46291,7 +46510,7 @@ var init_adhdev_daemon = __esm({
46291
46510
  import_ws3 = require("ws");
46292
46511
  import_chalk2 = __toESM(require("chalk"));
46293
46512
  init_version();
46294
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.14" });
46513
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.16" });
46295
46514
  DANGEROUS_PATTERNS = [
46296
46515
  /\brm\s+(-[a-z]*f|-[a-z]*r|--force|--recursive)/i,
46297
46516
  /\bsudo\b/i,
@@ -48266,29 +48485,21 @@ function getDefaultAutoFixReference(category, type, providers) {
48266
48485
  function escapeRegex2(value) {
48267
48486
  return String(value || "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
48268
48487
  }
48269
- function getCliAutoFixVerification(type, providerDir) {
48270
- if (type !== "codex-cli") return null;
48271
- const normalizedDir = providerDir.replace(/\\/g, "/");
48272
- const escapedDir = escapeRegex2(normalizedDir);
48273
- 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": {
48274
48491
  fixtureName: "codex-cli-provider-fix",
48275
- request: {
48276
- type,
48277
- workingDir: providerDir,
48278
- freshSession: true,
48279
- autoLaunch: true,
48280
- autoResolveApprovals: true,
48281
- approvalButtonIndex: 0,
48282
- timeoutMs: 9e4,
48283
- traceLimit: 200,
48284
- 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."
48285
- },
48286
48492
  inspectFields: [
48287
48493
  "debug.messages",
48288
48494
  "trace.entries[].payload.parsedLastAssistant",
48289
48495
  "trace.entries[].payload.detectStatus",
48290
48496
  "trace.entries[].payload.parsedStatus"
48291
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
+ ],
48292
48503
  lastAssistantMustContainAny: [
48293
48504
  "Exact output:",
48294
48505
  "1",
@@ -48297,16 +48508,81 @@ function getCliAutoFixVerification(type, providerDir) {
48297
48508
  "16",
48298
48509
  "25"
48299
48510
  ],
48300
- lastAssistantMustMatchAny: [
48301
- escapedDir
48302
- ],
48303
48511
  lastAssistantMustNotContainAny: [
48304
48512
  "Do you trust the contents of this directory?",
48305
48513
  "OpenAI Codex",
48306
48514
  "Tip: New",
48307
48515
  "Summarize recent commits"
48308
48516
  ],
48309
- 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
48310
48586
  };
48311
48587
  }
48312
48588
  function hideCommand2(command) {
@@ -49294,6 +49570,14 @@ function registerCdpCommands(program2) {
49294
49570
  // src/cli/index.ts
49295
49571
  init_version();
49296
49572
  var pkgVersion2 = resolvePackageVersion();
49573
+ if (process.platform === "win32") {
49574
+ const nodeMajor = Number.parseInt(process.versions.node.split(".")[0] || "0", 10);
49575
+ if (nodeMajor >= 24) {
49576
+ console.error(import_chalk8.default.red("\n\u2717 Windows is currently unsupported on Node.js 24+ for ADHDev."));
49577
+ console.error(import_chalk8.default.gray(" Install Node.js 22.x on Windows, then retry.\n"));
49578
+ process.exit(1);
49579
+ }
49580
+ }
49297
49581
  var _cliProviderLoader = new ProviderLoader({ logFn: () => {
49298
49582
  } });
49299
49583
  _cliProviderLoader.loadAll();