adhdev 0.6.52 → 0.6.55

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/index.js CHANGED
@@ -3173,6 +3173,9 @@ var init_builders = __esm({
3173
3173
  });
3174
3174
 
3175
3175
  // ../daemon-core/src/commands/chat-commands.ts
3176
+ function getTargetedCliAdapter(h, args, providerType) {
3177
+ return h.getCliAdapter(args?._targetInstance || h.currentIdeType || providerType);
3178
+ }
3176
3179
  async function handleChatHistory(h, args) {
3177
3180
  const { agentType, offset, limit, instanceId } = args;
3178
3181
  try {
@@ -3185,10 +3188,10 @@ async function handleChatHistory(h, args) {
3185
3188
  }
3186
3189
  }
3187
3190
  async function handleReadChat(h, args) {
3188
- const provider = h.getProvider();
3191
+ const provider = h.getProvider(args?.agentType);
3189
3192
  const _log = (msg) => LOG.debug("Command", `[read_chat] ${msg}`);
3190
3193
  if (provider?.category === "cli" || provider?.category === "acp") {
3191
- const adapter = h.getCliAdapter(provider.type);
3194
+ const adapter = getTargetedCliAdapter(h, args, provider.type);
3192
3195
  if (adapter) {
3193
3196
  _log(`${provider.category} adapter: ${adapter.cliType}`);
3194
3197
  const status = adapter.getStatus?.();
@@ -3304,7 +3307,7 @@ async function handleSendChat(h, args) {
3304
3307
  const text = args?.text || args?.message;
3305
3308
  if (!text) return { success: false, error: "text required" };
3306
3309
  const _log = (msg) => LOG.debug("Command", `[send_chat] ${msg}`);
3307
- const provider = h.getProvider();
3310
+ const provider = h.getProvider(args?.agentType);
3308
3311
  const _logSendSuccess = (method, targetAgent) => {
3309
3312
  h.historyWriter.appendNewMessages(
3310
3313
  targetAgent || provider?.type || h.currentIdeType || "unknown_agent",
@@ -3316,7 +3319,7 @@ async function handleSendChat(h, args) {
3316
3319
  return { success: true, sent: true, method, targetAgent };
3317
3320
  };
3318
3321
  if (provider?.category === "cli" || provider?.category === "acp") {
3319
- const adapter = h.getCliAdapter(provider.type);
3322
+ const adapter = getTargetedCliAdapter(h, args, provider.type);
3320
3323
  if (adapter) {
3321
3324
  _log(`${provider.category} adapter: ${adapter.cliType}`);
3322
3325
  try {
@@ -3469,7 +3472,7 @@ async function handleSendChat(h, args) {
3469
3472
  return { success: false, error: "No provider method could send the message" };
3470
3473
  }
3471
3474
  async function handleListChats(h, args) {
3472
- const provider = h.getProvider();
3475
+ const provider = h.getProvider(args?.agentType);
3473
3476
  if (provider?.category === "extension" && h.agentStream && h.getCdp()) {
3474
3477
  try {
3475
3478
  const chats = await h.agentStream.listAgentChats(h.getCdp(), provider.type);
@@ -3521,7 +3524,16 @@ async function handleListChats(h, args) {
3521
3524
  return { success: false, error: "listSessions script not available for this provider" };
3522
3525
  }
3523
3526
  async function handleNewChat(h, args) {
3524
- const provider = h.getProvider();
3527
+ const provider = h.getProvider(args?.agentType);
3528
+ if (provider?.category === "cli") {
3529
+ const adapter = getTargetedCliAdapter(h, args, provider.type);
3530
+ if (!adapter) return { success: false, error: "CLI adapter not running" };
3531
+ if (typeof adapter.clearHistory === "function") {
3532
+ adapter.clearHistory();
3533
+ return { success: true, cleared: true };
3534
+ }
3535
+ return { success: false, error: "new_chat not supported by this CLI provider" };
3536
+ }
3525
3537
  if (provider?.category === "extension" && h.agentStream && h.getCdp()) {
3526
3538
  const ok = await h.agentStream.newAgentSession(h.getCdp(), provider.type, h.currentIdeType);
3527
3539
  return { success: ok };
@@ -3546,7 +3558,7 @@ async function handleNewChat(h, args) {
3546
3558
  return { success: false, error: "newSession script not available for this provider" };
3547
3559
  }
3548
3560
  async function handleSwitchChat(h, args) {
3549
- const provider = h.getProvider();
3561
+ const provider = h.getProvider(args?.agentType);
3550
3562
  const ideType = h.currentIdeType;
3551
3563
  const sessionId = args?.sessionId || args?.id || args?.chatId;
3552
3564
  if (!sessionId) return { success: false, error: "sessionId required" };
@@ -3640,10 +3652,10 @@ async function handleSwitchChat(h, args) {
3640
3652
  }
3641
3653
  }
3642
3654
  async function handleSetMode(h, args) {
3643
- const provider = h.getProvider();
3655
+ const provider = h.getProvider(args?.agentType);
3644
3656
  const mode = args?.mode || "agent";
3645
3657
  if (provider?.category === "acp") {
3646
- const adapter = h.getCliAdapter(provider.type);
3658
+ const adapter = getTargetedCliAdapter(h, args, provider.type);
3647
3659
  if (adapter) {
3648
3660
  const acpInstance = adapter._acpInstance;
3649
3661
  if (acpInstance && typeof acpInstance.onEvent === "function") {
@@ -3695,11 +3707,11 @@ async function handleSetMode(h, args) {
3695
3707
  return { success: false, error: `setMode '${mode}' not supported by this provider` };
3696
3708
  }
3697
3709
  async function handleChangeModel(h, args) {
3698
- const provider = h.getProvider();
3710
+ const provider = h.getProvider(args?.agentType);
3699
3711
  const model = args?.model;
3700
3712
  LOG.info("Command", `[change_model] model=${model} provider=${provider?.type} category=${provider?.category} ideType=${h.currentIdeType} providerType=${h.currentProviderType}`);
3701
3713
  if (provider?.category === "acp") {
3702
- const adapter = h.getCliAdapter(provider.type);
3714
+ const adapter = getTargetedCliAdapter(h, args, provider.type);
3703
3715
  LOG.info("Command", `[change_model] ACP adapter found: ${!!adapter}, type=${adapter?.cliType}, hasAcpInstance=${!!adapter?._acpInstance}`);
3704
3716
  if (adapter) {
3705
3717
  const acpInstance = adapter._acpInstance;
@@ -3756,11 +3768,11 @@ async function handleSetThoughtLevel(h, args) {
3756
3768
  const configId = args?.configId;
3757
3769
  const value = args?.value;
3758
3770
  if (!configId || !value) return { success: false, error: "configId and value required" };
3759
- const provider = h.getProvider();
3771
+ const provider = h.getProvider(args?.agentType);
3760
3772
  if (!provider || provider.category !== "acp") {
3761
3773
  return { success: false, error: "set_thought_level only for ACP providers" };
3762
3774
  }
3763
- const adapter = h.getCliAdapter(provider.type);
3775
+ const adapter = getTargetedCliAdapter(h, args, provider.type);
3764
3776
  const acpInstance = adapter?._acpInstance;
3765
3777
  if (!acpInstance) return { success: false, error: "ACP instance not found" };
3766
3778
  try {
@@ -3772,13 +3784,22 @@ async function handleSetThoughtLevel(h, args) {
3772
3784
  }
3773
3785
  }
3774
3786
  async function handleResolveAction(h, args) {
3775
- const provider = h.getProvider();
3787
+ const provider = h.getProvider(args?.agentType);
3776
3788
  const action = args?.action || "approve";
3777
3789
  const button = args?.button || args?.buttonText || (action === "approve" ? "Accept" : action === "reject" ? "Reject" : "Accept");
3778
3790
  LOG.info("Command", `[resolveAction] action=${action} button="${button}" provider=${provider?.type}`);
3779
3791
  if (provider?.category === "cli") {
3780
- const adapter = h.getCliAdapter(provider.type);
3792
+ const adapter = getTargetedCliAdapter(h, args, provider.type);
3781
3793
  if (!adapter) return { success: false, error: "CLI adapter not running" };
3794
+ if (args?.data && typeof adapter.resolveAction === "function") {
3795
+ try {
3796
+ await adapter.resolveAction(args.data);
3797
+ LOG.info("Command", `[resolveAction] CLI PTY \u2192 resolveAction triggered with data payload`);
3798
+ return { success: true, method: "cli-resolve-action" };
3799
+ } catch (e) {
3800
+ return { success: false, error: `CLI resolveAction failed: ${e.message}` };
3801
+ }
3802
+ }
3782
3803
  const status = adapter.getStatus?.();
3783
3804
  if (status?.status !== "waiting_approval") {
3784
3805
  return { success: false, error: "Not in approval state" };
@@ -5830,7 +5851,7 @@ var init_provider_loader = __esm({
5830
5851
  // ─── Private ───────────────────────────────────
5831
5852
  /**
5832
5853
  * Find the on-disk directory for a provider by type.
5833
- * Preferred shape: root/category/type. Legacy flat root/type is kept temporarily for compatibility.
5854
+ * Canonical shape: root/category/type.
5834
5855
  */
5835
5856
  findProviderDirInternal(type) {
5836
5857
  const provider = this.providers.get(type);
@@ -5839,9 +5860,8 @@ var init_provider_loader = __esm({
5839
5860
  const searchRoots = this.getProviderRoots();
5840
5861
  for (const root of searchRoots) {
5841
5862
  if (!fs5.existsSync(root)) continue;
5842
- for (const candidate of [this.getProviderDir(root, cat, type), path6.join(root, type)]) {
5843
- if (fs5.existsSync(path6.join(candidate, "provider.json"))) return candidate;
5844
- }
5863
+ const candidate = this.getProviderDir(root, cat, type);
5864
+ if (fs5.existsSync(path6.join(candidate, "provider.json"))) return candidate;
5845
5865
  const catDir = path6.join(root, cat);
5846
5866
  if (fs5.existsSync(catDir)) {
5847
5867
  try {
@@ -6016,8 +6036,9 @@ var init_provider_loader = __esm({
6016
6036
  }
6017
6037
  }
6018
6038
  compareVersions(a, b) {
6019
- const pa = a.split(".").map(Number);
6020
- const pb = b.split(".").map(Number);
6039
+ const normalize2 = (v) => v.split(/[-_+]/)[0].split(".").map((x) => parseInt(x, 10) || 0);
6040
+ const pa = normalize2(a);
6041
+ const pb = normalize2(b);
6021
6042
  for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
6022
6043
  const va = pa[i] || 0;
6023
6044
  const vb = pb[i] || 0;
@@ -7041,6 +7062,246 @@ var init_reporter = __esm({
7041
7062
  }
7042
7063
  });
7043
7064
 
7065
+ // ../daemon-core/src/cli-adapters/terminal-screen.ts
7066
+ function clamp(value, min, max) {
7067
+ return Math.max(min, Math.min(max, value));
7068
+ }
7069
+ var TerminalScreen;
7070
+ var init_terminal_screen = __esm({
7071
+ "../daemon-core/src/cli-adapters/terminal-screen.ts"() {
7072
+ "use strict";
7073
+ TerminalScreen = class {
7074
+ rows;
7075
+ cols;
7076
+ cursorRow = 0;
7077
+ cursorCol = 0;
7078
+ savedRow = 0;
7079
+ savedCol = 0;
7080
+ lines;
7081
+ constructor(rows = 40, cols = 120) {
7082
+ this.rows = rows;
7083
+ this.cols = cols;
7084
+ this.lines = this.makeLines(rows, cols);
7085
+ }
7086
+ reset(rows = this.rows, cols = this.cols) {
7087
+ this.rows = rows;
7088
+ this.cols = cols;
7089
+ this.cursorRow = 0;
7090
+ this.cursorCol = 0;
7091
+ this.savedRow = 0;
7092
+ this.savedCol = 0;
7093
+ this.lines = this.makeLines(rows, cols);
7094
+ }
7095
+ resize(rows, cols) {
7096
+ const nextRows = Math.max(1, rows | 0);
7097
+ const nextCols = Math.max(1, cols | 0);
7098
+ const next = this.makeLines(nextRows, nextCols);
7099
+ const copyRows = Math.min(this.rows, nextRows);
7100
+ const copyCols = Math.min(this.cols, nextCols);
7101
+ for (let r = 0; r < copyRows; r++) {
7102
+ for (let c = 0; c < copyCols; c++) {
7103
+ next[r][c] = this.lines[r][c];
7104
+ }
7105
+ }
7106
+ this.rows = nextRows;
7107
+ this.cols = nextCols;
7108
+ this.lines = next;
7109
+ this.cursorRow = clamp(this.cursorRow, 0, this.rows - 1);
7110
+ this.cursorCol = clamp(this.cursorCol, 0, this.cols - 1);
7111
+ this.savedRow = clamp(this.savedRow, 0, this.rows - 1);
7112
+ this.savedCol = clamp(this.savedCol, 0, this.cols - 1);
7113
+ }
7114
+ write(data) {
7115
+ let i = 0;
7116
+ while (i < data.length) {
7117
+ const ch = data[i];
7118
+ if (ch === "\x1B") {
7119
+ const consumed = this.consumeEscape(data, i);
7120
+ i = consumed > i ? consumed : i + 1;
7121
+ continue;
7122
+ }
7123
+ if (ch === "\r") {
7124
+ this.cursorCol = 0;
7125
+ i++;
7126
+ continue;
7127
+ }
7128
+ if (ch === "\n") {
7129
+ this.newLine();
7130
+ i++;
7131
+ continue;
7132
+ }
7133
+ if (ch === "\b") {
7134
+ this.cursorCol = Math.max(0, this.cursorCol - 1);
7135
+ i++;
7136
+ continue;
7137
+ }
7138
+ if (ch === " ") {
7139
+ const nextStop = Math.min(this.cols - 1, this.cursorCol + (8 - (this.cursorCol % 8 || 8)));
7140
+ while (this.cursorCol < nextStop) this.putChar(" ");
7141
+ i++;
7142
+ continue;
7143
+ }
7144
+ if (ch >= " " && ch !== "\x7F") {
7145
+ this.putChar(ch);
7146
+ }
7147
+ i++;
7148
+ }
7149
+ }
7150
+ getText() {
7151
+ const raw = this.lines.map((line) => line.join("").replace(/\s+$/, ""));
7152
+ let start = 0;
7153
+ let end = raw.length;
7154
+ while (start < end && raw[start] === "") start++;
7155
+ while (end > start && raw[end - 1] === "") end--;
7156
+ return raw.slice(start, end).join("\n");
7157
+ }
7158
+ consumeEscape(data, start) {
7159
+ const next = data[start + 1];
7160
+ if (!next) return start + 1;
7161
+ if (next === "[") {
7162
+ let end = start + 2;
7163
+ while (end < data.length && !/[@-~]/.test(data[end])) end++;
7164
+ if (end >= data.length) return data.length;
7165
+ this.applyCsi(data.slice(start + 2, end), data[end]);
7166
+ return end + 1;
7167
+ }
7168
+ if (next === "]") {
7169
+ let end = start + 2;
7170
+ while (end < data.length) {
7171
+ if (data[end] === "\x07") return end + 1;
7172
+ if (data[end] === "\x1B" && data[end + 1] === "\\") return end + 2;
7173
+ end++;
7174
+ }
7175
+ return data.length;
7176
+ }
7177
+ if (next === "7") {
7178
+ this.savedRow = this.cursorRow;
7179
+ this.savedCol = this.cursorCol;
7180
+ return start + 2;
7181
+ }
7182
+ if (next === "8") {
7183
+ this.cursorRow = this.savedRow;
7184
+ this.cursorCol = this.savedCol;
7185
+ return start + 2;
7186
+ }
7187
+ return start + 2;
7188
+ }
7189
+ applyCsi(paramText, finalChar) {
7190
+ const privateMode = paramText.startsWith("?");
7191
+ const normalized = privateMode ? paramText.slice(1) : paramText;
7192
+ const params = normalized.length > 0 ? normalized.split(";").map((p) => parseInt(p || "0", 10) || 0) : [0];
7193
+ switch (finalChar) {
7194
+ case "A":
7195
+ this.cursorRow = clamp(this.cursorRow - (params[0] || 1), 0, this.rows - 1);
7196
+ return;
7197
+ case "B":
7198
+ this.cursorRow = clamp(this.cursorRow + (params[0] || 1), 0, this.rows - 1);
7199
+ return;
7200
+ case "C":
7201
+ this.cursorCol = clamp(this.cursorCol + (params[0] || 1), 0, this.cols - 1);
7202
+ return;
7203
+ case "D":
7204
+ this.cursorCol = clamp(this.cursorCol - (params[0] || 1), 0, this.cols - 1);
7205
+ return;
7206
+ case "E":
7207
+ this.cursorRow = clamp(this.cursorRow + (params[0] || 1), 0, this.rows - 1);
7208
+ this.cursorCol = 0;
7209
+ return;
7210
+ case "F":
7211
+ this.cursorRow = clamp(this.cursorRow - (params[0] || 1), 0, this.rows - 1);
7212
+ this.cursorCol = 0;
7213
+ return;
7214
+ case "G":
7215
+ this.cursorCol = clamp((params[0] || 1) - 1, 0, this.cols - 1);
7216
+ return;
7217
+ case "H":
7218
+ case "f": {
7219
+ const row = (params[0] || 1) - 1;
7220
+ const col = (params[1] || 1) - 1;
7221
+ this.cursorRow = clamp(row, 0, this.rows - 1);
7222
+ this.cursorCol = clamp(col, 0, this.cols - 1);
7223
+ return;
7224
+ }
7225
+ case "J": {
7226
+ const mode = params[0] || 0;
7227
+ if (mode === 2 || mode === 3) {
7228
+ this.reset(this.rows, this.cols);
7229
+ } else if (mode === 0) {
7230
+ this.clearToEndOfScreen();
7231
+ } else if (mode === 1) {
7232
+ this.clearToStartOfScreen();
7233
+ }
7234
+ return;
7235
+ }
7236
+ case "K": {
7237
+ const mode = params[0] || 0;
7238
+ if (mode === 2) this.clearLine(this.cursorRow, 0, this.cols - 1);
7239
+ else if (mode === 1) this.clearLine(this.cursorRow, 0, this.cursorCol);
7240
+ else this.clearLine(this.cursorRow, this.cursorCol, this.cols - 1);
7241
+ return;
7242
+ }
7243
+ case "m":
7244
+ return;
7245
+ case "s":
7246
+ this.savedRow = this.cursorRow;
7247
+ this.savedCol = this.cursorCol;
7248
+ return;
7249
+ case "u":
7250
+ this.cursorRow = this.savedRow;
7251
+ this.cursorCol = this.savedCol;
7252
+ return;
7253
+ case "h":
7254
+ case "l":
7255
+ if (privateMode && (normalized === "1049" || normalized === "47")) {
7256
+ this.reset(this.rows, this.cols);
7257
+ }
7258
+ return;
7259
+ default:
7260
+ return;
7261
+ }
7262
+ }
7263
+ putChar(ch) {
7264
+ if (this.cursorRow < 0 || this.cursorRow >= this.rows) return;
7265
+ if (this.cursorCol < 0) this.cursorCol = 0;
7266
+ if (this.cursorCol >= this.cols) this.newLine();
7267
+ this.lines[this.cursorRow][this.cursorCol] = ch;
7268
+ this.cursorCol++;
7269
+ if (this.cursorCol >= this.cols) this.newLine();
7270
+ }
7271
+ newLine() {
7272
+ this.cursorCol = 0;
7273
+ if (this.cursorRow >= this.rows - 1) {
7274
+ this.lines.shift();
7275
+ this.lines.push(Array.from({ length: this.cols }, () => " "));
7276
+ } else {
7277
+ this.cursorRow++;
7278
+ }
7279
+ }
7280
+ clearLine(row, start, end) {
7281
+ if (row < 0 || row >= this.rows) return;
7282
+ for (let c = clamp(start, 0, this.cols - 1); c <= clamp(end, 0, this.cols - 1); c++) {
7283
+ this.lines[row][c] = " ";
7284
+ }
7285
+ }
7286
+ clearToEndOfScreen() {
7287
+ this.clearLine(this.cursorRow, this.cursorCol, this.cols - 1);
7288
+ for (let r = this.cursorRow + 1; r < this.rows; r++) {
7289
+ this.clearLine(r, 0, this.cols - 1);
7290
+ }
7291
+ }
7292
+ clearToStartOfScreen() {
7293
+ for (let r = 0; r < this.cursorRow; r++) {
7294
+ this.clearLine(r, 0, this.cols - 1);
7295
+ }
7296
+ this.clearLine(this.cursorRow, 0, this.cursorCol);
7297
+ }
7298
+ makeLines(rows, cols) {
7299
+ return Array.from({ length: rows }, () => Array.from({ length: cols }, () => " "));
7300
+ }
7301
+ };
7302
+ }
7303
+ });
7304
+
7044
7305
  // ../daemon-core/src/cli-adapters/provider-cli-adapter.ts
7045
7306
  var provider_cli_adapter_exports = {};
7046
7307
  __export(provider_cli_adapter_exports, {
@@ -7113,28 +7374,19 @@ function parsePatternEntry(x) {
7113
7374
  }
7114
7375
  return null;
7115
7376
  }
7116
- function coercePatternArray(raw, fallbacks) {
7117
- if (!Array.isArray(raw)) return [...fallbacks];
7118
- const parsed = raw.map(parsePatternEntry).filter((r) => r != null);
7119
- return parsed.length > 0 ? parsed : [...fallbacks];
7120
- }
7121
- function defaultCleanOutput(raw, _lastUserInput) {
7122
- return stripAnsi(raw).trim();
7377
+ function coercePatternArray(raw) {
7378
+ if (!Array.isArray(raw)) return [];
7379
+ return raw.map(parsePatternEntry).filter((r) => r != null);
7123
7380
  }
7124
7381
  function normalizeCliProviderForRuntime(raw) {
7125
7382
  const patterns = raw?.patterns || {};
7126
7383
  return {
7127
- ...raw,
7128
7384
  patterns: {
7129
- prompt: coercePatternArray(patterns.prompt, FALLBACK_PROMPT),
7130
- generating: coercePatternArray(patterns.generating, FALLBACK_GENERATING),
7131
- approval: coercePatternArray(patterns.approval, FALLBACK_APPROVAL),
7132
- ready: coercePatternArray(patterns.ready, [])
7133
- },
7134
- cleanOutput: typeof raw?.cleanOutput === "function" ? raw.cleanOutput : defaultCleanOutput
7385
+ approval: coercePatternArray(patterns.approval)
7386
+ }
7135
7387
  };
7136
7388
  }
7137
- var os11, path9, import_child_process5, pty, FALLBACK_PROMPT, FALLBACK_GENERATING, FALLBACK_APPROVAL, ProviderCliAdapter;
7389
+ var os11, path9, import_child_process5, pty, ProviderCliAdapter;
7138
7390
  var init_provider_cli_adapter = __esm({
7139
7391
  "../daemon-core/src/cli-adapters/provider-cli-adapter.ts"() {
7140
7392
  "use strict";
@@ -7142,6 +7394,7 @@ var init_provider_cli_adapter = __esm({
7142
7394
  path9 = __toESM(require("path"));
7143
7395
  import_child_process5 = require("child_process");
7144
7396
  init_logger();
7397
+ init_terminal_screen();
7145
7398
  try {
7146
7399
  pty = require("node-pty");
7147
7400
  if (os11.platform() !== "win32") {
@@ -7163,40 +7416,10 @@ var init_provider_cli_adapter = __esm({
7163
7416
  } catch {
7164
7417
  LOG.error("CLI", "[ProviderCliAdapter] node-pty not found. Terminal features disabled.");
7165
7418
  }
7166
- FALLBACK_PROMPT = [
7167
- /Type your message/i,
7168
- /for\s*shortcuts/i,
7169
- // Claude Code prompt
7170
- /\?\s*for\s*help/i,
7171
- // Claude Code help prompt
7172
- /Press enter/i,
7173
- /^[>›❯]\s*$/i,
7174
- // Prompt char as the complete evaluated string
7175
- /[>›❯]\s*$/
7176
- // Prompt char at the very end of evaluated string
7177
- ];
7178
- FALLBACK_GENERATING = [
7179
- /[\u2800-\u28ff]/,
7180
- // Braille spinner blocks (universal TUI)
7181
- /esc to (cancel|interrupt|stop)/i,
7182
- // Common TUI generation status line
7183
- /generating\.\.\./i,
7184
- /Claude is (?:thinking|processing|working)/i
7185
- // Specific Claude Code status
7186
- ];
7187
- FALLBACK_APPROVAL = [
7188
- /Allow\s*once/i,
7189
- // ANSI strip may remove spaces
7190
- /Always\s*allow/i,
7191
- /\(y\/n\)/i,
7192
- /\[Y\/n\]/i,
7193
- /Yes,?\s*don'?t\s*ask/i
7194
- // "Yes, don't ask again" (Claude Code)
7195
- ];
7196
- ProviderCliAdapter = class {
7419
+ ProviderCliAdapter = class _ProviderCliAdapter {
7197
7420
  constructor(provider, workingDir, extraArgs = []) {
7198
7421
  this.extraArgs = extraArgs;
7199
- this.provider = normalizeCliProviderForRuntime(provider);
7422
+ this.provider = provider;
7200
7423
  this.cliType = provider.type;
7201
7424
  this.cliName = provider.name;
7202
7425
  this.workingDir = workingDir.startsWith("~") ? workingDir.replace(/^~/, os11.homedir()) : workingDir;
@@ -7213,6 +7436,13 @@ var init_provider_cli_adapter = __esm({
7213
7436
  };
7214
7437
  const rawKeys = provider.approvalKeys;
7215
7438
  this.approvalKeys = rawKeys && typeof rawKeys === "object" ? rawKeys : {};
7439
+ this.cliScripts = provider.scripts || {};
7440
+ const scriptNames = Object.keys(this.cliScripts).filter((k) => typeof this.cliScripts[k] === "function");
7441
+ if (scriptNames.length > 0) {
7442
+ LOG.info("CLI", `[${this.cliType}] CLI scripts: [${scriptNames.join(", ")}]`);
7443
+ } else {
7444
+ LOG.warn("CLI", `[${this.cliType}] \u26A0 No CLI scripts loaded! Provider needs scripts/{version}/scripts.js`);
7445
+ }
7216
7446
  }
7217
7447
  cliType;
7218
7448
  cliName;
@@ -7220,6 +7450,7 @@ var init_provider_cli_adapter = __esm({
7220
7450
  provider;
7221
7451
  ptyProcess = null;
7222
7452
  messages = [];
7453
+ structuredMessages = [];
7223
7454
  currentStatus = "starting";
7224
7455
  onStatusChange = null;
7225
7456
  responseBuffer = "";
@@ -7230,7 +7461,6 @@ var init_provider_cli_adapter = __esm({
7230
7461
  idleTimeout = null;
7231
7462
  ready = false;
7232
7463
  startupBuffer = "";
7233
- /** After spawn: briefly skip generating/settle so splash/redraw does not flip status; not used to gate sendMessage */
7234
7464
  startupParseGate = false;
7235
7465
  spawnAt = 0;
7236
7466
  // PTY I/O
@@ -7248,11 +7478,20 @@ var init_provider_cli_adapter = __esm({
7248
7478
  // Output settle debounce — fires after PTY output goes quiet
7249
7479
  settleTimer = null;
7250
7480
  settledBuffer = "";
7251
- // snapshot of recentOutputBuffer at settle time
7252
7481
  // Resize redraw suppression
7253
7482
  resizeSuppressUntil = 0;
7254
7483
  // Debug: status transition history
7255
7484
  statusHistory = [];
7485
+ // ─── CLI Scripts (script-based parsing) ───
7486
+ cliScripts;
7487
+ /** Full accumulated ANSI-stripped PTY output */
7488
+ accumulatedBuffer = "";
7489
+ /** Full accumulated raw PTY output (with ANSI) */
7490
+ accumulatedRawBuffer = "";
7491
+ /** Current visible terminal screen snapshot */
7492
+ terminalScreen = new TerminalScreen(40, 120);
7493
+ /** Max accumulated buffer size (last 50KB) */
7494
+ static MAX_ACCUMULATED_BUFFER = 5e4;
7256
7495
  setStatus(status, trigger) {
7257
7496
  const prev = this.currentStatus;
7258
7497
  if (prev === status) return;
@@ -7261,10 +7500,16 @@ var init_provider_cli_adapter = __esm({
7261
7500
  if (this.statusHistory.length > 50) this.statusHistory.shift();
7262
7501
  LOG.info("CLI", `[${this.cliType}] status: ${prev} \u2192 ${status}${trigger ? ` (${trigger})` : ""}`);
7263
7502
  }
7264
- // Resolved timeouts (provider defaults + overrides)
7503
+ // Resolved timeouts
7265
7504
  timeouts;
7266
- // Provider approval key mapping (e.g. { 0: '1', 1: '2', 2: '3' }) — loaded from provider.json
7505
+ // Provider approval key mapping
7267
7506
  approvalKeys;
7507
+ /** Inject CLI scripts after construction (e.g. when resolved by ProviderLoader) */
7508
+ setCliScripts(scripts) {
7509
+ this.cliScripts = scripts;
7510
+ const scriptNames = Object.keys(scripts).filter((k) => typeof scripts[k] === "function");
7511
+ LOG.info("CLI", `[${this.cliType}] CLI scripts injected: [${scriptNames.join(", ")}]`);
7512
+ }
7268
7513
  // ─── Lifecycle ─────────────────────────────────
7269
7514
  setServerConn(serverConn) {
7270
7515
  this.serverConn = serverConn;
@@ -7353,15 +7598,19 @@ var init_provider_cli_adapter = __esm({
7353
7598
  this.spawnAt = Date.now();
7354
7599
  this.startupParseGate = true;
7355
7600
  this.startupBuffer = "";
7601
+ this.terminalScreen.reset(40, 120);
7356
7602
  this.ready = true;
7357
7603
  this.setStatus("idle", "pty_ready");
7358
7604
  this.onStatusChange?.();
7359
7605
  }
7360
- // ─── Output state machine ────────────────────────────
7606
+ // ─── Output Handling ────────────────────────────
7361
7607
  handleOutput(rawData) {
7362
7608
  if (Date.now() < this.resizeSuppressUntil) return;
7609
+ this.terminalScreen.write(rawData);
7363
7610
  const cleanData = stripAnsi(rawData);
7364
- const { patterns } = this.provider;
7611
+ if (this.isWaitingForResponse && cleanData) {
7612
+ this.responseBuffer = (this.responseBuffer + cleanData).slice(-8e3);
7613
+ }
7365
7614
  if (cleanData.trim()) {
7366
7615
  if (this.serverConn) {
7367
7616
  this.serverConn.sendMessage("log", { message: cleanData.trim(), level: "info" });
@@ -7370,9 +7619,10 @@ var init_provider_cli_adapter = __esm({
7370
7619
  }
7371
7620
  }
7372
7621
  this.recentOutputBuffer = (this.recentOutputBuffer + cleanData).slice(-1e3);
7622
+ this.accumulatedBuffer = (this.accumulatedBuffer + cleanData).slice(-_ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
7623
+ this.accumulatedRawBuffer = (this.accumulatedRawBuffer + rawData).slice(-_ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
7373
7624
  if (this.startupParseGate) {
7374
7625
  this.startupBuffer += cleanData;
7375
- LOG.info("CLI", `[${this.cliType}] startup chunk (${cleanData.length} chars): ${cleanData.slice(0, 200).replace(/\n/g, "\\n")}`);
7376
7626
  const dialogPatterns = [
7377
7627
  /Do you want to connect/i,
7378
7628
  /Do you trust the files/i,
@@ -7387,60 +7637,17 @@ var init_provider_cli_adapter = __esm({
7387
7637
  }
7388
7638
  const elapsed = Date.now() - this.spawnAt;
7389
7639
  const bufCap = this.startupBuffer.length > 12e3;
7390
- const promptMatched = patterns.prompt.some((p) => p.test(this.startupBuffer));
7391
- if (promptMatched || elapsed > 8e3 || bufCap) {
7640
+ const scriptStatus = this.runDetectStatus(this.startupBuffer);
7641
+ const isReady = scriptStatus === "idle" || elapsed > 8e3 || bufCap;
7642
+ if (isReady) {
7392
7643
  this.startupParseGate = false;
7393
- if (promptMatched) {
7394
- LOG.info("CLI", `[${this.cliType}] \u2713 Startup gate end (prompt matched)`);
7395
- } else {
7396
- LOG.info("CLI", `[${this.cliType}] startup gate end (${elapsed}ms, cap=${bufCap}, prompt=${promptMatched})`);
7397
- }
7644
+ LOG.info("CLI", `[${this.cliType}] Startup gate end (${elapsed}ms, scriptStatus=${scriptStatus})`);
7398
7645
  } else {
7399
7646
  return;
7400
7647
  }
7401
7648
  }
7402
- if (cleanData.trim().length > 5) {
7403
- LOG.debug("CLI", `[${this.cliType}] output chunk (${cleanData.length}): ${cleanData.slice(0, 300).replace(/\n/g, "\\n")}`);
7404
- }
7405
- if (!this.isWaitingForResponse) {
7406
- if (patterns.generating.some((p) => p.test(cleanData))) {
7407
- if (this.settleTimer) {
7408
- clearTimeout(this.settleTimer);
7409
- this.settleTimer = null;
7410
- }
7411
- this.isWaitingForResponse = true;
7412
- this.responseBuffer = "";
7413
- this.setStatus("generating", "autonomous_gen");
7414
- this.onStatusChange?.();
7415
- }
7416
- }
7417
- if (this.isWaitingForResponse) {
7418
- this.responseBuffer += cleanData;
7419
- if (this.idleTimeout) clearTimeout(this.idleTimeout);
7420
- if (patterns.generating.some((p) => p.test(cleanData))) {
7421
- this.setStatus("generating", "still_generating");
7422
- this.idleTimeout = setTimeout(() => {
7423
- if (this.isWaitingForResponse) this.finishResponse();
7424
- }, this.timeouts.generatingIdle);
7425
- this.onStatusChange?.();
7426
- if (this.settleTimer) {
7427
- clearTimeout(this.settleTimer);
7428
- this.settleTimer = null;
7429
- }
7430
- return;
7431
- }
7432
- }
7433
- if (this.currentStatus === "waiting_approval") {
7434
- this.approvalTransitionBuffer = (this.approvalTransitionBuffer + cleanData).slice(-500);
7435
- this.scheduleSettle();
7436
- return;
7437
- }
7438
7649
  this.scheduleSettle();
7439
7650
  }
7440
- /**
7441
- * Fired after output goes quiet for outputSettle ms.
7442
- * Evaluates the stabilised buffer for approval, prompt (idle), or timeout.
7443
- */
7444
7651
  scheduleSettle() {
7445
7652
  if (this.settleTimer) clearTimeout(this.settleTimer);
7446
7653
  this.settleTimer = setTimeout(() => {
@@ -7450,59 +7657,25 @@ var init_provider_cli_adapter = __esm({
7450
7657
  }, this.timeouts.outputSettle);
7451
7658
  }
7452
7659
  evaluateSettled() {
7453
- const { patterns } = this.provider;
7454
- const buf = this.settledBuffer;
7455
- if (this.currentStatus === "waiting_approval") {
7456
- const genResume = patterns.generating.some((p) => p.test(this.approvalTransitionBuffer));
7457
- const promptResume = patterns.prompt.some((p) => p.test(this.approvalTransitionBuffer));
7458
- if (genResume) {
7459
- if (this.approvalExitTimeout) {
7460
- clearTimeout(this.approvalExitTimeout);
7461
- this.approvalExitTimeout = null;
7462
- }
7463
- this.setStatus("generating", "approval_gen_resume");
7464
- this.activeModal = null;
7465
- this.recentOutputBuffer = "";
7466
- this.approvalTransitionBuffer = "";
7467
- this.lastApprovalResolvedAt = Date.now();
7468
- this.onStatusChange?.();
7469
- } else if (promptResume) {
7470
- if (this.approvalExitTimeout) {
7471
- clearTimeout(this.approvalExitTimeout);
7472
- this.approvalExitTimeout = null;
7473
- }
7474
- this.activeModal = null;
7475
- this.recentOutputBuffer = "";
7476
- this.approvalTransitionBuffer = "";
7477
- this.lastApprovalResolvedAt = Date.now();
7478
- this.finishResponse();
7479
- }
7480
- return;
7481
- }
7482
- const hasApproval = patterns.approval.some((p) => p.test(buf));
7483
- if (hasApproval) {
7660
+ const tail = this.settledBuffer;
7661
+ const scriptStatus = this.runDetectStatus(tail);
7662
+ if (!scriptStatus) return;
7663
+ const prevStatus = this.currentStatus;
7664
+ if (scriptStatus === "waiting_approval") {
7484
7665
  const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
7485
7666
  if (!inCooldown) {
7486
- const ctxLines = buf.split("\n").map((l) => l.trim()).filter((l) => l && !/^[─═╭╮╰╯│]+$/.test(l));
7487
7667
  this.isWaitingForResponse = true;
7488
- this.setStatus("waiting_approval", "approval_pattern");
7489
- this.recentOutputBuffer = "";
7490
- this.approvalTransitionBuffer = "";
7491
- this.activeModal = {
7492
- message: ctxLines.slice(-5).join(" ").slice(0, 200) || "Approval required",
7493
- buttons: this.cliType === "claude-cli" ? ["Yes (y)", "Always allow (a)", "Deny (Esc)"] : ["Allow once", "Always allow", "Deny"]
7494
- };
7668
+ this.setStatus("waiting_approval", "script_detect");
7669
+ const modal = this.runParseApproval(tail);
7670
+ this.activeModal = modal || { message: "Approval required", buttons: ["Allow", "Deny"] };
7495
7671
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
7496
7672
  if (this.approvalExitTimeout) clearTimeout(this.approvalExitTimeout);
7497
7673
  this.approvalExitTimeout = setTimeout(() => {
7498
7674
  if (this.currentStatus === "waiting_approval") {
7499
- LOG.warn("CLI", `[${this.cliType}] Approval timeout \u2014 auto-exiting waiting_approval`);
7675
+ LOG.warn("CLI", `[${this.cliType}] Approval timeout \u2014 auto-clearing`);
7500
7676
  this.activeModal = null;
7501
7677
  this.lastApprovalResolvedAt = Date.now();
7502
- this.recentOutputBuffer = "";
7503
- this.approvalTransitionBuffer = "";
7504
- this.approvalExitTimeout = null;
7505
- this.setStatus(this.isWaitingForResponse ? "generating" : "idle", "approval_cleared");
7678
+ this.setStatus("idle", "approval_timeout");
7506
7679
  this.onStatusChange?.();
7507
7680
  }
7508
7681
  }, 6e4);
@@ -7510,19 +7683,42 @@ var init_provider_cli_adapter = __esm({
7510
7683
  return;
7511
7684
  }
7512
7685
  }
7513
- if (this.isWaitingForResponse) {
7514
- const trailingLines = buf.split("\n").slice(-3).join("\n");
7515
- if (patterns.prompt.some((p) => p.test(trailingLines)) && !hasApproval) {
7516
- this.finishResponse();
7517
- } else if (!patterns.generating.some((p) => p.test(buf))) {
7518
- if (this.idleTimeout) clearTimeout(this.idleTimeout);
7519
- this.idleTimeout = setTimeout(() => {
7520
- if (this.isWaitingForResponse && this.responseBuffer.trim()) {
7521
- this.finishResponse();
7522
- }
7523
- }, this.timeouts.idleFinish);
7686
+ if (scriptStatus === "generating") {
7687
+ if (prevStatus === "waiting_approval") {
7688
+ if (this.approvalExitTimeout) {
7689
+ clearTimeout(this.approvalExitTimeout);
7690
+ this.approvalExitTimeout = null;
7691
+ }
7692
+ this.activeModal = null;
7693
+ this.lastApprovalResolvedAt = Date.now();
7524
7694
  }
7695
+ if (!this.isWaitingForResponse) {
7696
+ this.isWaitingForResponse = true;
7697
+ this.responseBuffer = "";
7698
+ }
7699
+ this.setStatus("generating", "script_detect");
7700
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
7701
+ this.idleTimeout = setTimeout(() => {
7702
+ if (this.isWaitingForResponse) this.finishResponse();
7703
+ }, this.timeouts.generatingIdle);
7525
7704
  this.onStatusChange?.();
7705
+ return;
7706
+ }
7707
+ if (scriptStatus === "idle") {
7708
+ if (prevStatus === "waiting_approval") {
7709
+ if (this.approvalExitTimeout) {
7710
+ clearTimeout(this.approvalExitTimeout);
7711
+ this.approvalExitTimeout = null;
7712
+ }
7713
+ this.activeModal = null;
7714
+ this.lastApprovalResolvedAt = Date.now();
7715
+ }
7716
+ if (this.isWaitingForResponse) {
7717
+ this.finishResponse();
7718
+ } else if (prevStatus !== "idle") {
7719
+ this.setStatus("idle", "script_detect");
7720
+ this.onStatusChange?.();
7721
+ }
7526
7722
  }
7527
7723
  }
7528
7724
  finishResponse() {
@@ -7538,25 +7734,50 @@ var init_provider_cli_adapter = __esm({
7538
7734
  clearTimeout(this.approvalExitTimeout);
7539
7735
  this.approvalExitTimeout = null;
7540
7736
  }
7541
- const lastUserText = this.messages.filter((m) => m.role === "user").pop()?.content;
7542
- let response = this.provider.cleanOutput(this.responseBuffer, lastUserText);
7543
- if (lastUserText && response) {
7544
- const userTrimmed = lastUserText.trim();
7545
- response = response.split("\n").filter((l) => l.trim() !== userTrimmed).join("\n").trim();
7546
- }
7547
- if (response) {
7548
- this.messages.push({ role: "assistant", content: response, timestamp: Date.now() });
7549
- if (this.messages.length > 200) this.messages = this.messages.slice(-200);
7550
- LOG.info("CLI", `[${this.cliType}] Response (${response.length} chars)`);
7551
- }
7552
7737
  this.responseBuffer = "";
7553
7738
  this.isWaitingForResponse = false;
7554
7739
  this.activeModal = null;
7555
7740
  this.setStatus("idle", "response_finished");
7556
7741
  this.onStatusChange?.();
7557
7742
  }
7558
- // ─── Public API (CliAdapter interface) ──────────
7743
+ // ─── Script Execution ──────────────────────────
7744
+ runDetectStatus(text) {
7745
+ if (!this.cliScripts?.detectStatus) return null;
7746
+ try {
7747
+ return this.cliScripts.detectStatus({ tail: text.slice(-500) });
7748
+ } catch (e) {
7749
+ LOG.warn("CLI", `[${this.cliType}] detectStatus error: ${e.message}`);
7750
+ return null;
7751
+ }
7752
+ }
7753
+ runParseApproval(tail) {
7754
+ if (!this.cliScripts?.parseApproval) return null;
7755
+ try {
7756
+ return this.cliScripts.parseApproval({
7757
+ buffer: this.terminalScreen.getText() || this.accumulatedBuffer,
7758
+ rawBuffer: this.accumulatedRawBuffer,
7759
+ tail
7760
+ });
7761
+ } catch (e) {
7762
+ LOG.warn("CLI", `[${this.cliType}] parseApproval error: ${e.message}`);
7763
+ return null;
7764
+ }
7765
+ }
7766
+ // ─── Public API (CliAdapter) ───────────────────
7559
7767
  getStatus() {
7768
+ const scriptResult = this.getScriptParsedStatus();
7769
+ if (scriptResult) {
7770
+ return {
7771
+ status: this.currentStatus,
7772
+ messages: (scriptResult.messages || []).map((m) => ({
7773
+ role: m.role,
7774
+ content: m.content,
7775
+ timestamp: m.timestamp
7776
+ })),
7777
+ workingDir: this.workingDir,
7778
+ activeModal: this.activeModal
7779
+ };
7780
+ }
7560
7781
  return {
7561
7782
  status: this.currentStatus,
7562
7783
  messages: [...this.messages],
@@ -7564,11 +7785,71 @@ var init_provider_cli_adapter = __esm({
7564
7785
  activeModal: this.activeModal
7565
7786
  };
7566
7787
  }
7788
+ /**
7789
+ * Script-based full parse — returns ReadChatResult.
7790
+ * Called by command handler / dashboard for rich content rendering.
7791
+ */
7792
+ getScriptParsedStatus() {
7793
+ if (!this.cliScripts?.parseOutput) return null;
7794
+ try {
7795
+ const input = {
7796
+ buffer: this.accumulatedBuffer,
7797
+ rawBuffer: this.accumulatedRawBuffer,
7798
+ recentBuffer: this.recentOutputBuffer,
7799
+ screenText: this.terminalScreen.getText(),
7800
+ messages: [...this.structuredMessages.length > 0 ? this.structuredMessages : this.messages],
7801
+ partialResponse: this.responseBuffer
7802
+ };
7803
+ const result = this.cliScripts.parseOutput(input);
7804
+ if (result && typeof result === "object") {
7805
+ if (Array.isArray(result.messages)) {
7806
+ this.structuredMessages = result.messages.map((m) => ({
7807
+ role: m.role,
7808
+ content: m.content,
7809
+ timestamp: m.timestamp
7810
+ }));
7811
+ }
7812
+ return result;
7813
+ }
7814
+ } catch (e) {
7815
+ LOG.warn("CLI", `[${this.cliType}] parseOutput error: ${e.message}`);
7816
+ }
7817
+ return null;
7818
+ }
7819
+ /** Whether this adapter has CLI scripts loaded */
7820
+ hasCliScripts() {
7821
+ return typeof this.cliScripts?.detectStatus === "function";
7822
+ }
7823
+ /**
7824
+ * Resolves an action (like 'fix' lint error) from the dashboard.
7825
+ * Uses resolveAction script if available, otherwise falls back to standard text.
7826
+ */
7827
+ async resolveAction(data) {
7828
+ let promptText = "";
7829
+ if (this.cliScripts && typeof this.cliScripts.resolveAction === "function") {
7830
+ try {
7831
+ promptText = this.cliScripts.resolveAction(data);
7832
+ } catch (e) {
7833
+ LOG.warn("CLI", `[${this.cliType}] resolveAction error: ${e.message}`);
7834
+ }
7835
+ }
7836
+ if (!promptText && data) {
7837
+ promptText = `Please fix the following issue:
7838
+ ${data.title || ""}
7839
+ ${data.explanation || ""}
7840
+
7841
+ ${data.message || ""}`.trim();
7842
+ }
7843
+ if (promptText) {
7844
+ await this.sendMessage(promptText);
7845
+ }
7846
+ }
7567
7847
  async sendMessage(text) {
7568
7848
  if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
7569
7849
  if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
7570
7850
  if (this.isWaitingForResponse) return;
7571
7851
  this.messages.push({ role: "user", content: text, timestamp: Date.now() });
7852
+ this.structuredMessages.push({ role: "user", content: text, timestamp: Date.now() });
7572
7853
  this.isWaitingForResponse = true;
7573
7854
  this.responseBuffer = "";
7574
7855
  this.setStatus("generating", "sendMessage");
@@ -7580,8 +7861,7 @@ var init_provider_cli_adapter = __esm({
7580
7861
  }
7581
7862
  getPartialResponse() {
7582
7863
  if (!this.isWaitingForResponse) return "";
7583
- const partial2 = this.provider.cleanOutput(this.responseBuffer);
7584
- return partial2 || (this.isWaitingForResponse ? "(generating...)" : "");
7864
+ return this.responseBuffer;
7585
7865
  }
7586
7866
  cancel() {
7587
7867
  this.shutdown();
@@ -7613,6 +7893,10 @@ var init_provider_cli_adapter = __esm({
7613
7893
  }
7614
7894
  clearHistory() {
7615
7895
  this.messages = [];
7896
+ this.structuredMessages = [];
7897
+ this.accumulatedBuffer = "";
7898
+ this.accumulatedRawBuffer = "";
7899
+ this.terminalScreen.reset();
7616
7900
  this.onStatusChange?.();
7617
7901
  }
7618
7902
  isProcessing() {
@@ -7624,11 +7908,6 @@ var init_provider_cli_adapter = __esm({
7624
7908
  writeRaw(data) {
7625
7909
  this.ptyProcess?.write(data);
7626
7910
  }
7627
- /**
7628
- * Resolve an approval modal by navigating to the button at `buttonIndex` and pressing Enter.
7629
- * Index 0 = first option (already selected by default — just Enter).
7630
- * Index N = press Arrow Down N times, then Enter.
7631
- */
7632
7911
  resolveModal(buttonIndex) {
7633
7912
  if (!this.ptyProcess || this.currentStatus !== "waiting_approval") return;
7634
7913
  if (buttonIndex in this.approvalKeys) {
@@ -7643,25 +7922,13 @@ var init_provider_cli_adapter = __esm({
7643
7922
  if (this.ptyProcess) {
7644
7923
  try {
7645
7924
  this.ptyProcess.resize(cols, rows);
7925
+ this.terminalScreen.resize(rows, cols);
7646
7926
  this.resizeSuppressUntil = Date.now() + 300;
7647
7927
  } catch {
7648
7928
  }
7649
7929
  }
7650
7930
  }
7651
- /**
7652
- * Full debug state — exposes all internal buffers, status, and patterns for debugging.
7653
- * Used by DevServer /api/cli/debug endpoint.
7654
- */
7655
7931
  getDebugState() {
7656
- const sb = this.startupBuffer;
7657
- const testOnStartup = (p) => {
7658
- const flags = p.flags.includes("g") ? p.flags.replace(/g/g, "") : p.flags;
7659
- return new RegExp(p.source, flags).test(sb);
7660
- };
7661
- const promptDiagnostics = this.provider.patterns.prompt.map((p) => ({
7662
- pattern: p.toString(),
7663
- matchedAgainstStartupBuffer: testOnStartup(p)
7664
- }));
7665
7932
  return {
7666
7933
  type: this.cliType,
7667
7934
  name: this.cliName,
@@ -7671,32 +7938,20 @@ var init_provider_cli_adapter = __esm({
7671
7938
  spawnAt: this.spawnAt,
7672
7939
  workingDir: this.workingDir,
7673
7940
  messages: this.messages.slice(-20),
7941
+ structuredMessages: this.structuredMessages.slice(-20),
7674
7942
  messageCount: this.messages.length,
7675
- // Buffers (longer tails here than in periodic logs — for matching provider.json patterns)
7676
- startupBuffer: sb.slice(-4e3),
7677
- startupBufferLength: sb.length,
7678
- promptDiagnostics,
7943
+ startupBuffer: this.startupBuffer.slice(-4e3),
7679
7944
  recentOutputBuffer: this.recentOutputBuffer.slice(-500),
7680
7945
  settledBuffer: this.settledBuffer.slice(-500),
7681
- responseBuffer: this.responseBuffer.slice(-500),
7682
- approvalTransitionBuffer: this.approvalTransitionBuffer.slice(-500),
7683
- // State
7946
+ accumulatedBufferLength: this.accumulatedBuffer.length,
7684
7947
  isWaitingForResponse: this.isWaitingForResponse,
7685
7948
  activeModal: this.activeModal,
7686
7949
  lastApprovalResolvedAt: this.lastApprovalResolvedAt,
7687
7950
  resizeSuppressUntil: this.resizeSuppressUntil,
7688
- // Provider patterns (serialized)
7689
- patterns: {
7690
- prompt: this.provider.patterns.prompt.map((p) => p.toString()),
7691
- generating: this.provider.patterns.generating.map((p) => p.toString()),
7692
- approval: this.provider.patterns.approval.map((p) => p.toString()),
7693
- ready: this.provider.patterns.ready.map((p) => p.toString())
7694
- },
7695
- // Status history
7951
+ hasCliScripts: this.hasCliScripts(),
7952
+ scriptNames: Object.keys(this.cliScripts).filter((k) => typeof this.cliScripts[k] === "function"),
7696
7953
  statusHistory: this.statusHistory.slice(-30),
7697
- // Timeouts config
7698
7954
  timeouts: this.timeouts,
7699
- // PTY alive
7700
7955
  ptyAlive: !!this.ptyProcess
7701
7956
  };
7702
7957
  }
@@ -7762,21 +8017,27 @@ var init_cli_provider_instance = __esm({
7762
8017
  async onTick() {
7763
8018
  }
7764
8019
  getState() {
7765
- const adapterStatus = this.adapter.getStatus();
8020
+ const rawStatus = this.adapter.getStatus();
8021
+ const parsedStatus = this.adapter.getScriptParsedStatus();
8022
+ const adapterStatus = parsedStatus ? {
8023
+ ...rawStatus,
8024
+ messages: parsedStatus.messages || rawStatus.messages,
8025
+ activeModal: parsedStatus.activeModal || rawStatus.activeModal
8026
+ } : rawStatus;
7766
8027
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
7767
- const recentMessages = adapterStatus.messages.slice(-50).map((m) => ({
7768
- role: m.role,
7769
- content: m.content.length > 2e3 ? m.content.slice(0, 2e3) + "\n... (truncated)" : m.content,
7770
- timestamp: m.timestamp
7771
- }));
8028
+ const recentMessages = adapterStatus.messages.slice(-50).map((m) => {
8029
+ const content = typeof m.content === "string" && m.content.length > 8e3 ? m.content.slice(0, 8e3) + "\n... (truncated)" : m.content;
8030
+ return { ...m, content };
8031
+ });
7772
8032
  const partial2 = this.adapter.getPartialResponse();
7773
8033
  if (adapterStatus.status === "generating" && partial2) {
7774
8034
  const cleaned = partial2.trim();
7775
8035
  if (cleaned && cleaned !== "(generating...)") {
7776
8036
  recentMessages.push({
7777
8037
  role: "assistant",
7778
- content: (cleaned.length > 2e3 ? cleaned.slice(0, 2e3) + "..." : cleaned) + "...",
7779
- timestamp: Date.now()
8038
+ content: (cleaned.length > 8e3 ? cleaned.slice(0, 8e3) + "..." : cleaned) + "...",
8039
+ timestamp: Date.now(),
8040
+ meta: { streaming: true }
7780
8041
  });
7781
8042
  }
7782
8043
  }
@@ -7815,6 +8076,8 @@ var init_cli_provider_instance = __esm({
7815
8076
  this.adapter.sendMessage(data.text);
7816
8077
  } else if (event === "server_connected" && data?.serverConn) {
7817
8078
  this.adapter.setServerConn(data.serverConn);
8079
+ } else if (event === "resolve_action" && data) {
8080
+ this.adapter.resolveAction(data);
7818
8081
  }
7819
8082
  }
7820
8083
  dispose() {
@@ -25201,7 +25464,8 @@ var init_cli_manager = __esm({
25201
25464
  const provider = this.providerLoader.getMeta(normalizedType);
25202
25465
  if (provider && provider.category === "cli" && provider.patterns && provider.spawn) {
25203
25466
  console.log(import_chalk.default.cyan(` \u{1F4E6} Using provider: ${provider.name} (${provider.type})`));
25204
- return new ProviderCliAdapter(provider, workingDir, cliArgs);
25467
+ const resolvedProvider = this.providerLoader.resolve(normalizedType) || provider;
25468
+ return new ProviderCliAdapter(resolvedProvider, workingDir, cliArgs);
25205
25469
  }
25206
25470
  throw new Error(`No CLI provider found for '${cliType}'. Create a provider.js in providers/cli/${cliType}/`);
25207
25471
  }
@@ -25286,7 +25550,8 @@ ${installInfo}`
25286
25550
  }
25287
25551
  const instanceManager = this.deps.getInstanceManager();
25288
25552
  if (provider && instanceManager) {
25289
- const cliInstance = new CliProviderInstance(provider, resolvedDir, cliArgs, key);
25553
+ const resolvedProvider = this.providerLoader.resolve(cliType, { version: cliInfo.version }) || provider;
25554
+ const cliInstance = new CliProviderInstance(resolvedProvider, resolvedDir, cliArgs, key);
25290
25555
  try {
25291
25556
  await instanceManager.addInstance(key, cliInstance, {
25292
25557
  serverConn: this.deps.getServerConn(),
@@ -28550,6 +28815,46 @@ var init_dev_server = __esm({
28550
28815
  }
28551
28816
  }
28552
28817
  // ─── Phase 2: Auto-Implement Backend ───
28818
+ getDefaultAutoImplReference(category, type) {
28819
+ if (category === "cli") {
28820
+ return type === "codex-cli" ? "claude-cli" : "codex-cli";
28821
+ }
28822
+ return "antigravity";
28823
+ }
28824
+ resolveAutoImplReference(category, requestedReference, targetType) {
28825
+ const desired = requestedReference || this.getDefaultAutoImplReference(category, targetType);
28826
+ const ref = this.providerLoader.resolve(desired) || this.providerLoader.getMeta(desired);
28827
+ if (ref?.category === category) return desired;
28828
+ const all = this.providerLoader.getAll();
28829
+ const fallback = all.find((p) => p.category === category && p.type !== targetType);
28830
+ return fallback?.type || null;
28831
+ }
28832
+ loadAutoImplReferenceScripts(category, referenceType) {
28833
+ if (!referenceType) return {};
28834
+ const builtinDir = this.providerLoader.getPrimaryBuiltinDir();
28835
+ const refDir = path12.join(builtinDir, category, referenceType);
28836
+ if (!fs9.existsSync(refDir)) return {};
28837
+ const referenceScripts = {};
28838
+ const scriptsDir = path12.join(refDir, "scripts");
28839
+ if (!fs9.existsSync(scriptsDir)) return referenceScripts;
28840
+ const versions = fs9.readdirSync(scriptsDir).filter((d) => {
28841
+ try {
28842
+ return fs9.statSync(path12.join(scriptsDir, d)).isDirectory();
28843
+ } catch {
28844
+ return false;
28845
+ }
28846
+ }).sort().reverse();
28847
+ if (versions.length === 0) return referenceScripts;
28848
+ const latestDir = path12.join(scriptsDir, versions[0]);
28849
+ for (const file2 of fs9.readdirSync(latestDir)) {
28850
+ if (!file2.endsWith(".js")) continue;
28851
+ try {
28852
+ referenceScripts[file2] = fs9.readFileSync(path12.join(latestDir, file2), "utf-8");
28853
+ } catch {
28854
+ }
28855
+ }
28856
+ return referenceScripts;
28857
+ }
28553
28858
  async handleAutoImplement(type, req, res) {
28554
28859
  const body = await this.readBody(req);
28555
28860
  const { agent = "claude-cli", functions, reference = "antigravity", model, comment } = body;
@@ -28572,36 +28877,26 @@ var init_dev_server = __esm({
28572
28877
  return;
28573
28878
  }
28574
28879
  try {
28575
- this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "analyzing", message: "\uC5D0\uC774\uC804\uD2B8 \uCD08\uAE30\uD654 (DOM \uD0D0\uC0C9 \uAD8C\uD55C \uBD80\uC5EC)..." } });
28880
+ const resolvedReference = this.resolveAutoImplReference(provider.category, reference, type);
28881
+ this.sendAutoImplSSE({
28882
+ event: "progress",
28883
+ data: {
28884
+ function: "_init",
28885
+ status: "analyzing",
28886
+ message: provider.category === "cli" ? "Initializing agent (granting CLI PTY debug access)..." : "Initializing agent (granting DOM access)..."
28887
+ }
28888
+ });
28576
28889
  const domContext = null;
28577
- this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "loading_reference", message: `\uB808\uD37C\uB7F0\uC2A4 \uC2A4\uD06C\uB9BD\uD2B8 \uB85C\uB4DC \uC911 (${reference})...` } });
28578
- let referenceScripts = {};
28579
- const builtinDir = this.providerLoader.getPrimaryBuiltinDir();
28580
- const refDir = path12.join(builtinDir, "ide", reference);
28581
- if (fs9.existsSync(refDir)) {
28582
- const scriptsDir = path12.join(refDir, "scripts");
28583
- if (fs9.existsSync(scriptsDir)) {
28584
- const versions = fs9.readdirSync(scriptsDir).filter((d) => {
28585
- try {
28586
- return fs9.statSync(path12.join(scriptsDir, d)).isDirectory();
28587
- } catch {
28588
- return false;
28589
- }
28590
- }).sort().reverse();
28591
- if (versions.length > 0) {
28592
- const latestDir = path12.join(scriptsDir, versions[0]);
28593
- for (const file2 of fs9.readdirSync(latestDir)) {
28594
- if (file2.endsWith(".js")) {
28595
- try {
28596
- referenceScripts[file2] = fs9.readFileSync(path12.join(latestDir, file2), "utf-8");
28597
- } catch {
28598
- }
28599
- }
28600
- }
28601
- }
28890
+ this.sendAutoImplSSE({
28891
+ event: "progress",
28892
+ data: {
28893
+ function: "_init",
28894
+ status: "loading_reference",
28895
+ message: `Loading reference script (${resolvedReference || "none"})...`
28602
28896
  }
28603
- }
28604
- const prompt = this.buildAutoImplPrompt(type, provider, providerDir, functions, domContext, referenceScripts, comment);
28897
+ });
28898
+ const referenceScripts = this.loadAutoImplReferenceScripts(provider.category, resolvedReference);
28899
+ const prompt = this.buildAutoImplPrompt(type, provider, providerDir, functions, domContext, referenceScripts, comment, resolvedReference);
28605
28900
  const tmpDir = path12.join(os14.tmpdir(), "adhdev-autoimpl");
28606
28901
  if (!fs9.existsSync(tmpDir)) fs9.mkdirSync(tmpDir, { recursive: true });
28607
28902
  const promptFile = path12.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
@@ -28619,7 +28914,7 @@ var init_dev_server = __esm({
28619
28914
  }
28620
28915
  const agentCategory = agentProvider?.category;
28621
28916
  if (agentCategory === "acp") {
28622
- this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "spawning", message: `ACP \uC5D0\uC774\uC804\uD2B8 \uC2E4\uD589 \uC911: ${spawn3.command} ${(spawn3.args || []).join(" ")}` } });
28917
+ this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "spawning", message: `Spawning ACP agent: ${spawn3.command} ${(spawn3.args || []).join(" ")}` } });
28623
28918
  this.autoImplStatus = { running: true, type, progress: [] };
28624
28919
  const { ClientSideConnection: ClientSideConnection2, ndJsonStream: ndJsonStream2, PROTOCOL_VERSION: PROTOCOL_VERSION2 } = await Promise.resolve().then(() => (init_acp(), acp_exports));
28625
28920
  const { Readable: Readable2, Writable: Writable2 } = await import("stream");
@@ -28706,7 +29001,7 @@ var init_dev_server = __esm({
28706
29001
  this.autoImplProcess = null;
28707
29002
  this.autoImplStatus.running = false;
28708
29003
  const success2 = code === 0;
28709
- this.sendAutoImplSSE({ event: "complete", data: { success: success2, exitCode: code, functions, message: success2 ? "\u2705 ACP Auto-implement \uC644\uB8CC" : `\u274C ACP \uC5D0\uC774\uC804\uD2B8 \uC885\uB8CC (code: ${code})` } });
29004
+ this.sendAutoImplSSE({ event: "complete", data: { success: success2, exitCode: code, functions, message: success2 ? "\u2705 ACP Auto-implement complete" : `\u274C ACP agent exited (code: ${code})` } });
28710
29005
  try {
28711
29006
  this.providerLoader.reload();
28712
29007
  } catch {
@@ -28721,16 +29016,16 @@ var init_dev_server = __esm({
28721
29016
  try {
28722
29017
  this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "initializing", message: "ACP initialize..." } });
28723
29018
  await connection.initialize({ protocolVersion: PROTOCOL_VERSION2, clientCapabilities: {} });
28724
- this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "session", message: "ACP session \uC0DD\uC131 \uC911..." } });
29019
+ this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "session", message: "Creating ACP session..." } });
28725
29020
  const session = await connection.newSession({ cwd: providerDir, mcpServers: [] });
28726
29021
  const sessionId = session?.sessionId;
28727
29022
  if (!sessionId) throw new Error("No sessionId returned from session/new");
28728
- this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "prompting", message: `\uD504\uB86C\uD504\uD2B8 \uC804\uC1A1 \uC911 (${prompt.length} chars)...` } });
29023
+ this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "prompting", message: `Sending prompt (${prompt.length} chars)...` } });
28729
29024
  await connection.prompt({
28730
29025
  sessionId,
28731
29026
  prompt: [{ type: "text", text: prompt }]
28732
29027
  });
28733
- this.sendAutoImplSSE({ event: "progress", data: { function: "_done", status: "complete", message: "\u2705 ACP \uD504\uB86C\uD504\uD2B8 \uCC98\uB9AC \uC644\uB8CC" } });
29028
+ this.sendAutoImplSSE({ event: "progress", data: { function: "_done", status: "complete", message: "\u2705 ACP prompt processing complete" } });
28734
29029
  } catch (e) {
28735
29030
  this.sendAutoImplSSE({ event: "output", data: { chunk: `[ACP Error] ${e.message}
28736
29031
  `, stream: "stderr" } });
@@ -28782,7 +29077,7 @@ var init_dev_server = __esm({
28782
29077
  const escapedArgs = baseArgs.map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(" ");
28783
29078
  shellCmd = `cat '${promptFile}' | ${command} ${escapedArgs}`;
28784
29079
  }
28785
- this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "spawning", message: `\uC5D0\uC774\uC804\uD2B8 \uC2E4\uD589 \uC911: ${shellCmd.substring(0, 200)}... (prompt: ${prompt.length} chars)` } });
29080
+ this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "spawning", message: `Spawning agent: ${shellCmd.substring(0, 200)}... (prompt: ${prompt.length} chars)` } });
28786
29081
  this.autoImplStatus = { running: true, type, progress: [] };
28787
29082
  const spawnedAt = Date.now();
28788
29083
  let child;
@@ -28878,7 +29173,7 @@ var init_dev_server = __esm({
28878
29173
  const success2 = code === 0;
28879
29174
  this.sendAutoImplSSE({
28880
29175
  event: "complete",
28881
- data: { success: success2, exitCode: code, functions, message: success2 ? "\u2705 Auto-implement \uC644\uB8CC" : `\u274C \uC5D0\uC774\uC804\uD2B8 \uC885\uB8CC (code: ${code})` }
29176
+ data: { success: success2, exitCode: code, functions, message: success2 ? "\u2705 Auto-implement complete" : `\u274C Agent exited (code: ${code})` }
28882
29177
  });
28883
29178
  try {
28884
29179
  this.providerLoader.reload();
@@ -28913,7 +29208,7 @@ var init_dev_server = __esm({
28913
29208
  success: success2,
28914
29209
  exitCode: code,
28915
29210
  functions,
28916
- message: success2 ? "\u2705 Auto-implement \uC644\uB8CC" : `\u274C \uC5D0\uC774\uC804\uD2B8 \uC885\uB8CC (code: ${code})`
29211
+ message: success2 ? "\u2705 Auto-implement complete" : `\u274C Agent exited (code: ${code})`
28917
29212
  }
28918
29213
  });
28919
29214
  try {
@@ -28941,7 +29236,10 @@ var init_dev_server = __esm({
28941
29236
  this.json(res, 500, { error: `Auto-implement failed: ${e.message}` });
28942
29237
  }
28943
29238
  }
28944
- buildAutoImplPrompt(type, provider, providerDir, functions, domContext, referenceScripts, userComment) {
29239
+ buildAutoImplPrompt(type, provider, providerDir, functions, domContext, referenceScripts, userComment, referenceType) {
29240
+ if (provider.category === "cli") {
29241
+ return this.buildCliAutoImplPrompt(type, provider, providerDir, functions, referenceScripts, userComment, referenceType);
29242
+ }
28945
29243
  const lines = [];
28946
29244
  lines.push("You are implementing browser automation scripts for an IDE provider.");
28947
29245
  lines.push("Be concise. Do NOT explain your reasoning. Just edit files directly.");
@@ -29004,7 +29302,7 @@ var init_dev_server = __esm({
29004
29302
  setMode: "set_mode.js"
29005
29303
  };
29006
29304
  if (Object.keys(referenceScripts).length > 0) {
29007
- lines.push("## Reference Implementation (from Antigravity provider)");
29305
+ lines.push(`## Reference Implementation (from ${referenceType || "antigravity"} provider)`);
29008
29306
  lines.push("These are WORKING scripts from another IDE. Adapt the PATTERNS (not selectors) for the target IDE.");
29009
29307
  lines.push("");
29010
29308
  for (const fn of functions) {
@@ -29153,6 +29451,150 @@ var init_dev_server = __esm({
29153
29451
  lines.push("Start NOW. Do not ask for permission. Explore the DOM -> Code -> Test.");
29154
29452
  return lines.join("\n");
29155
29453
  }
29454
+ buildCliAutoImplPrompt(type, provider, providerDir, functions, referenceScripts, userComment, referenceType) {
29455
+ const lines = [];
29456
+ lines.push("You are implementing PTY parsing scripts for a CLI provider.");
29457
+ lines.push("Be concise. Do NOT explain your reasoning. Edit files directly and verify with the local DevServer.");
29458
+ lines.push("");
29459
+ lines.push(`# Target: ${provider.name || type} (${type})`);
29460
+ lines.push(`Provider directory: \`${providerDir}\``);
29461
+ lines.push("Provider category: `cli`");
29462
+ lines.push("");
29463
+ lines.push("## Current Target Files");
29464
+ lines.push("These are the files you need to edit. Replace TODO or heuristic-only logic with working PTY-aware implementations.");
29465
+ lines.push("");
29466
+ const scriptsDir = path12.join(providerDir, "scripts");
29467
+ if (fs9.existsSync(scriptsDir)) {
29468
+ const versions = fs9.readdirSync(scriptsDir).filter((d) => {
29469
+ try {
29470
+ return fs9.statSync(path12.join(scriptsDir, d)).isDirectory();
29471
+ } catch {
29472
+ return false;
29473
+ }
29474
+ }).sort().reverse();
29475
+ if (versions.length > 0) {
29476
+ const vDir = path12.join(scriptsDir, versions[0]);
29477
+ lines.push(`Scripts version directory: \`${vDir}\``);
29478
+ lines.push("");
29479
+ for (const file2 of fs9.readdirSync(vDir)) {
29480
+ if (!file2.endsWith(".js")) continue;
29481
+ try {
29482
+ const content = fs9.readFileSync(path12.join(vDir, file2), "utf-8");
29483
+ lines.push(`### \`${file2}\``);
29484
+ lines.push("```javascript");
29485
+ lines.push(content);
29486
+ lines.push("```");
29487
+ lines.push("");
29488
+ } catch {
29489
+ }
29490
+ }
29491
+ }
29492
+ }
29493
+ const funcToFile = {
29494
+ parseOutput: "parse_output.js",
29495
+ detectStatus: "detect_status.js",
29496
+ parseApproval: "parse_approval.js"
29497
+ };
29498
+ if (Object.keys(referenceScripts).length > 0) {
29499
+ lines.push(`## Reference Implementation (from ${referenceType || "another CLI"} provider)`);
29500
+ lines.push("These are working CLI PTY parser scripts. Reuse the parsing shape and runtime contract, but adapt to the target CLI screen.");
29501
+ lines.push("");
29502
+ for (const fn of functions) {
29503
+ const fileName = funcToFile[fn];
29504
+ if (fileName && referenceScripts[fileName]) {
29505
+ lines.push(`### ${fn} \u2192 \`${fileName}\``);
29506
+ lines.push("```javascript");
29507
+ lines.push(referenceScripts[fileName]);
29508
+ lines.push("```");
29509
+ lines.push("");
29510
+ }
29511
+ }
29512
+ if (referenceScripts["scripts.js"]) {
29513
+ lines.push("### Router \u2192 `scripts.js`");
29514
+ lines.push("```javascript");
29515
+ lines.push(referenceScripts["scripts.js"]);
29516
+ lines.push("```");
29517
+ lines.push("");
29518
+ }
29519
+ }
29520
+ lines.push("## Runtime Contract");
29521
+ lines.push("The daemon runtime is already implemented in `packages/daemon-core/src/cli-adapters/provider-cli-adapter.ts`.");
29522
+ lines.push("Your scripts receive PTY-derived input and must return plain JS objects.");
29523
+ lines.push("");
29524
+ lines.push("| Function | Input | Return |");
29525
+ lines.push("|---|---|---|");
29526
+ lines.push("| `parseOutput` | `{ buffer, rawBuffer, recentBuffer, screenText, messages, partialResponse }` | `{ id, status, title, messages, activeModal }` |");
29527
+ lines.push("| `detectStatus` | `{ tail }` | `idle`, `generating`, `waiting_approval`, or `error` |");
29528
+ lines.push("| `parseApproval` | `{ buffer, rawBuffer, tail }` | `{ message, buttons }` or `null` |");
29529
+ lines.push("");
29530
+ lines.push("## Rules");
29531
+ lines.push("1. These scripts run in Node.js CommonJS, not in the browser. Do NOT use DOM APIs.");
29532
+ lines.push("2. Prefer `screenText` for current visible UI state. That is the PTY equivalent of parsing the current IDE DOM.");
29533
+ lines.push("3. Use `messages` as prior transcript state so redraws do not duplicate old turns on every parse.");
29534
+ lines.push("4. Use `partialResponse` for the actively streaming assistant text when status is `generating`.");
29535
+ lines.push("5. `detectStatus` must stay lightweight and tail-based. Do not scan the entire history there.");
29536
+ lines.push("6. `parseApproval` should understand the live approval area and return clean button labels.");
29537
+ lines.push("7. Use `rawBuffer` only when ANSI/control-sequence artifacts matter. Do not depend on raw escape noise unless necessary.");
29538
+ lines.push("8. Keep exports compatible with the existing `scripts.js` router (`module.exports = function ...`).");
29539
+ lines.push("9. Do not rewrite unrelated provider config. Only touch the scripts needed for this task unless a tiny supporting change is required.");
29540
+ lines.push("");
29541
+ lines.push("## Task");
29542
+ lines.push(`Edit files in \`${providerDir}\` to implement: **${functions.join(", ")}**`);
29543
+ lines.push("");
29544
+ lines.push("## Verification API");
29545
+ lines.push("Use the DevServer CLI debug endpoints, not DOM/CDP routes.");
29546
+ lines.push("");
29547
+ lines.push("### 1. Launch the target CLI");
29548
+ lines.push("```bash");
29549
+ lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/launch \\`);
29550
+ lines.push(' -H "Content-Type: application/json" \\');
29551
+ lines.push(` -d '{"type":"${type}","workingDir":"${providerDir.replace(/\\/g, "\\\\")}"}'`);
29552
+ lines.push("```");
29553
+ lines.push("");
29554
+ lines.push("### 2. Inspect parsed + raw adapter state");
29555
+ lines.push("```bash");
29556
+ lines.push(`curl -sS http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/debug/${type}`);
29557
+ lines.push(`curl -sS http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/status`);
29558
+ lines.push("```");
29559
+ lines.push("");
29560
+ lines.push("### 3. Send a rich test prompt");
29561
+ lines.push("```bash");
29562
+ lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/send \\`);
29563
+ lines.push(' -H "Content-Type: application/json" \\');
29564
+ lines.push(` -d '{"type":"${type}","text":"Write a short python snippet, include a markdown table, and briefly explain what you did."}'`);
29565
+ lines.push("```");
29566
+ lines.push("");
29567
+ lines.push("### 4. If approval appears, resolve it");
29568
+ lines.push("```bash");
29569
+ lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/resolve \\`);
29570
+ lines.push(' -H "Content-Type: application/json" \\');
29571
+ lines.push(` -d '{"type":"${type}","buttonIndex":0}'`);
29572
+ lines.push("```");
29573
+ lines.push("");
29574
+ lines.push("### 5. Stop the CLI when finished");
29575
+ lines.push("```bash");
29576
+ lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/stop \\`);
29577
+ lines.push(' -H "Content-Type: application/json" \\');
29578
+ lines.push(` -d '{"type":"${type}"}'`);
29579
+ lines.push("```");
29580
+ lines.push("");
29581
+ lines.push("## Required Validation");
29582
+ lines.push("1. Confirm `detectStatus` changes sensibly between startup, generating, approval, and idle.");
29583
+ lines.push("2. Confirm `parseOutput` produces a stable transcript without duplicating past turns when the PTY redraws.");
29584
+ lines.push("3. Confirm the latest assistant message streams through `partialResponse` while generation is in progress.");
29585
+ lines.push("4. Confirm approval parsing returns meaningful button labels when the CLI requests permission.");
29586
+ lines.push("5. Re-run the debug endpoints after edits. Do NOT finish until the parsed result looks correct.");
29587
+ lines.push("");
29588
+ if (userComment) {
29589
+ lines.push("## \u26A0\uFE0F User Instructions (HIGH PRIORITY)");
29590
+ lines.push("The user has provided the following additional instructions. Follow them strictly:");
29591
+ lines.push("");
29592
+ lines.push(userComment);
29593
+ lines.push("");
29594
+ }
29595
+ lines.push("Start NOW. Launch the CLI, inspect PTY state, edit the scripts, and verify via the CLI debug endpoints.");
29596
+ return lines.join("\n");
29597
+ }
29156
29598
  handleAutoImplSSE(type, req, res) {
29157
29599
  res.writeHead(200, {
29158
29600
  "Content-Type": "text/event-stream",
@@ -29180,7 +29622,7 @@ data: ${JSON.stringify(p.data)}
29180
29622
  setTimeout(() => {
29181
29623
  if (this.autoImplProcess) this.autoImplProcess.kill("SIGKILL");
29182
29624
  }, 3e3);
29183
- this.sendAutoImplSSE({ event: "complete", data: { success: false, exitCode: -1, message: "\u26D4 \uC0AC\uC6A9\uC790\uC5D0 \uC758\uD574 \uC911\uB2E8\uB428" } });
29625
+ this.sendAutoImplSSE({ event: "complete", data: { success: false, exitCode: -1, message: "\u26D4 Aborted by user" } });
29184
29626
  this.autoImplProcess = null;
29185
29627
  this.autoImplStatus.running = false;
29186
29628
  this.json(res, 200, { cancelled: true });
@@ -30749,11 +31191,12 @@ ${e?.stack || ""}`);
30749
31191
  });
30750
31192
 
30751
31193
  // src/screenshot-controller.ts
30752
- var ScreenshotController;
31194
+ var import_sharp, ScreenshotController;
30753
31195
  var init_screenshot_controller = __esm({
30754
31196
  "src/screenshot-controller.ts"() {
30755
31197
  "use strict";
30756
31198
  init_src();
31199
+ import_sharp = __toESM(require("sharp"));
30757
31200
  ScreenshotController = class _ScreenshotController {
30758
31201
  deps;
30759
31202
  timer = null;
@@ -30780,12 +31223,16 @@ var init_screenshot_controller = __esm({
30780
31223
  this.profileDirect = {
30781
31224
  minInterval: Math.max(300, planMinIntervalMs),
30782
31225
  maxInterval: Math.max(2e3, planMinIntervalMs),
30783
- quality: 25
31226
+ quality: 25,
31227
+ maxLongEdge: 1728,
31228
+ firstFrameLongEdge: 1920
30784
31229
  };
30785
31230
  this.profileRelay = {
30786
31231
  minInterval: Math.max(700, planMinIntervalMs),
30787
31232
  maxInterval: Math.max(3e3, planMinIntervalMs),
30788
- quality: 12
31233
+ quality: 12,
31234
+ maxLongEdge: 1152,
31235
+ firstFrameLongEdge: 1280
30789
31236
  };
30790
31237
  this.currentInterval = this.profileDirect.maxInterval;
30791
31238
  this.dailyBudgetMinutes = planLimits?.dailyScreenshotMinutes ?? -1;
@@ -30858,6 +31305,7 @@ var init_screenshot_controller = __esm({
30858
31305
  const sizeMatch = buf.length === this.lastSize;
30859
31306
  const hashMatch = hash2 === this.lastHash;
30860
31307
  const anyNeedsFirstFrame = this.deps.hasAnyNeedingFirstFrame();
31308
+ const resizeTarget = anyNeedsFirstFrame ? profile.firstFrameLongEdge : profile.maxLongEdge;
30861
31309
  if (sizeMatch && hashMatch && !anyNeedsFirstFrame) {
30862
31310
  this.staticFrameCount++;
30863
31311
  if (this.staticFrameCount >= this.STATIC_THRESHOLD) {
@@ -30867,13 +31315,14 @@ var init_screenshot_controller = __esm({
30867
31315
  LOG.debug("Screenshot", `skip (unchanged, static=${this.staticFrameCount}, interval=${this.currentInterval}ms, ${isRelay ? "RELAY" : "DIRECT"})`);
30868
31316
  }
30869
31317
  } else {
31318
+ const normalizedBuf = await this.normalizeBuffer(buf, resizeTarget, profile.quality);
30870
31319
  this.lastSize = buf.length;
30871
31320
  this.lastHash = hash2;
30872
31321
  this.staticFrameCount = 0;
30873
31322
  this.currentInterval = profile.minInterval;
30874
- const sent = this.deps.sendScreenshotBuffer(buf);
31323
+ const sent = this.deps.sendScreenshotBuffer(normalizedBuf);
30875
31324
  if (this.debugCount <= 3 || anyNeedsFirstFrame) {
30876
- LOG.debug("Screenshot", `sent: ${buf.length} bytes, delivered=${sent}, interval=${this.currentInterval}ms, ${isRelay ? "RELAY" : "DIRECT"}${anyNeedsFirstFrame ? " (first-frame)" : ""}`);
31325
+ LOG.debug("Screenshot", `sent: ${normalizedBuf.length} bytes, delivered=${sent}, interval=${this.currentInterval}ms, ${isRelay ? "RELAY" : "DIRECT"}${anyNeedsFirstFrame ? " (first-frame)" : ""}`);
30877
31326
  }
30878
31327
  }
30879
31328
  } else {
@@ -30921,6 +31370,28 @@ var init_screenshot_controller = __esm({
30921
31370
  }
30922
31371
  return h;
30923
31372
  }
31373
+ /** Normalize screenshot resolution before transport so UX feels consistent across machines/DPI. */
31374
+ async normalizeBuffer(buf, maxLongEdge, quality) {
31375
+ try {
31376
+ const image = (0, import_sharp.default)(buf, { failOn: "none" });
31377
+ const meta3 = await image.metadata();
31378
+ const width = meta3.width || 0;
31379
+ const height = meta3.height || 0;
31380
+ if (!width || !height) return buf;
31381
+ const longEdge = Math.max(width, height);
31382
+ if (longEdge <= maxLongEdge) return buf;
31383
+ return await image.resize({
31384
+ width: width >= height ? maxLongEdge : void 0,
31385
+ height: height > width ? maxLongEdge : void 0,
31386
+ fit: "inside",
31387
+ withoutEnlargement: true,
31388
+ kernel: import_sharp.default.kernel.lanczos3
31389
+ }).webp({ quality, effort: 4 }).toBuffer();
31390
+ } catch (e) {
31391
+ LOG.debug("Screenshot", `normalize skipped: ${e?.message || e}`);
31392
+ return buf;
31393
+ }
31394
+ }
30924
31395
  };
30925
31396
  }
30926
31397
  });
@@ -30983,7 +31454,7 @@ var init_adhdev_daemon = __esm({
30983
31454
  fs11 = __toESM(require("fs"));
30984
31455
  path14 = __toESM(require("path"));
30985
31456
  import_chalk2 = __toESM(require("chalk"));
30986
- pkgVersion = "0.6.52";
31457
+ pkgVersion = "0.6.55";
30987
31458
  if (pkgVersion === "unknown") {
30988
31459
  try {
30989
31460
  const possiblePaths = [