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/cli/index.js CHANGED
@@ -3341,6 +3341,9 @@ var init_builders = __esm({
3341
3341
  });
3342
3342
 
3343
3343
  // ../daemon-core/src/commands/chat-commands.ts
3344
+ function getTargetedCliAdapter(h, args, providerType) {
3345
+ return h.getCliAdapter(args?._targetInstance || h.currentIdeType || providerType);
3346
+ }
3344
3347
  async function handleChatHistory(h, args) {
3345
3348
  const { agentType, offset, limit, instanceId } = args;
3346
3349
  try {
@@ -3353,10 +3356,10 @@ async function handleChatHistory(h, args) {
3353
3356
  }
3354
3357
  }
3355
3358
  async function handleReadChat(h, args) {
3356
- const provider = h.getProvider();
3359
+ const provider = h.getProvider(args?.agentType);
3357
3360
  const _log = (msg) => LOG.debug("Command", `[read_chat] ${msg}`);
3358
3361
  if (provider?.category === "cli" || provider?.category === "acp") {
3359
- const adapter = h.getCliAdapter(provider.type);
3362
+ const adapter = getTargetedCliAdapter(h, args, provider.type);
3360
3363
  if (adapter) {
3361
3364
  _log(`${provider.category} adapter: ${adapter.cliType}`);
3362
3365
  const status = adapter.getStatus?.();
@@ -3472,7 +3475,7 @@ async function handleSendChat(h, args) {
3472
3475
  const text = args?.text || args?.message;
3473
3476
  if (!text) return { success: false, error: "text required" };
3474
3477
  const _log = (msg) => LOG.debug("Command", `[send_chat] ${msg}`);
3475
- const provider = h.getProvider();
3478
+ const provider = h.getProvider(args?.agentType);
3476
3479
  const _logSendSuccess = (method, targetAgent) => {
3477
3480
  h.historyWriter.appendNewMessages(
3478
3481
  targetAgent || provider?.type || h.currentIdeType || "unknown_agent",
@@ -3484,7 +3487,7 @@ async function handleSendChat(h, args) {
3484
3487
  return { success: true, sent: true, method, targetAgent };
3485
3488
  };
3486
3489
  if (provider?.category === "cli" || provider?.category === "acp") {
3487
- const adapter = h.getCliAdapter(provider.type);
3490
+ const adapter = getTargetedCliAdapter(h, args, provider.type);
3488
3491
  if (adapter) {
3489
3492
  _log(`${provider.category} adapter: ${adapter.cliType}`);
3490
3493
  try {
@@ -3637,7 +3640,7 @@ async function handleSendChat(h, args) {
3637
3640
  return { success: false, error: "No provider method could send the message" };
3638
3641
  }
3639
3642
  async function handleListChats(h, args) {
3640
- const provider = h.getProvider();
3643
+ const provider = h.getProvider(args?.agentType);
3641
3644
  if (provider?.category === "extension" && h.agentStream && h.getCdp()) {
3642
3645
  try {
3643
3646
  const chats = await h.agentStream.listAgentChats(h.getCdp(), provider.type);
@@ -3689,7 +3692,16 @@ async function handleListChats(h, args) {
3689
3692
  return { success: false, error: "listSessions script not available for this provider" };
3690
3693
  }
3691
3694
  async function handleNewChat(h, args) {
3692
- const provider = h.getProvider();
3695
+ const provider = h.getProvider(args?.agentType);
3696
+ if (provider?.category === "cli") {
3697
+ const adapter = getTargetedCliAdapter(h, args, provider.type);
3698
+ if (!adapter) return { success: false, error: "CLI adapter not running" };
3699
+ if (typeof adapter.clearHistory === "function") {
3700
+ adapter.clearHistory();
3701
+ return { success: true, cleared: true };
3702
+ }
3703
+ return { success: false, error: "new_chat not supported by this CLI provider" };
3704
+ }
3693
3705
  if (provider?.category === "extension" && h.agentStream && h.getCdp()) {
3694
3706
  const ok = await h.agentStream.newAgentSession(h.getCdp(), provider.type, h.currentIdeType);
3695
3707
  return { success: ok };
@@ -3714,7 +3726,7 @@ async function handleNewChat(h, args) {
3714
3726
  return { success: false, error: "newSession script not available for this provider" };
3715
3727
  }
3716
3728
  async function handleSwitchChat(h, args) {
3717
- const provider = h.getProvider();
3729
+ const provider = h.getProvider(args?.agentType);
3718
3730
  const ideType = h.currentIdeType;
3719
3731
  const sessionId = args?.sessionId || args?.id || args?.chatId;
3720
3732
  if (!sessionId) return { success: false, error: "sessionId required" };
@@ -3808,10 +3820,10 @@ async function handleSwitchChat(h, args) {
3808
3820
  }
3809
3821
  }
3810
3822
  async function handleSetMode(h, args) {
3811
- const provider = h.getProvider();
3823
+ const provider = h.getProvider(args?.agentType);
3812
3824
  const mode = args?.mode || "agent";
3813
3825
  if (provider?.category === "acp") {
3814
- const adapter = h.getCliAdapter(provider.type);
3826
+ const adapter = getTargetedCliAdapter(h, args, provider.type);
3815
3827
  if (adapter) {
3816
3828
  const acpInstance = adapter._acpInstance;
3817
3829
  if (acpInstance && typeof acpInstance.onEvent === "function") {
@@ -3863,11 +3875,11 @@ async function handleSetMode(h, args) {
3863
3875
  return { success: false, error: `setMode '${mode}' not supported by this provider` };
3864
3876
  }
3865
3877
  async function handleChangeModel(h, args) {
3866
- const provider = h.getProvider();
3878
+ const provider = h.getProvider(args?.agentType);
3867
3879
  const model = args?.model;
3868
3880
  LOG.info("Command", `[change_model] model=${model} provider=${provider?.type} category=${provider?.category} ideType=${h.currentIdeType} providerType=${h.currentProviderType}`);
3869
3881
  if (provider?.category === "acp") {
3870
- const adapter = h.getCliAdapter(provider.type);
3882
+ const adapter = getTargetedCliAdapter(h, args, provider.type);
3871
3883
  LOG.info("Command", `[change_model] ACP adapter found: ${!!adapter}, type=${adapter?.cliType}, hasAcpInstance=${!!adapter?._acpInstance}`);
3872
3884
  if (adapter) {
3873
3885
  const acpInstance = adapter._acpInstance;
@@ -3924,11 +3936,11 @@ async function handleSetThoughtLevel(h, args) {
3924
3936
  const configId = args?.configId;
3925
3937
  const value = args?.value;
3926
3938
  if (!configId || !value) return { success: false, error: "configId and value required" };
3927
- const provider = h.getProvider();
3939
+ const provider = h.getProvider(args?.agentType);
3928
3940
  if (!provider || provider.category !== "acp") {
3929
3941
  return { success: false, error: "set_thought_level only for ACP providers" };
3930
3942
  }
3931
- const adapter = h.getCliAdapter(provider.type);
3943
+ const adapter = getTargetedCliAdapter(h, args, provider.type);
3932
3944
  const acpInstance = adapter?._acpInstance;
3933
3945
  if (!acpInstance) return { success: false, error: "ACP instance not found" };
3934
3946
  try {
@@ -3940,13 +3952,22 @@ async function handleSetThoughtLevel(h, args) {
3940
3952
  }
3941
3953
  }
3942
3954
  async function handleResolveAction(h, args) {
3943
- const provider = h.getProvider();
3955
+ const provider = h.getProvider(args?.agentType);
3944
3956
  const action = args?.action || "approve";
3945
3957
  const button = args?.button || args?.buttonText || (action === "approve" ? "Accept" : action === "reject" ? "Reject" : "Accept");
3946
3958
  LOG.info("Command", `[resolveAction] action=${action} button="${button}" provider=${provider?.type}`);
3947
3959
  if (provider?.category === "cli") {
3948
- const adapter = h.getCliAdapter(provider.type);
3960
+ const adapter = getTargetedCliAdapter(h, args, provider.type);
3949
3961
  if (!adapter) return { success: false, error: "CLI adapter not running" };
3962
+ if (args?.data && typeof adapter.resolveAction === "function") {
3963
+ try {
3964
+ await adapter.resolveAction(args.data);
3965
+ LOG.info("Command", `[resolveAction] CLI PTY \u2192 resolveAction triggered with data payload`);
3966
+ return { success: true, method: "cli-resolve-action" };
3967
+ } catch (e) {
3968
+ return { success: false, error: `CLI resolveAction failed: ${e.message}` };
3969
+ }
3970
+ }
3950
3971
  const status = adapter.getStatus?.();
3951
3972
  if (status?.status !== "waiting_approval") {
3952
3973
  return { success: false, error: "Not in approval state" };
@@ -5998,7 +6019,7 @@ var init_provider_loader = __esm({
5998
6019
  // ─── Private ───────────────────────────────────
5999
6020
  /**
6000
6021
  * Find the on-disk directory for a provider by type.
6001
- * Preferred shape: root/category/type. Legacy flat root/type is kept temporarily for compatibility.
6022
+ * Canonical shape: root/category/type.
6002
6023
  */
6003
6024
  findProviderDirInternal(type) {
6004
6025
  const provider = this.providers.get(type);
@@ -6007,9 +6028,8 @@ var init_provider_loader = __esm({
6007
6028
  const searchRoots = this.getProviderRoots();
6008
6029
  for (const root of searchRoots) {
6009
6030
  if (!fs5.existsSync(root)) continue;
6010
- for (const candidate of [this.getProviderDir(root, cat, type), path6.join(root, type)]) {
6011
- if (fs5.existsSync(path6.join(candidate, "provider.json"))) return candidate;
6012
- }
6031
+ const candidate = this.getProviderDir(root, cat, type);
6032
+ if (fs5.existsSync(path6.join(candidate, "provider.json"))) return candidate;
6013
6033
  const catDir = path6.join(root, cat);
6014
6034
  if (fs5.existsSync(catDir)) {
6015
6035
  try {
@@ -6184,8 +6204,9 @@ var init_provider_loader = __esm({
6184
6204
  }
6185
6205
  }
6186
6206
  compareVersions(a, b) {
6187
- const pa = a.split(".").map(Number);
6188
- const pb = b.split(".").map(Number);
6207
+ const normalize2 = (v) => v.split(/[-_+]/)[0].split(".").map((x) => parseInt(x, 10) || 0);
6208
+ const pa = normalize2(a);
6209
+ const pb = normalize2(b);
6189
6210
  for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
6190
6211
  const va = pa[i] || 0;
6191
6212
  const vb = pb[i] || 0;
@@ -7237,6 +7258,246 @@ var init_reporter = __esm({
7237
7258
  }
7238
7259
  });
7239
7260
 
7261
+ // ../daemon-core/src/cli-adapters/terminal-screen.ts
7262
+ function clamp(value, min, max) {
7263
+ return Math.max(min, Math.min(max, value));
7264
+ }
7265
+ var TerminalScreen;
7266
+ var init_terminal_screen = __esm({
7267
+ "../daemon-core/src/cli-adapters/terminal-screen.ts"() {
7268
+ "use strict";
7269
+ TerminalScreen = class {
7270
+ rows;
7271
+ cols;
7272
+ cursorRow = 0;
7273
+ cursorCol = 0;
7274
+ savedRow = 0;
7275
+ savedCol = 0;
7276
+ lines;
7277
+ constructor(rows = 40, cols = 120) {
7278
+ this.rows = rows;
7279
+ this.cols = cols;
7280
+ this.lines = this.makeLines(rows, cols);
7281
+ }
7282
+ reset(rows = this.rows, cols = this.cols) {
7283
+ this.rows = rows;
7284
+ this.cols = cols;
7285
+ this.cursorRow = 0;
7286
+ this.cursorCol = 0;
7287
+ this.savedRow = 0;
7288
+ this.savedCol = 0;
7289
+ this.lines = this.makeLines(rows, cols);
7290
+ }
7291
+ resize(rows, cols) {
7292
+ const nextRows = Math.max(1, rows | 0);
7293
+ const nextCols = Math.max(1, cols | 0);
7294
+ const next = this.makeLines(nextRows, nextCols);
7295
+ const copyRows = Math.min(this.rows, nextRows);
7296
+ const copyCols = Math.min(this.cols, nextCols);
7297
+ for (let r = 0; r < copyRows; r++) {
7298
+ for (let c = 0; c < copyCols; c++) {
7299
+ next[r][c] = this.lines[r][c];
7300
+ }
7301
+ }
7302
+ this.rows = nextRows;
7303
+ this.cols = nextCols;
7304
+ this.lines = next;
7305
+ this.cursorRow = clamp(this.cursorRow, 0, this.rows - 1);
7306
+ this.cursorCol = clamp(this.cursorCol, 0, this.cols - 1);
7307
+ this.savedRow = clamp(this.savedRow, 0, this.rows - 1);
7308
+ this.savedCol = clamp(this.savedCol, 0, this.cols - 1);
7309
+ }
7310
+ write(data) {
7311
+ let i = 0;
7312
+ while (i < data.length) {
7313
+ const ch = data[i];
7314
+ if (ch === "\x1B") {
7315
+ const consumed = this.consumeEscape(data, i);
7316
+ i = consumed > i ? consumed : i + 1;
7317
+ continue;
7318
+ }
7319
+ if (ch === "\r") {
7320
+ this.cursorCol = 0;
7321
+ i++;
7322
+ continue;
7323
+ }
7324
+ if (ch === "\n") {
7325
+ this.newLine();
7326
+ i++;
7327
+ continue;
7328
+ }
7329
+ if (ch === "\b") {
7330
+ this.cursorCol = Math.max(0, this.cursorCol - 1);
7331
+ i++;
7332
+ continue;
7333
+ }
7334
+ if (ch === " ") {
7335
+ const nextStop = Math.min(this.cols - 1, this.cursorCol + (8 - (this.cursorCol % 8 || 8)));
7336
+ while (this.cursorCol < nextStop) this.putChar(" ");
7337
+ i++;
7338
+ continue;
7339
+ }
7340
+ if (ch >= " " && ch !== "\x7F") {
7341
+ this.putChar(ch);
7342
+ }
7343
+ i++;
7344
+ }
7345
+ }
7346
+ getText() {
7347
+ const raw = this.lines.map((line) => line.join("").replace(/\s+$/, ""));
7348
+ let start = 0;
7349
+ let end = raw.length;
7350
+ while (start < end && raw[start] === "") start++;
7351
+ while (end > start && raw[end - 1] === "") end--;
7352
+ return raw.slice(start, end).join("\n");
7353
+ }
7354
+ consumeEscape(data, start) {
7355
+ const next = data[start + 1];
7356
+ if (!next) return start + 1;
7357
+ if (next === "[") {
7358
+ let end = start + 2;
7359
+ while (end < data.length && !/[@-~]/.test(data[end])) end++;
7360
+ if (end >= data.length) return data.length;
7361
+ this.applyCsi(data.slice(start + 2, end), data[end]);
7362
+ return end + 1;
7363
+ }
7364
+ if (next === "]") {
7365
+ let end = start + 2;
7366
+ while (end < data.length) {
7367
+ if (data[end] === "\x07") return end + 1;
7368
+ if (data[end] === "\x1B" && data[end + 1] === "\\") return end + 2;
7369
+ end++;
7370
+ }
7371
+ return data.length;
7372
+ }
7373
+ if (next === "7") {
7374
+ this.savedRow = this.cursorRow;
7375
+ this.savedCol = this.cursorCol;
7376
+ return start + 2;
7377
+ }
7378
+ if (next === "8") {
7379
+ this.cursorRow = this.savedRow;
7380
+ this.cursorCol = this.savedCol;
7381
+ return start + 2;
7382
+ }
7383
+ return start + 2;
7384
+ }
7385
+ applyCsi(paramText, finalChar) {
7386
+ const privateMode = paramText.startsWith("?");
7387
+ const normalized = privateMode ? paramText.slice(1) : paramText;
7388
+ const params = normalized.length > 0 ? normalized.split(";").map((p) => parseInt(p || "0", 10) || 0) : [0];
7389
+ switch (finalChar) {
7390
+ case "A":
7391
+ this.cursorRow = clamp(this.cursorRow - (params[0] || 1), 0, this.rows - 1);
7392
+ return;
7393
+ case "B":
7394
+ this.cursorRow = clamp(this.cursorRow + (params[0] || 1), 0, this.rows - 1);
7395
+ return;
7396
+ case "C":
7397
+ this.cursorCol = clamp(this.cursorCol + (params[0] || 1), 0, this.cols - 1);
7398
+ return;
7399
+ case "D":
7400
+ this.cursorCol = clamp(this.cursorCol - (params[0] || 1), 0, this.cols - 1);
7401
+ return;
7402
+ case "E":
7403
+ this.cursorRow = clamp(this.cursorRow + (params[0] || 1), 0, this.rows - 1);
7404
+ this.cursorCol = 0;
7405
+ return;
7406
+ case "F":
7407
+ this.cursorRow = clamp(this.cursorRow - (params[0] || 1), 0, this.rows - 1);
7408
+ this.cursorCol = 0;
7409
+ return;
7410
+ case "G":
7411
+ this.cursorCol = clamp((params[0] || 1) - 1, 0, this.cols - 1);
7412
+ return;
7413
+ case "H":
7414
+ case "f": {
7415
+ const row = (params[0] || 1) - 1;
7416
+ const col = (params[1] || 1) - 1;
7417
+ this.cursorRow = clamp(row, 0, this.rows - 1);
7418
+ this.cursorCol = clamp(col, 0, this.cols - 1);
7419
+ return;
7420
+ }
7421
+ case "J": {
7422
+ const mode = params[0] || 0;
7423
+ if (mode === 2 || mode === 3) {
7424
+ this.reset(this.rows, this.cols);
7425
+ } else if (mode === 0) {
7426
+ this.clearToEndOfScreen();
7427
+ } else if (mode === 1) {
7428
+ this.clearToStartOfScreen();
7429
+ }
7430
+ return;
7431
+ }
7432
+ case "K": {
7433
+ const mode = params[0] || 0;
7434
+ if (mode === 2) this.clearLine(this.cursorRow, 0, this.cols - 1);
7435
+ else if (mode === 1) this.clearLine(this.cursorRow, 0, this.cursorCol);
7436
+ else this.clearLine(this.cursorRow, this.cursorCol, this.cols - 1);
7437
+ return;
7438
+ }
7439
+ case "m":
7440
+ return;
7441
+ case "s":
7442
+ this.savedRow = this.cursorRow;
7443
+ this.savedCol = this.cursorCol;
7444
+ return;
7445
+ case "u":
7446
+ this.cursorRow = this.savedRow;
7447
+ this.cursorCol = this.savedCol;
7448
+ return;
7449
+ case "h":
7450
+ case "l":
7451
+ if (privateMode && (normalized === "1049" || normalized === "47")) {
7452
+ this.reset(this.rows, this.cols);
7453
+ }
7454
+ return;
7455
+ default:
7456
+ return;
7457
+ }
7458
+ }
7459
+ putChar(ch) {
7460
+ if (this.cursorRow < 0 || this.cursorRow >= this.rows) return;
7461
+ if (this.cursorCol < 0) this.cursorCol = 0;
7462
+ if (this.cursorCol >= this.cols) this.newLine();
7463
+ this.lines[this.cursorRow][this.cursorCol] = ch;
7464
+ this.cursorCol++;
7465
+ if (this.cursorCol >= this.cols) this.newLine();
7466
+ }
7467
+ newLine() {
7468
+ this.cursorCol = 0;
7469
+ if (this.cursorRow >= this.rows - 1) {
7470
+ this.lines.shift();
7471
+ this.lines.push(Array.from({ length: this.cols }, () => " "));
7472
+ } else {
7473
+ this.cursorRow++;
7474
+ }
7475
+ }
7476
+ clearLine(row, start, end) {
7477
+ if (row < 0 || row >= this.rows) return;
7478
+ for (let c = clamp(start, 0, this.cols - 1); c <= clamp(end, 0, this.cols - 1); c++) {
7479
+ this.lines[row][c] = " ";
7480
+ }
7481
+ }
7482
+ clearToEndOfScreen() {
7483
+ this.clearLine(this.cursorRow, this.cursorCol, this.cols - 1);
7484
+ for (let r = this.cursorRow + 1; r < this.rows; r++) {
7485
+ this.clearLine(r, 0, this.cols - 1);
7486
+ }
7487
+ }
7488
+ clearToStartOfScreen() {
7489
+ for (let r = 0; r < this.cursorRow; r++) {
7490
+ this.clearLine(r, 0, this.cols - 1);
7491
+ }
7492
+ this.clearLine(this.cursorRow, 0, this.cursorCol);
7493
+ }
7494
+ makeLines(rows, cols) {
7495
+ return Array.from({ length: rows }, () => Array.from({ length: cols }, () => " "));
7496
+ }
7497
+ };
7498
+ }
7499
+ });
7500
+
7240
7501
  // ../daemon-core/src/cli-adapters/provider-cli-adapter.ts
7241
7502
  var provider_cli_adapter_exports = {};
7242
7503
  __export(provider_cli_adapter_exports, {
@@ -7309,28 +7570,19 @@ function parsePatternEntry(x) {
7309
7570
  }
7310
7571
  return null;
7311
7572
  }
7312
- function coercePatternArray(raw, fallbacks) {
7313
- if (!Array.isArray(raw)) return [...fallbacks];
7314
- const parsed = raw.map(parsePatternEntry).filter((r) => r != null);
7315
- return parsed.length > 0 ? parsed : [...fallbacks];
7316
- }
7317
- function defaultCleanOutput(raw, _lastUserInput) {
7318
- return stripAnsi(raw).trim();
7573
+ function coercePatternArray(raw) {
7574
+ if (!Array.isArray(raw)) return [];
7575
+ return raw.map(parsePatternEntry).filter((r) => r != null);
7319
7576
  }
7320
7577
  function normalizeCliProviderForRuntime(raw) {
7321
7578
  const patterns = raw?.patterns || {};
7322
7579
  return {
7323
- ...raw,
7324
7580
  patterns: {
7325
- prompt: coercePatternArray(patterns.prompt, FALLBACK_PROMPT),
7326
- generating: coercePatternArray(patterns.generating, FALLBACK_GENERATING),
7327
- approval: coercePatternArray(patterns.approval, FALLBACK_APPROVAL),
7328
- ready: coercePatternArray(patterns.ready, [])
7329
- },
7330
- cleanOutput: typeof raw?.cleanOutput === "function" ? raw.cleanOutput : defaultCleanOutput
7581
+ approval: coercePatternArray(patterns.approval)
7582
+ }
7331
7583
  };
7332
7584
  }
7333
- var os11, path9, import_child_process5, pty, FALLBACK_PROMPT, FALLBACK_GENERATING, FALLBACK_APPROVAL, ProviderCliAdapter;
7585
+ var os11, path9, import_child_process5, pty, ProviderCliAdapter;
7334
7586
  var init_provider_cli_adapter = __esm({
7335
7587
  "../daemon-core/src/cli-adapters/provider-cli-adapter.ts"() {
7336
7588
  "use strict";
@@ -7338,6 +7590,7 @@ var init_provider_cli_adapter = __esm({
7338
7590
  path9 = __toESM(require("path"));
7339
7591
  import_child_process5 = require("child_process");
7340
7592
  init_logger();
7593
+ init_terminal_screen();
7341
7594
  try {
7342
7595
  pty = require("node-pty");
7343
7596
  if (os11.platform() !== "win32") {
@@ -7359,40 +7612,10 @@ var init_provider_cli_adapter = __esm({
7359
7612
  } catch {
7360
7613
  LOG.error("CLI", "[ProviderCliAdapter] node-pty not found. Terminal features disabled.");
7361
7614
  }
7362
- FALLBACK_PROMPT = [
7363
- /Type your message/i,
7364
- /for\s*shortcuts/i,
7365
- // Claude Code prompt
7366
- /\?\s*for\s*help/i,
7367
- // Claude Code help prompt
7368
- /Press enter/i,
7369
- /^[>›❯]\s*$/i,
7370
- // Prompt char as the complete evaluated string
7371
- /[>›❯]\s*$/
7372
- // Prompt char at the very end of evaluated string
7373
- ];
7374
- FALLBACK_GENERATING = [
7375
- /[\u2800-\u28ff]/,
7376
- // Braille spinner blocks (universal TUI)
7377
- /esc to (cancel|interrupt|stop)/i,
7378
- // Common TUI generation status line
7379
- /generating\.\.\./i,
7380
- /Claude is (?:thinking|processing|working)/i
7381
- // Specific Claude Code status
7382
- ];
7383
- FALLBACK_APPROVAL = [
7384
- /Allow\s*once/i,
7385
- // ANSI strip may remove spaces
7386
- /Always\s*allow/i,
7387
- /\(y\/n\)/i,
7388
- /\[Y\/n\]/i,
7389
- /Yes,?\s*don'?t\s*ask/i
7390
- // "Yes, don't ask again" (Claude Code)
7391
- ];
7392
- ProviderCliAdapter = class {
7615
+ ProviderCliAdapter = class _ProviderCliAdapter {
7393
7616
  constructor(provider, workingDir, extraArgs = []) {
7394
7617
  this.extraArgs = extraArgs;
7395
- this.provider = normalizeCliProviderForRuntime(provider);
7618
+ this.provider = provider;
7396
7619
  this.cliType = provider.type;
7397
7620
  this.cliName = provider.name;
7398
7621
  this.workingDir = workingDir.startsWith("~") ? workingDir.replace(/^~/, os11.homedir()) : workingDir;
@@ -7409,6 +7632,13 @@ var init_provider_cli_adapter = __esm({
7409
7632
  };
7410
7633
  const rawKeys = provider.approvalKeys;
7411
7634
  this.approvalKeys = rawKeys && typeof rawKeys === "object" ? rawKeys : {};
7635
+ this.cliScripts = provider.scripts || {};
7636
+ const scriptNames = Object.keys(this.cliScripts).filter((k) => typeof this.cliScripts[k] === "function");
7637
+ if (scriptNames.length > 0) {
7638
+ LOG.info("CLI", `[${this.cliType}] CLI scripts: [${scriptNames.join(", ")}]`);
7639
+ } else {
7640
+ LOG.warn("CLI", `[${this.cliType}] \u26A0 No CLI scripts loaded! Provider needs scripts/{version}/scripts.js`);
7641
+ }
7412
7642
  }
7413
7643
  cliType;
7414
7644
  cliName;
@@ -7416,6 +7646,7 @@ var init_provider_cli_adapter = __esm({
7416
7646
  provider;
7417
7647
  ptyProcess = null;
7418
7648
  messages = [];
7649
+ structuredMessages = [];
7419
7650
  currentStatus = "starting";
7420
7651
  onStatusChange = null;
7421
7652
  responseBuffer = "";
@@ -7426,7 +7657,6 @@ var init_provider_cli_adapter = __esm({
7426
7657
  idleTimeout = null;
7427
7658
  ready = false;
7428
7659
  startupBuffer = "";
7429
- /** After spawn: briefly skip generating/settle so splash/redraw does not flip status; not used to gate sendMessage */
7430
7660
  startupParseGate = false;
7431
7661
  spawnAt = 0;
7432
7662
  // PTY I/O
@@ -7444,11 +7674,20 @@ var init_provider_cli_adapter = __esm({
7444
7674
  // Output settle debounce — fires after PTY output goes quiet
7445
7675
  settleTimer = null;
7446
7676
  settledBuffer = "";
7447
- // snapshot of recentOutputBuffer at settle time
7448
7677
  // Resize redraw suppression
7449
7678
  resizeSuppressUntil = 0;
7450
7679
  // Debug: status transition history
7451
7680
  statusHistory = [];
7681
+ // ─── CLI Scripts (script-based parsing) ───
7682
+ cliScripts;
7683
+ /** Full accumulated ANSI-stripped PTY output */
7684
+ accumulatedBuffer = "";
7685
+ /** Full accumulated raw PTY output (with ANSI) */
7686
+ accumulatedRawBuffer = "";
7687
+ /** Current visible terminal screen snapshot */
7688
+ terminalScreen = new TerminalScreen(40, 120);
7689
+ /** Max accumulated buffer size (last 50KB) */
7690
+ static MAX_ACCUMULATED_BUFFER = 5e4;
7452
7691
  setStatus(status, trigger) {
7453
7692
  const prev = this.currentStatus;
7454
7693
  if (prev === status) return;
@@ -7457,10 +7696,16 @@ var init_provider_cli_adapter = __esm({
7457
7696
  if (this.statusHistory.length > 50) this.statusHistory.shift();
7458
7697
  LOG.info("CLI", `[${this.cliType}] status: ${prev} \u2192 ${status}${trigger ? ` (${trigger})` : ""}`);
7459
7698
  }
7460
- // Resolved timeouts (provider defaults + overrides)
7699
+ // Resolved timeouts
7461
7700
  timeouts;
7462
- // Provider approval key mapping (e.g. { 0: '1', 1: '2', 2: '3' }) — loaded from provider.json
7701
+ // Provider approval key mapping
7463
7702
  approvalKeys;
7703
+ /** Inject CLI scripts after construction (e.g. when resolved by ProviderLoader) */
7704
+ setCliScripts(scripts) {
7705
+ this.cliScripts = scripts;
7706
+ const scriptNames = Object.keys(scripts).filter((k) => typeof scripts[k] === "function");
7707
+ LOG.info("CLI", `[${this.cliType}] CLI scripts injected: [${scriptNames.join(", ")}]`);
7708
+ }
7464
7709
  // ─── Lifecycle ─────────────────────────────────
7465
7710
  setServerConn(serverConn) {
7466
7711
  this.serverConn = serverConn;
@@ -7549,15 +7794,19 @@ var init_provider_cli_adapter = __esm({
7549
7794
  this.spawnAt = Date.now();
7550
7795
  this.startupParseGate = true;
7551
7796
  this.startupBuffer = "";
7797
+ this.terminalScreen.reset(40, 120);
7552
7798
  this.ready = true;
7553
7799
  this.setStatus("idle", "pty_ready");
7554
7800
  this.onStatusChange?.();
7555
7801
  }
7556
- // ─── Output state machine ────────────────────────────
7802
+ // ─── Output Handling ────────────────────────────
7557
7803
  handleOutput(rawData) {
7558
7804
  if (Date.now() < this.resizeSuppressUntil) return;
7805
+ this.terminalScreen.write(rawData);
7559
7806
  const cleanData = stripAnsi(rawData);
7560
- const { patterns } = this.provider;
7807
+ if (this.isWaitingForResponse && cleanData) {
7808
+ this.responseBuffer = (this.responseBuffer + cleanData).slice(-8e3);
7809
+ }
7561
7810
  if (cleanData.trim()) {
7562
7811
  if (this.serverConn) {
7563
7812
  this.serverConn.sendMessage("log", { message: cleanData.trim(), level: "info" });
@@ -7566,9 +7815,10 @@ var init_provider_cli_adapter = __esm({
7566
7815
  }
7567
7816
  }
7568
7817
  this.recentOutputBuffer = (this.recentOutputBuffer + cleanData).slice(-1e3);
7818
+ this.accumulatedBuffer = (this.accumulatedBuffer + cleanData).slice(-_ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
7819
+ this.accumulatedRawBuffer = (this.accumulatedRawBuffer + rawData).slice(-_ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
7569
7820
  if (this.startupParseGate) {
7570
7821
  this.startupBuffer += cleanData;
7571
- LOG.info("CLI", `[${this.cliType}] startup chunk (${cleanData.length} chars): ${cleanData.slice(0, 200).replace(/\n/g, "\\n")}`);
7572
7822
  const dialogPatterns = [
7573
7823
  /Do you want to connect/i,
7574
7824
  /Do you trust the files/i,
@@ -7583,60 +7833,17 @@ var init_provider_cli_adapter = __esm({
7583
7833
  }
7584
7834
  const elapsed = Date.now() - this.spawnAt;
7585
7835
  const bufCap = this.startupBuffer.length > 12e3;
7586
- const promptMatched = patterns.prompt.some((p) => p.test(this.startupBuffer));
7587
- if (promptMatched || elapsed > 8e3 || bufCap) {
7836
+ const scriptStatus = this.runDetectStatus(this.startupBuffer);
7837
+ const isReady = scriptStatus === "idle" || elapsed > 8e3 || bufCap;
7838
+ if (isReady) {
7588
7839
  this.startupParseGate = false;
7589
- if (promptMatched) {
7590
- LOG.info("CLI", `[${this.cliType}] \u2713 Startup gate end (prompt matched)`);
7591
- } else {
7592
- LOG.info("CLI", `[${this.cliType}] startup gate end (${elapsed}ms, cap=${bufCap}, prompt=${promptMatched})`);
7593
- }
7840
+ LOG.info("CLI", `[${this.cliType}] Startup gate end (${elapsed}ms, scriptStatus=${scriptStatus})`);
7594
7841
  } else {
7595
7842
  return;
7596
7843
  }
7597
7844
  }
7598
- if (cleanData.trim().length > 5) {
7599
- LOG.debug("CLI", `[${this.cliType}] output chunk (${cleanData.length}): ${cleanData.slice(0, 300).replace(/\n/g, "\\n")}`);
7600
- }
7601
- if (!this.isWaitingForResponse) {
7602
- if (patterns.generating.some((p) => p.test(cleanData))) {
7603
- if (this.settleTimer) {
7604
- clearTimeout(this.settleTimer);
7605
- this.settleTimer = null;
7606
- }
7607
- this.isWaitingForResponse = true;
7608
- this.responseBuffer = "";
7609
- this.setStatus("generating", "autonomous_gen");
7610
- this.onStatusChange?.();
7611
- }
7612
- }
7613
- if (this.isWaitingForResponse) {
7614
- this.responseBuffer += cleanData;
7615
- if (this.idleTimeout) clearTimeout(this.idleTimeout);
7616
- if (patterns.generating.some((p) => p.test(cleanData))) {
7617
- this.setStatus("generating", "still_generating");
7618
- this.idleTimeout = setTimeout(() => {
7619
- if (this.isWaitingForResponse) this.finishResponse();
7620
- }, this.timeouts.generatingIdle);
7621
- this.onStatusChange?.();
7622
- if (this.settleTimer) {
7623
- clearTimeout(this.settleTimer);
7624
- this.settleTimer = null;
7625
- }
7626
- return;
7627
- }
7628
- }
7629
- if (this.currentStatus === "waiting_approval") {
7630
- this.approvalTransitionBuffer = (this.approvalTransitionBuffer + cleanData).slice(-500);
7631
- this.scheduleSettle();
7632
- return;
7633
- }
7634
7845
  this.scheduleSettle();
7635
7846
  }
7636
- /**
7637
- * Fired after output goes quiet for outputSettle ms.
7638
- * Evaluates the stabilised buffer for approval, prompt (idle), or timeout.
7639
- */
7640
7847
  scheduleSettle() {
7641
7848
  if (this.settleTimer) clearTimeout(this.settleTimer);
7642
7849
  this.settleTimer = setTimeout(() => {
@@ -7646,59 +7853,25 @@ var init_provider_cli_adapter = __esm({
7646
7853
  }, this.timeouts.outputSettle);
7647
7854
  }
7648
7855
  evaluateSettled() {
7649
- const { patterns } = this.provider;
7650
- const buf = this.settledBuffer;
7651
- if (this.currentStatus === "waiting_approval") {
7652
- const genResume = patterns.generating.some((p) => p.test(this.approvalTransitionBuffer));
7653
- const promptResume = patterns.prompt.some((p) => p.test(this.approvalTransitionBuffer));
7654
- if (genResume) {
7655
- if (this.approvalExitTimeout) {
7656
- clearTimeout(this.approvalExitTimeout);
7657
- this.approvalExitTimeout = null;
7658
- }
7659
- this.setStatus("generating", "approval_gen_resume");
7660
- this.activeModal = null;
7661
- this.recentOutputBuffer = "";
7662
- this.approvalTransitionBuffer = "";
7663
- this.lastApprovalResolvedAt = Date.now();
7664
- this.onStatusChange?.();
7665
- } else if (promptResume) {
7666
- if (this.approvalExitTimeout) {
7667
- clearTimeout(this.approvalExitTimeout);
7668
- this.approvalExitTimeout = null;
7669
- }
7670
- this.activeModal = null;
7671
- this.recentOutputBuffer = "";
7672
- this.approvalTransitionBuffer = "";
7673
- this.lastApprovalResolvedAt = Date.now();
7674
- this.finishResponse();
7675
- }
7676
- return;
7677
- }
7678
- const hasApproval = patterns.approval.some((p) => p.test(buf));
7679
- if (hasApproval) {
7856
+ const tail = this.settledBuffer;
7857
+ const scriptStatus = this.runDetectStatus(tail);
7858
+ if (!scriptStatus) return;
7859
+ const prevStatus = this.currentStatus;
7860
+ if (scriptStatus === "waiting_approval") {
7680
7861
  const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
7681
7862
  if (!inCooldown) {
7682
- const ctxLines = buf.split("\n").map((l) => l.trim()).filter((l) => l && !/^[─═╭╮╰╯│]+$/.test(l));
7683
7863
  this.isWaitingForResponse = true;
7684
- this.setStatus("waiting_approval", "approval_pattern");
7685
- this.recentOutputBuffer = "";
7686
- this.approvalTransitionBuffer = "";
7687
- this.activeModal = {
7688
- message: ctxLines.slice(-5).join(" ").slice(0, 200) || "Approval required",
7689
- buttons: this.cliType === "claude-cli" ? ["Yes (y)", "Always allow (a)", "Deny (Esc)"] : ["Allow once", "Always allow", "Deny"]
7690
- };
7864
+ this.setStatus("waiting_approval", "script_detect");
7865
+ const modal = this.runParseApproval(tail);
7866
+ this.activeModal = modal || { message: "Approval required", buttons: ["Allow", "Deny"] };
7691
7867
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
7692
7868
  if (this.approvalExitTimeout) clearTimeout(this.approvalExitTimeout);
7693
7869
  this.approvalExitTimeout = setTimeout(() => {
7694
7870
  if (this.currentStatus === "waiting_approval") {
7695
- LOG.warn("CLI", `[${this.cliType}] Approval timeout \u2014 auto-exiting waiting_approval`);
7871
+ LOG.warn("CLI", `[${this.cliType}] Approval timeout \u2014 auto-clearing`);
7696
7872
  this.activeModal = null;
7697
7873
  this.lastApprovalResolvedAt = Date.now();
7698
- this.recentOutputBuffer = "";
7699
- this.approvalTransitionBuffer = "";
7700
- this.approvalExitTimeout = null;
7701
- this.setStatus(this.isWaitingForResponse ? "generating" : "idle", "approval_cleared");
7874
+ this.setStatus("idle", "approval_timeout");
7702
7875
  this.onStatusChange?.();
7703
7876
  }
7704
7877
  }, 6e4);
@@ -7706,19 +7879,42 @@ var init_provider_cli_adapter = __esm({
7706
7879
  return;
7707
7880
  }
7708
7881
  }
7709
- if (this.isWaitingForResponse) {
7710
- const trailingLines = buf.split("\n").slice(-3).join("\n");
7711
- if (patterns.prompt.some((p) => p.test(trailingLines)) && !hasApproval) {
7712
- this.finishResponse();
7713
- } else if (!patterns.generating.some((p) => p.test(buf))) {
7714
- if (this.idleTimeout) clearTimeout(this.idleTimeout);
7715
- this.idleTimeout = setTimeout(() => {
7716
- if (this.isWaitingForResponse && this.responseBuffer.trim()) {
7717
- this.finishResponse();
7718
- }
7719
- }, this.timeouts.idleFinish);
7882
+ if (scriptStatus === "generating") {
7883
+ if (prevStatus === "waiting_approval") {
7884
+ if (this.approvalExitTimeout) {
7885
+ clearTimeout(this.approvalExitTimeout);
7886
+ this.approvalExitTimeout = null;
7887
+ }
7888
+ this.activeModal = null;
7889
+ this.lastApprovalResolvedAt = Date.now();
7890
+ }
7891
+ if (!this.isWaitingForResponse) {
7892
+ this.isWaitingForResponse = true;
7893
+ this.responseBuffer = "";
7720
7894
  }
7895
+ this.setStatus("generating", "script_detect");
7896
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
7897
+ this.idleTimeout = setTimeout(() => {
7898
+ if (this.isWaitingForResponse) this.finishResponse();
7899
+ }, this.timeouts.generatingIdle);
7721
7900
  this.onStatusChange?.();
7901
+ return;
7902
+ }
7903
+ if (scriptStatus === "idle") {
7904
+ if (prevStatus === "waiting_approval") {
7905
+ if (this.approvalExitTimeout) {
7906
+ clearTimeout(this.approvalExitTimeout);
7907
+ this.approvalExitTimeout = null;
7908
+ }
7909
+ this.activeModal = null;
7910
+ this.lastApprovalResolvedAt = Date.now();
7911
+ }
7912
+ if (this.isWaitingForResponse) {
7913
+ this.finishResponse();
7914
+ } else if (prevStatus !== "idle") {
7915
+ this.setStatus("idle", "script_detect");
7916
+ this.onStatusChange?.();
7917
+ }
7722
7918
  }
7723
7919
  }
7724
7920
  finishResponse() {
@@ -7734,25 +7930,50 @@ var init_provider_cli_adapter = __esm({
7734
7930
  clearTimeout(this.approvalExitTimeout);
7735
7931
  this.approvalExitTimeout = null;
7736
7932
  }
7737
- const lastUserText = this.messages.filter((m) => m.role === "user").pop()?.content;
7738
- let response = this.provider.cleanOutput(this.responseBuffer, lastUserText);
7739
- if (lastUserText && response) {
7740
- const userTrimmed = lastUserText.trim();
7741
- response = response.split("\n").filter((l) => l.trim() !== userTrimmed).join("\n").trim();
7742
- }
7743
- if (response) {
7744
- this.messages.push({ role: "assistant", content: response, timestamp: Date.now() });
7745
- if (this.messages.length > 200) this.messages = this.messages.slice(-200);
7746
- LOG.info("CLI", `[${this.cliType}] Response (${response.length} chars)`);
7747
- }
7748
7933
  this.responseBuffer = "";
7749
7934
  this.isWaitingForResponse = false;
7750
7935
  this.activeModal = null;
7751
7936
  this.setStatus("idle", "response_finished");
7752
7937
  this.onStatusChange?.();
7753
7938
  }
7754
- // ─── Public API (CliAdapter interface) ──────────
7939
+ // ─── Script Execution ──────────────────────────
7940
+ runDetectStatus(text) {
7941
+ if (!this.cliScripts?.detectStatus) return null;
7942
+ try {
7943
+ return this.cliScripts.detectStatus({ tail: text.slice(-500) });
7944
+ } catch (e) {
7945
+ LOG.warn("CLI", `[${this.cliType}] detectStatus error: ${e.message}`);
7946
+ return null;
7947
+ }
7948
+ }
7949
+ runParseApproval(tail) {
7950
+ if (!this.cliScripts?.parseApproval) return null;
7951
+ try {
7952
+ return this.cliScripts.parseApproval({
7953
+ buffer: this.terminalScreen.getText() || this.accumulatedBuffer,
7954
+ rawBuffer: this.accumulatedRawBuffer,
7955
+ tail
7956
+ });
7957
+ } catch (e) {
7958
+ LOG.warn("CLI", `[${this.cliType}] parseApproval error: ${e.message}`);
7959
+ return null;
7960
+ }
7961
+ }
7962
+ // ─── Public API (CliAdapter) ───────────────────
7755
7963
  getStatus() {
7964
+ const scriptResult = this.getScriptParsedStatus();
7965
+ if (scriptResult) {
7966
+ return {
7967
+ status: this.currentStatus,
7968
+ messages: (scriptResult.messages || []).map((m) => ({
7969
+ role: m.role,
7970
+ content: m.content,
7971
+ timestamp: m.timestamp
7972
+ })),
7973
+ workingDir: this.workingDir,
7974
+ activeModal: this.activeModal
7975
+ };
7976
+ }
7756
7977
  return {
7757
7978
  status: this.currentStatus,
7758
7979
  messages: [...this.messages],
@@ -7760,11 +7981,71 @@ var init_provider_cli_adapter = __esm({
7760
7981
  activeModal: this.activeModal
7761
7982
  };
7762
7983
  }
7984
+ /**
7985
+ * Script-based full parse — returns ReadChatResult.
7986
+ * Called by command handler / dashboard for rich content rendering.
7987
+ */
7988
+ getScriptParsedStatus() {
7989
+ if (!this.cliScripts?.parseOutput) return null;
7990
+ try {
7991
+ const input = {
7992
+ buffer: this.accumulatedBuffer,
7993
+ rawBuffer: this.accumulatedRawBuffer,
7994
+ recentBuffer: this.recentOutputBuffer,
7995
+ screenText: this.terminalScreen.getText(),
7996
+ messages: [...this.structuredMessages.length > 0 ? this.structuredMessages : this.messages],
7997
+ partialResponse: this.responseBuffer
7998
+ };
7999
+ const result = this.cliScripts.parseOutput(input);
8000
+ if (result && typeof result === "object") {
8001
+ if (Array.isArray(result.messages)) {
8002
+ this.structuredMessages = result.messages.map((m) => ({
8003
+ role: m.role,
8004
+ content: m.content,
8005
+ timestamp: m.timestamp
8006
+ }));
8007
+ }
8008
+ return result;
8009
+ }
8010
+ } catch (e) {
8011
+ LOG.warn("CLI", `[${this.cliType}] parseOutput error: ${e.message}`);
8012
+ }
8013
+ return null;
8014
+ }
8015
+ /** Whether this adapter has CLI scripts loaded */
8016
+ hasCliScripts() {
8017
+ return typeof this.cliScripts?.detectStatus === "function";
8018
+ }
8019
+ /**
8020
+ * Resolves an action (like 'fix' lint error) from the dashboard.
8021
+ * Uses resolveAction script if available, otherwise falls back to standard text.
8022
+ */
8023
+ async resolveAction(data) {
8024
+ let promptText = "";
8025
+ if (this.cliScripts && typeof this.cliScripts.resolveAction === "function") {
8026
+ try {
8027
+ promptText = this.cliScripts.resolveAction(data);
8028
+ } catch (e) {
8029
+ LOG.warn("CLI", `[${this.cliType}] resolveAction error: ${e.message}`);
8030
+ }
8031
+ }
8032
+ if (!promptText && data) {
8033
+ promptText = `Please fix the following issue:
8034
+ ${data.title || ""}
8035
+ ${data.explanation || ""}
8036
+
8037
+ ${data.message || ""}`.trim();
8038
+ }
8039
+ if (promptText) {
8040
+ await this.sendMessage(promptText);
8041
+ }
8042
+ }
7763
8043
  async sendMessage(text) {
7764
8044
  if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
7765
8045
  if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
7766
8046
  if (this.isWaitingForResponse) return;
7767
8047
  this.messages.push({ role: "user", content: text, timestamp: Date.now() });
8048
+ this.structuredMessages.push({ role: "user", content: text, timestamp: Date.now() });
7768
8049
  this.isWaitingForResponse = true;
7769
8050
  this.responseBuffer = "";
7770
8051
  this.setStatus("generating", "sendMessage");
@@ -7776,8 +8057,7 @@ var init_provider_cli_adapter = __esm({
7776
8057
  }
7777
8058
  getPartialResponse() {
7778
8059
  if (!this.isWaitingForResponse) return "";
7779
- const partial2 = this.provider.cleanOutput(this.responseBuffer);
7780
- return partial2 || (this.isWaitingForResponse ? "(generating...)" : "");
8060
+ return this.responseBuffer;
7781
8061
  }
7782
8062
  cancel() {
7783
8063
  this.shutdown();
@@ -7809,6 +8089,10 @@ var init_provider_cli_adapter = __esm({
7809
8089
  }
7810
8090
  clearHistory() {
7811
8091
  this.messages = [];
8092
+ this.structuredMessages = [];
8093
+ this.accumulatedBuffer = "";
8094
+ this.accumulatedRawBuffer = "";
8095
+ this.terminalScreen.reset();
7812
8096
  this.onStatusChange?.();
7813
8097
  }
7814
8098
  isProcessing() {
@@ -7820,11 +8104,6 @@ var init_provider_cli_adapter = __esm({
7820
8104
  writeRaw(data) {
7821
8105
  this.ptyProcess?.write(data);
7822
8106
  }
7823
- /**
7824
- * Resolve an approval modal by navigating to the button at `buttonIndex` and pressing Enter.
7825
- * Index 0 = first option (already selected by default — just Enter).
7826
- * Index N = press Arrow Down N times, then Enter.
7827
- */
7828
8107
  resolveModal(buttonIndex) {
7829
8108
  if (!this.ptyProcess || this.currentStatus !== "waiting_approval") return;
7830
8109
  if (buttonIndex in this.approvalKeys) {
@@ -7839,25 +8118,13 @@ var init_provider_cli_adapter = __esm({
7839
8118
  if (this.ptyProcess) {
7840
8119
  try {
7841
8120
  this.ptyProcess.resize(cols, rows);
8121
+ this.terminalScreen.resize(rows, cols);
7842
8122
  this.resizeSuppressUntil = Date.now() + 300;
7843
8123
  } catch {
7844
8124
  }
7845
8125
  }
7846
8126
  }
7847
- /**
7848
- * Full debug state — exposes all internal buffers, status, and patterns for debugging.
7849
- * Used by DevServer /api/cli/debug endpoint.
7850
- */
7851
8127
  getDebugState() {
7852
- const sb = this.startupBuffer;
7853
- const testOnStartup = (p) => {
7854
- const flags = p.flags.includes("g") ? p.flags.replace(/g/g, "") : p.flags;
7855
- return new RegExp(p.source, flags).test(sb);
7856
- };
7857
- const promptDiagnostics = this.provider.patterns.prompt.map((p) => ({
7858
- pattern: p.toString(),
7859
- matchedAgainstStartupBuffer: testOnStartup(p)
7860
- }));
7861
8128
  return {
7862
8129
  type: this.cliType,
7863
8130
  name: this.cliName,
@@ -7867,32 +8134,20 @@ var init_provider_cli_adapter = __esm({
7867
8134
  spawnAt: this.spawnAt,
7868
8135
  workingDir: this.workingDir,
7869
8136
  messages: this.messages.slice(-20),
8137
+ structuredMessages: this.structuredMessages.slice(-20),
7870
8138
  messageCount: this.messages.length,
7871
- // Buffers (longer tails here than in periodic logs — for matching provider.json patterns)
7872
- startupBuffer: sb.slice(-4e3),
7873
- startupBufferLength: sb.length,
7874
- promptDiagnostics,
8139
+ startupBuffer: this.startupBuffer.slice(-4e3),
7875
8140
  recentOutputBuffer: this.recentOutputBuffer.slice(-500),
7876
8141
  settledBuffer: this.settledBuffer.slice(-500),
7877
- responseBuffer: this.responseBuffer.slice(-500),
7878
- approvalTransitionBuffer: this.approvalTransitionBuffer.slice(-500),
7879
- // State
8142
+ accumulatedBufferLength: this.accumulatedBuffer.length,
7880
8143
  isWaitingForResponse: this.isWaitingForResponse,
7881
8144
  activeModal: this.activeModal,
7882
8145
  lastApprovalResolvedAt: this.lastApprovalResolvedAt,
7883
8146
  resizeSuppressUntil: this.resizeSuppressUntil,
7884
- // Provider patterns (serialized)
7885
- patterns: {
7886
- prompt: this.provider.patterns.prompt.map((p) => p.toString()),
7887
- generating: this.provider.patterns.generating.map((p) => p.toString()),
7888
- approval: this.provider.patterns.approval.map((p) => p.toString()),
7889
- ready: this.provider.patterns.ready.map((p) => p.toString())
7890
- },
7891
- // Status history
8147
+ hasCliScripts: this.hasCliScripts(),
8148
+ scriptNames: Object.keys(this.cliScripts).filter((k) => typeof this.cliScripts[k] === "function"),
7892
8149
  statusHistory: this.statusHistory.slice(-30),
7893
- // Timeouts config
7894
8150
  timeouts: this.timeouts,
7895
- // PTY alive
7896
8151
  ptyAlive: !!this.ptyProcess
7897
8152
  };
7898
8153
  }
@@ -7958,21 +8213,27 @@ var init_cli_provider_instance = __esm({
7958
8213
  async onTick() {
7959
8214
  }
7960
8215
  getState() {
7961
- const adapterStatus = this.adapter.getStatus();
8216
+ const rawStatus = this.adapter.getStatus();
8217
+ const parsedStatus = this.adapter.getScriptParsedStatus();
8218
+ const adapterStatus = parsedStatus ? {
8219
+ ...rawStatus,
8220
+ messages: parsedStatus.messages || rawStatus.messages,
8221
+ activeModal: parsedStatus.activeModal || rawStatus.activeModal
8222
+ } : rawStatus;
7962
8223
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
7963
- const recentMessages = adapterStatus.messages.slice(-50).map((m) => ({
7964
- role: m.role,
7965
- content: m.content.length > 2e3 ? m.content.slice(0, 2e3) + "\n... (truncated)" : m.content,
7966
- timestamp: m.timestamp
7967
- }));
8224
+ const recentMessages = adapterStatus.messages.slice(-50).map((m) => {
8225
+ const content = typeof m.content === "string" && m.content.length > 8e3 ? m.content.slice(0, 8e3) + "\n... (truncated)" : m.content;
8226
+ return { ...m, content };
8227
+ });
7968
8228
  const partial2 = this.adapter.getPartialResponse();
7969
8229
  if (adapterStatus.status === "generating" && partial2) {
7970
8230
  const cleaned = partial2.trim();
7971
8231
  if (cleaned && cleaned !== "(generating...)") {
7972
8232
  recentMessages.push({
7973
8233
  role: "assistant",
7974
- content: (cleaned.length > 2e3 ? cleaned.slice(0, 2e3) + "..." : cleaned) + "...",
7975
- timestamp: Date.now()
8234
+ content: (cleaned.length > 8e3 ? cleaned.slice(0, 8e3) + "..." : cleaned) + "...",
8235
+ timestamp: Date.now(),
8236
+ meta: { streaming: true }
7976
8237
  });
7977
8238
  }
7978
8239
  }
@@ -8011,6 +8272,8 @@ var init_cli_provider_instance = __esm({
8011
8272
  this.adapter.sendMessage(data.text);
8012
8273
  } else if (event === "server_connected" && data?.serverConn) {
8013
8274
  this.adapter.setServerConn(data.serverConn);
8275
+ } else if (event === "resolve_action" && data) {
8276
+ this.adapter.resolveAction(data);
8014
8277
  }
8015
8278
  }
8016
8279
  dispose() {
@@ -25397,7 +25660,8 @@ var init_cli_manager = __esm({
25397
25660
  const provider = this.providerLoader.getMeta(normalizedType);
25398
25661
  if (provider && provider.category === "cli" && provider.patterns && provider.spawn) {
25399
25662
  console.log(import_chalk.default.cyan(` \u{1F4E6} Using provider: ${provider.name} (${provider.type})`));
25400
- return new ProviderCliAdapter(provider, workingDir, cliArgs);
25663
+ const resolvedProvider = this.providerLoader.resolve(normalizedType) || provider;
25664
+ return new ProviderCliAdapter(resolvedProvider, workingDir, cliArgs);
25401
25665
  }
25402
25666
  throw new Error(`No CLI provider found for '${cliType}'. Create a provider.js in providers/cli/${cliType}/`);
25403
25667
  }
@@ -25482,7 +25746,8 @@ ${installInfo}`
25482
25746
  }
25483
25747
  const instanceManager = this.deps.getInstanceManager();
25484
25748
  if (provider && instanceManager) {
25485
- const cliInstance = new CliProviderInstance(provider, resolvedDir, cliArgs, key);
25749
+ const resolvedProvider = this.providerLoader.resolve(cliType, { version: cliInfo.version }) || provider;
25750
+ const cliInstance = new CliProviderInstance(resolvedProvider, resolvedDir, cliArgs, key);
25486
25751
  try {
25487
25752
  await instanceManager.addInstance(key, cliInstance, {
25488
25753
  serverConn: this.deps.getServerConn(),
@@ -28747,6 +29012,46 @@ var init_dev_server = __esm({
28747
29012
  }
28748
29013
  }
28749
29014
  // ─── Phase 2: Auto-Implement Backend ───
29015
+ getDefaultAutoImplReference(category, type) {
29016
+ if (category === "cli") {
29017
+ return type === "codex-cli" ? "claude-cli" : "codex-cli";
29018
+ }
29019
+ return "antigravity";
29020
+ }
29021
+ resolveAutoImplReference(category, requestedReference, targetType) {
29022
+ const desired = requestedReference || this.getDefaultAutoImplReference(category, targetType);
29023
+ const ref = this.providerLoader.resolve(desired) || this.providerLoader.getMeta(desired);
29024
+ if (ref?.category === category) return desired;
29025
+ const all = this.providerLoader.getAll();
29026
+ const fallback = all.find((p) => p.category === category && p.type !== targetType);
29027
+ return fallback?.type || null;
29028
+ }
29029
+ loadAutoImplReferenceScripts(category, referenceType) {
29030
+ if (!referenceType) return {};
29031
+ const builtinDir = this.providerLoader.getPrimaryBuiltinDir();
29032
+ const refDir = path12.join(builtinDir, category, referenceType);
29033
+ if (!fs9.existsSync(refDir)) return {};
29034
+ const referenceScripts = {};
29035
+ const scriptsDir = path12.join(refDir, "scripts");
29036
+ if (!fs9.existsSync(scriptsDir)) return referenceScripts;
29037
+ const versions = fs9.readdirSync(scriptsDir).filter((d) => {
29038
+ try {
29039
+ return fs9.statSync(path12.join(scriptsDir, d)).isDirectory();
29040
+ } catch {
29041
+ return false;
29042
+ }
29043
+ }).sort().reverse();
29044
+ if (versions.length === 0) return referenceScripts;
29045
+ const latestDir = path12.join(scriptsDir, versions[0]);
29046
+ for (const file2 of fs9.readdirSync(latestDir)) {
29047
+ if (!file2.endsWith(".js")) continue;
29048
+ try {
29049
+ referenceScripts[file2] = fs9.readFileSync(path12.join(latestDir, file2), "utf-8");
29050
+ } catch {
29051
+ }
29052
+ }
29053
+ return referenceScripts;
29054
+ }
28750
29055
  async handleAutoImplement(type, req, res) {
28751
29056
  const body = await this.readBody(req);
28752
29057
  const { agent = "claude-cli", functions, reference = "antigravity", model, comment } = body;
@@ -28769,36 +29074,26 @@ var init_dev_server = __esm({
28769
29074
  return;
28770
29075
  }
28771
29076
  try {
28772
- this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "analyzing", message: "\uC5D0\uC774\uC804\uD2B8 \uCD08\uAE30\uD654 (DOM \uD0D0\uC0C9 \uAD8C\uD55C \uBD80\uC5EC)..." } });
29077
+ const resolvedReference = this.resolveAutoImplReference(provider.category, reference, type);
29078
+ this.sendAutoImplSSE({
29079
+ event: "progress",
29080
+ data: {
29081
+ function: "_init",
29082
+ status: "analyzing",
29083
+ message: provider.category === "cli" ? "Initializing agent (granting CLI PTY debug access)..." : "Initializing agent (granting DOM access)..."
29084
+ }
29085
+ });
28773
29086
  const domContext = null;
28774
- this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "loading_reference", message: `\uB808\uD37C\uB7F0\uC2A4 \uC2A4\uD06C\uB9BD\uD2B8 \uB85C\uB4DC \uC911 (${reference})...` } });
28775
- let referenceScripts = {};
28776
- const builtinDir = this.providerLoader.getPrimaryBuiltinDir();
28777
- const refDir = path12.join(builtinDir, "ide", reference);
28778
- if (fs9.existsSync(refDir)) {
28779
- const scriptsDir = path12.join(refDir, "scripts");
28780
- if (fs9.existsSync(scriptsDir)) {
28781
- const versions = fs9.readdirSync(scriptsDir).filter((d) => {
28782
- try {
28783
- return fs9.statSync(path12.join(scriptsDir, d)).isDirectory();
28784
- } catch {
28785
- return false;
28786
- }
28787
- }).sort().reverse();
28788
- if (versions.length > 0) {
28789
- const latestDir = path12.join(scriptsDir, versions[0]);
28790
- for (const file2 of fs9.readdirSync(latestDir)) {
28791
- if (file2.endsWith(".js")) {
28792
- try {
28793
- referenceScripts[file2] = fs9.readFileSync(path12.join(latestDir, file2), "utf-8");
28794
- } catch {
28795
- }
28796
- }
28797
- }
28798
- }
29087
+ this.sendAutoImplSSE({
29088
+ event: "progress",
29089
+ data: {
29090
+ function: "_init",
29091
+ status: "loading_reference",
29092
+ message: `Loading reference script (${resolvedReference || "none"})...`
28799
29093
  }
28800
- }
28801
- const prompt = this.buildAutoImplPrompt(type, provider, providerDir, functions, domContext, referenceScripts, comment);
29094
+ });
29095
+ const referenceScripts = this.loadAutoImplReferenceScripts(provider.category, resolvedReference);
29096
+ const prompt = this.buildAutoImplPrompt(type, provider, providerDir, functions, domContext, referenceScripts, comment, resolvedReference);
28802
29097
  const tmpDir = path12.join(os14.tmpdir(), "adhdev-autoimpl");
28803
29098
  if (!fs9.existsSync(tmpDir)) fs9.mkdirSync(tmpDir, { recursive: true });
28804
29099
  const promptFile = path12.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
@@ -28816,7 +29111,7 @@ var init_dev_server = __esm({
28816
29111
  }
28817
29112
  const agentCategory = agentProvider?.category;
28818
29113
  if (agentCategory === "acp") {
28819
- this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "spawning", message: `ACP \uC5D0\uC774\uC804\uD2B8 \uC2E4\uD589 \uC911: ${spawn3.command} ${(spawn3.args || []).join(" ")}` } });
29114
+ this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "spawning", message: `Spawning ACP agent: ${spawn3.command} ${(spawn3.args || []).join(" ")}` } });
28820
29115
  this.autoImplStatus = { running: true, type, progress: [] };
28821
29116
  const { ClientSideConnection: ClientSideConnection2, ndJsonStream: ndJsonStream2, PROTOCOL_VERSION: PROTOCOL_VERSION2 } = await Promise.resolve().then(() => (init_acp(), acp_exports));
28822
29117
  const { Readable: Readable2, Writable: Writable2 } = await import("stream");
@@ -28903,7 +29198,7 @@ var init_dev_server = __esm({
28903
29198
  this.autoImplProcess = null;
28904
29199
  this.autoImplStatus.running = false;
28905
29200
  const success2 = code === 0;
28906
- 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})` } });
29201
+ this.sendAutoImplSSE({ event: "complete", data: { success: success2, exitCode: code, functions, message: success2 ? "\u2705 ACP Auto-implement complete" : `\u274C ACP agent exited (code: ${code})` } });
28907
29202
  try {
28908
29203
  this.providerLoader.reload();
28909
29204
  } catch {
@@ -28918,16 +29213,16 @@ var init_dev_server = __esm({
28918
29213
  try {
28919
29214
  this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "initializing", message: "ACP initialize..." } });
28920
29215
  await connection.initialize({ protocolVersion: PROTOCOL_VERSION2, clientCapabilities: {} });
28921
- this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "session", message: "ACP session \uC0DD\uC131 \uC911..." } });
29216
+ this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "session", message: "Creating ACP session..." } });
28922
29217
  const session = await connection.newSession({ cwd: providerDir, mcpServers: [] });
28923
29218
  const sessionId = session?.sessionId;
28924
29219
  if (!sessionId) throw new Error("No sessionId returned from session/new");
28925
- this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "prompting", message: `\uD504\uB86C\uD504\uD2B8 \uC804\uC1A1 \uC911 (${prompt.length} chars)...` } });
29220
+ this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "prompting", message: `Sending prompt (${prompt.length} chars)...` } });
28926
29221
  await connection.prompt({
28927
29222
  sessionId,
28928
29223
  prompt: [{ type: "text", text: prompt }]
28929
29224
  });
28930
- this.sendAutoImplSSE({ event: "progress", data: { function: "_done", status: "complete", message: "\u2705 ACP \uD504\uB86C\uD504\uD2B8 \uCC98\uB9AC \uC644\uB8CC" } });
29225
+ this.sendAutoImplSSE({ event: "progress", data: { function: "_done", status: "complete", message: "\u2705 ACP prompt processing complete" } });
28931
29226
  } catch (e) {
28932
29227
  this.sendAutoImplSSE({ event: "output", data: { chunk: `[ACP Error] ${e.message}
28933
29228
  `, stream: "stderr" } });
@@ -28979,7 +29274,7 @@ var init_dev_server = __esm({
28979
29274
  const escapedArgs = baseArgs.map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(" ");
28980
29275
  shellCmd = `cat '${promptFile}' | ${command} ${escapedArgs}`;
28981
29276
  }
28982
- 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)` } });
29277
+ this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "spawning", message: `Spawning agent: ${shellCmd.substring(0, 200)}... (prompt: ${prompt.length} chars)` } });
28983
29278
  this.autoImplStatus = { running: true, type, progress: [] };
28984
29279
  const spawnedAt = Date.now();
28985
29280
  let child;
@@ -29075,7 +29370,7 @@ var init_dev_server = __esm({
29075
29370
  const success2 = code === 0;
29076
29371
  this.sendAutoImplSSE({
29077
29372
  event: "complete",
29078
- data: { success: success2, exitCode: code, functions, message: success2 ? "\u2705 Auto-implement \uC644\uB8CC" : `\u274C \uC5D0\uC774\uC804\uD2B8 \uC885\uB8CC (code: ${code})` }
29373
+ data: { success: success2, exitCode: code, functions, message: success2 ? "\u2705 Auto-implement complete" : `\u274C Agent exited (code: ${code})` }
29079
29374
  });
29080
29375
  try {
29081
29376
  this.providerLoader.reload();
@@ -29110,7 +29405,7 @@ var init_dev_server = __esm({
29110
29405
  success: success2,
29111
29406
  exitCode: code,
29112
29407
  functions,
29113
- message: success2 ? "\u2705 Auto-implement \uC644\uB8CC" : `\u274C \uC5D0\uC774\uC804\uD2B8 \uC885\uB8CC (code: ${code})`
29408
+ message: success2 ? "\u2705 Auto-implement complete" : `\u274C Agent exited (code: ${code})`
29114
29409
  }
29115
29410
  });
29116
29411
  try {
@@ -29138,7 +29433,10 @@ var init_dev_server = __esm({
29138
29433
  this.json(res, 500, { error: `Auto-implement failed: ${e.message}` });
29139
29434
  }
29140
29435
  }
29141
- buildAutoImplPrompt(type, provider, providerDir, functions, domContext, referenceScripts, userComment) {
29436
+ buildAutoImplPrompt(type, provider, providerDir, functions, domContext, referenceScripts, userComment, referenceType) {
29437
+ if (provider.category === "cli") {
29438
+ return this.buildCliAutoImplPrompt(type, provider, providerDir, functions, referenceScripts, userComment, referenceType);
29439
+ }
29142
29440
  const lines = [];
29143
29441
  lines.push("You are implementing browser automation scripts for an IDE provider.");
29144
29442
  lines.push("Be concise. Do NOT explain your reasoning. Just edit files directly.");
@@ -29201,7 +29499,7 @@ var init_dev_server = __esm({
29201
29499
  setMode: "set_mode.js"
29202
29500
  };
29203
29501
  if (Object.keys(referenceScripts).length > 0) {
29204
- lines.push("## Reference Implementation (from Antigravity provider)");
29502
+ lines.push(`## Reference Implementation (from ${referenceType || "antigravity"} provider)`);
29205
29503
  lines.push("These are WORKING scripts from another IDE. Adapt the PATTERNS (not selectors) for the target IDE.");
29206
29504
  lines.push("");
29207
29505
  for (const fn of functions) {
@@ -29350,6 +29648,150 @@ var init_dev_server = __esm({
29350
29648
  lines.push("Start NOW. Do not ask for permission. Explore the DOM -> Code -> Test.");
29351
29649
  return lines.join("\n");
29352
29650
  }
29651
+ buildCliAutoImplPrompt(type, provider, providerDir, functions, referenceScripts, userComment, referenceType) {
29652
+ const lines = [];
29653
+ lines.push("You are implementing PTY parsing scripts for a CLI provider.");
29654
+ lines.push("Be concise. Do NOT explain your reasoning. Edit files directly and verify with the local DevServer.");
29655
+ lines.push("");
29656
+ lines.push(`# Target: ${provider.name || type} (${type})`);
29657
+ lines.push(`Provider directory: \`${providerDir}\``);
29658
+ lines.push("Provider category: `cli`");
29659
+ lines.push("");
29660
+ lines.push("## Current Target Files");
29661
+ lines.push("These are the files you need to edit. Replace TODO or heuristic-only logic with working PTY-aware implementations.");
29662
+ lines.push("");
29663
+ const scriptsDir = path12.join(providerDir, "scripts");
29664
+ if (fs9.existsSync(scriptsDir)) {
29665
+ const versions = fs9.readdirSync(scriptsDir).filter((d) => {
29666
+ try {
29667
+ return fs9.statSync(path12.join(scriptsDir, d)).isDirectory();
29668
+ } catch {
29669
+ return false;
29670
+ }
29671
+ }).sort().reverse();
29672
+ if (versions.length > 0) {
29673
+ const vDir = path12.join(scriptsDir, versions[0]);
29674
+ lines.push(`Scripts version directory: \`${vDir}\``);
29675
+ lines.push("");
29676
+ for (const file2 of fs9.readdirSync(vDir)) {
29677
+ if (!file2.endsWith(".js")) continue;
29678
+ try {
29679
+ const content = fs9.readFileSync(path12.join(vDir, file2), "utf-8");
29680
+ lines.push(`### \`${file2}\``);
29681
+ lines.push("```javascript");
29682
+ lines.push(content);
29683
+ lines.push("```");
29684
+ lines.push("");
29685
+ } catch {
29686
+ }
29687
+ }
29688
+ }
29689
+ }
29690
+ const funcToFile = {
29691
+ parseOutput: "parse_output.js",
29692
+ detectStatus: "detect_status.js",
29693
+ parseApproval: "parse_approval.js"
29694
+ };
29695
+ if (Object.keys(referenceScripts).length > 0) {
29696
+ lines.push(`## Reference Implementation (from ${referenceType || "another CLI"} provider)`);
29697
+ lines.push("These are working CLI PTY parser scripts. Reuse the parsing shape and runtime contract, but adapt to the target CLI screen.");
29698
+ lines.push("");
29699
+ for (const fn of functions) {
29700
+ const fileName = funcToFile[fn];
29701
+ if (fileName && referenceScripts[fileName]) {
29702
+ lines.push(`### ${fn} \u2192 \`${fileName}\``);
29703
+ lines.push("```javascript");
29704
+ lines.push(referenceScripts[fileName]);
29705
+ lines.push("```");
29706
+ lines.push("");
29707
+ }
29708
+ }
29709
+ if (referenceScripts["scripts.js"]) {
29710
+ lines.push("### Router \u2192 `scripts.js`");
29711
+ lines.push("```javascript");
29712
+ lines.push(referenceScripts["scripts.js"]);
29713
+ lines.push("```");
29714
+ lines.push("");
29715
+ }
29716
+ }
29717
+ lines.push("## Runtime Contract");
29718
+ lines.push("The daemon runtime is already implemented in `packages/daemon-core/src/cli-adapters/provider-cli-adapter.ts`.");
29719
+ lines.push("Your scripts receive PTY-derived input and must return plain JS objects.");
29720
+ lines.push("");
29721
+ lines.push("| Function | Input | Return |");
29722
+ lines.push("|---|---|---|");
29723
+ lines.push("| `parseOutput` | `{ buffer, rawBuffer, recentBuffer, screenText, messages, partialResponse }` | `{ id, status, title, messages, activeModal }` |");
29724
+ lines.push("| `detectStatus` | `{ tail }` | `idle`, `generating`, `waiting_approval`, or `error` |");
29725
+ lines.push("| `parseApproval` | `{ buffer, rawBuffer, tail }` | `{ message, buttons }` or `null` |");
29726
+ lines.push("");
29727
+ lines.push("## Rules");
29728
+ lines.push("1. These scripts run in Node.js CommonJS, not in the browser. Do NOT use DOM APIs.");
29729
+ lines.push("2. Prefer `screenText` for current visible UI state. That is the PTY equivalent of parsing the current IDE DOM.");
29730
+ lines.push("3. Use `messages` as prior transcript state so redraws do not duplicate old turns on every parse.");
29731
+ lines.push("4. Use `partialResponse` for the actively streaming assistant text when status is `generating`.");
29732
+ lines.push("5. `detectStatus` must stay lightweight and tail-based. Do not scan the entire history there.");
29733
+ lines.push("6. `parseApproval` should understand the live approval area and return clean button labels.");
29734
+ lines.push("7. Use `rawBuffer` only when ANSI/control-sequence artifacts matter. Do not depend on raw escape noise unless necessary.");
29735
+ lines.push("8. Keep exports compatible with the existing `scripts.js` router (`module.exports = function ...`).");
29736
+ lines.push("9. Do not rewrite unrelated provider config. Only touch the scripts needed for this task unless a tiny supporting change is required.");
29737
+ lines.push("");
29738
+ lines.push("## Task");
29739
+ lines.push(`Edit files in \`${providerDir}\` to implement: **${functions.join(", ")}**`);
29740
+ lines.push("");
29741
+ lines.push("## Verification API");
29742
+ lines.push("Use the DevServer CLI debug endpoints, not DOM/CDP routes.");
29743
+ lines.push("");
29744
+ lines.push("### 1. Launch the target CLI");
29745
+ lines.push("```bash");
29746
+ lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/launch \\`);
29747
+ lines.push(' -H "Content-Type: application/json" \\');
29748
+ lines.push(` -d '{"type":"${type}","workingDir":"${providerDir.replace(/\\/g, "\\\\")}"}'`);
29749
+ lines.push("```");
29750
+ lines.push("");
29751
+ lines.push("### 2. Inspect parsed + raw adapter state");
29752
+ lines.push("```bash");
29753
+ lines.push(`curl -sS http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/debug/${type}`);
29754
+ lines.push(`curl -sS http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/status`);
29755
+ lines.push("```");
29756
+ lines.push("");
29757
+ lines.push("### 3. Send a rich test prompt");
29758
+ lines.push("```bash");
29759
+ lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/send \\`);
29760
+ lines.push(' -H "Content-Type: application/json" \\');
29761
+ lines.push(` -d '{"type":"${type}","text":"Write a short python snippet, include a markdown table, and briefly explain what you did."}'`);
29762
+ lines.push("```");
29763
+ lines.push("");
29764
+ lines.push("### 4. If approval appears, resolve it");
29765
+ lines.push("```bash");
29766
+ lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/resolve \\`);
29767
+ lines.push(' -H "Content-Type: application/json" \\');
29768
+ lines.push(` -d '{"type":"${type}","buttonIndex":0}'`);
29769
+ lines.push("```");
29770
+ lines.push("");
29771
+ lines.push("### 5. Stop the CLI when finished");
29772
+ lines.push("```bash");
29773
+ lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/stop \\`);
29774
+ lines.push(' -H "Content-Type: application/json" \\');
29775
+ lines.push(` -d '{"type":"${type}"}'`);
29776
+ lines.push("```");
29777
+ lines.push("");
29778
+ lines.push("## Required Validation");
29779
+ lines.push("1. Confirm `detectStatus` changes sensibly between startup, generating, approval, and idle.");
29780
+ lines.push("2. Confirm `parseOutput` produces a stable transcript without duplicating past turns when the PTY redraws.");
29781
+ lines.push("3. Confirm the latest assistant message streams through `partialResponse` while generation is in progress.");
29782
+ lines.push("4. Confirm approval parsing returns meaningful button labels when the CLI requests permission.");
29783
+ lines.push("5. Re-run the debug endpoints after edits. Do NOT finish until the parsed result looks correct.");
29784
+ lines.push("");
29785
+ if (userComment) {
29786
+ lines.push("## \u26A0\uFE0F User Instructions (HIGH PRIORITY)");
29787
+ lines.push("The user has provided the following additional instructions. Follow them strictly:");
29788
+ lines.push("");
29789
+ lines.push(userComment);
29790
+ lines.push("");
29791
+ }
29792
+ lines.push("Start NOW. Launch the CLI, inspect PTY state, edit the scripts, and verify via the CLI debug endpoints.");
29793
+ return lines.join("\n");
29794
+ }
29353
29795
  handleAutoImplSSE(type, req, res) {
29354
29796
  res.writeHead(200, {
29355
29797
  "Content-Type": "text/event-stream",
@@ -29377,7 +29819,7 @@ data: ${JSON.stringify(p.data)}
29377
29819
  setTimeout(() => {
29378
29820
  if (this.autoImplProcess) this.autoImplProcess.kill("SIGKILL");
29379
29821
  }, 3e3);
29380
- this.sendAutoImplSSE({ event: "complete", data: { success: false, exitCode: -1, message: "\u26D4 \uC0AC\uC6A9\uC790\uC5D0 \uC758\uD574 \uC911\uB2E8\uB428" } });
29822
+ this.sendAutoImplSSE({ event: "complete", data: { success: false, exitCode: -1, message: "\u26D4 Aborted by user" } });
29381
29823
  this.autoImplProcess = null;
29382
29824
  this.autoImplStatus.running = false;
29383
29825
  this.json(res, 200, { cancelled: true });
@@ -31189,11 +31631,12 @@ ${e?.stack || ""}`);
31189
31631
  });
31190
31632
 
31191
31633
  // src/screenshot-controller.ts
31192
- var ScreenshotController;
31634
+ var import_sharp, ScreenshotController;
31193
31635
  var init_screenshot_controller = __esm({
31194
31636
  "src/screenshot-controller.ts"() {
31195
31637
  "use strict";
31196
31638
  init_src();
31639
+ import_sharp = __toESM(require("sharp"));
31197
31640
  ScreenshotController = class _ScreenshotController {
31198
31641
  deps;
31199
31642
  timer = null;
@@ -31220,12 +31663,16 @@ var init_screenshot_controller = __esm({
31220
31663
  this.profileDirect = {
31221
31664
  minInterval: Math.max(300, planMinIntervalMs),
31222
31665
  maxInterval: Math.max(2e3, planMinIntervalMs),
31223
- quality: 25
31666
+ quality: 25,
31667
+ maxLongEdge: 1728,
31668
+ firstFrameLongEdge: 1920
31224
31669
  };
31225
31670
  this.profileRelay = {
31226
31671
  minInterval: Math.max(700, planMinIntervalMs),
31227
31672
  maxInterval: Math.max(3e3, planMinIntervalMs),
31228
- quality: 12
31673
+ quality: 12,
31674
+ maxLongEdge: 1152,
31675
+ firstFrameLongEdge: 1280
31229
31676
  };
31230
31677
  this.currentInterval = this.profileDirect.maxInterval;
31231
31678
  this.dailyBudgetMinutes = planLimits?.dailyScreenshotMinutes ?? -1;
@@ -31298,6 +31745,7 @@ var init_screenshot_controller = __esm({
31298
31745
  const sizeMatch = buf.length === this.lastSize;
31299
31746
  const hashMatch = hash2 === this.lastHash;
31300
31747
  const anyNeedsFirstFrame = this.deps.hasAnyNeedingFirstFrame();
31748
+ const resizeTarget = anyNeedsFirstFrame ? profile.firstFrameLongEdge : profile.maxLongEdge;
31301
31749
  if (sizeMatch && hashMatch && !anyNeedsFirstFrame) {
31302
31750
  this.staticFrameCount++;
31303
31751
  if (this.staticFrameCount >= this.STATIC_THRESHOLD) {
@@ -31307,13 +31755,14 @@ var init_screenshot_controller = __esm({
31307
31755
  LOG.debug("Screenshot", `skip (unchanged, static=${this.staticFrameCount}, interval=${this.currentInterval}ms, ${isRelay ? "RELAY" : "DIRECT"})`);
31308
31756
  }
31309
31757
  } else {
31758
+ const normalizedBuf = await this.normalizeBuffer(buf, resizeTarget, profile.quality);
31310
31759
  this.lastSize = buf.length;
31311
31760
  this.lastHash = hash2;
31312
31761
  this.staticFrameCount = 0;
31313
31762
  this.currentInterval = profile.minInterval;
31314
- const sent = this.deps.sendScreenshotBuffer(buf);
31763
+ const sent = this.deps.sendScreenshotBuffer(normalizedBuf);
31315
31764
  if (this.debugCount <= 3 || anyNeedsFirstFrame) {
31316
- LOG.debug("Screenshot", `sent: ${buf.length} bytes, delivered=${sent}, interval=${this.currentInterval}ms, ${isRelay ? "RELAY" : "DIRECT"}${anyNeedsFirstFrame ? " (first-frame)" : ""}`);
31765
+ LOG.debug("Screenshot", `sent: ${normalizedBuf.length} bytes, delivered=${sent}, interval=${this.currentInterval}ms, ${isRelay ? "RELAY" : "DIRECT"}${anyNeedsFirstFrame ? " (first-frame)" : ""}`);
31317
31766
  }
31318
31767
  }
31319
31768
  } else {
@@ -31361,6 +31810,28 @@ var init_screenshot_controller = __esm({
31361
31810
  }
31362
31811
  return h;
31363
31812
  }
31813
+ /** Normalize screenshot resolution before transport so UX feels consistent across machines/DPI. */
31814
+ async normalizeBuffer(buf, maxLongEdge, quality) {
31815
+ try {
31816
+ const image = (0, import_sharp.default)(buf, { failOn: "none" });
31817
+ const meta3 = await image.metadata();
31818
+ const width = meta3.width || 0;
31819
+ const height = meta3.height || 0;
31820
+ if (!width || !height) return buf;
31821
+ const longEdge = Math.max(width, height);
31822
+ if (longEdge <= maxLongEdge) return buf;
31823
+ return await image.resize({
31824
+ width: width >= height ? maxLongEdge : void 0,
31825
+ height: height > width ? maxLongEdge : void 0,
31826
+ fit: "inside",
31827
+ withoutEnlargement: true,
31828
+ kernel: import_sharp.default.kernel.lanczos3
31829
+ }).webp({ quality, effort: 4 }).toBuffer();
31830
+ } catch (e) {
31831
+ LOG.debug("Screenshot", `normalize skipped: ${e?.message || e}`);
31832
+ return buf;
31833
+ }
31834
+ }
31364
31835
  };
31365
31836
  }
31366
31837
  });
@@ -31423,7 +31894,7 @@ var init_adhdev_daemon = __esm({
31423
31894
  fs11 = __toESM(require("fs"));
31424
31895
  path14 = __toESM(require("path"));
31425
31896
  import_chalk2 = __toESM(require("chalk"));
31426
- pkgVersion = "0.6.52";
31897
+ pkgVersion = "0.6.55";
31427
31898
  if (pkgVersion === "unknown") {
31428
31899
  try {
31429
31900
  const possiblePaths = [
@@ -32824,6 +33295,42 @@ function registerDaemonCommands(program2, pkgVersion3) {
32824
33295
  // src/cli/provider-commands.ts
32825
33296
  var import_chalk6 = __toESM(require("chalk"));
32826
33297
  init_cdp_utils();
33298
+ var IDE_AUTO_FIX_FUNCTIONS = [
33299
+ "openPanel",
33300
+ "sendMessage",
33301
+ "readChat",
33302
+ "newSession",
33303
+ "listSessions",
33304
+ "switchSession",
33305
+ "resolveAction",
33306
+ "listModels",
33307
+ "setModel",
33308
+ "listModes",
33309
+ "setMode",
33310
+ "focusEditor"
33311
+ ];
33312
+ var CLI_AUTO_FIX_FUNCTIONS = [
33313
+ "parseOutput",
33314
+ "detectStatus",
33315
+ "parseApproval"
33316
+ ];
33317
+ var AUTO_FIX_SUPPORTED_CATEGORIES = /* @__PURE__ */ new Set(["ide", "extension", "cli"]);
33318
+ function getAutoFixFunctions(category) {
33319
+ if (category === "cli") return CLI_AUTO_FIX_FUNCTIONS;
33320
+ return IDE_AUTO_FIX_FUNCTIONS;
33321
+ }
33322
+ function getDefaultAutoFixReference(category, type, providers) {
33323
+ if (category === "cli") {
33324
+ const preferred = ["codex-cli", "claude-cli", "gemini-cli"];
33325
+ const picked = preferred.find(
33326
+ (ref) => ref !== type && providers.some((p) => p.type === ref && p.category === "cli")
33327
+ );
33328
+ if (picked) return picked;
33329
+ const fallback = providers.find((p) => p.category === "cli" && p.type !== type);
33330
+ return fallback?.type || "codex-cli";
33331
+ }
33332
+ return "antigravity";
33333
+ }
32827
33334
  function hideCommand2(command) {
32828
33335
  command.hideHelp?.();
32829
33336
  return command;
@@ -33098,7 +33605,7 @@ function registerProviderCommands(program2) {
33098
33605
  process.exit(1);
33099
33606
  }
33100
33607
  });
33101
- provider.command("fix [type] [scripts...]").description("Auto-implement provider scripts using AI reading the target IDE DOM").option("-a, --agent <agent>", "AI agent to use (e.g. claude-cli, gemini-cli, codex-cli, or any ACP provider like cline-acp)", "codex-cli").option("-m, --model <model>", "Model override (e.g. claude-sonnet-3.5, gemini-2.0-pro)").option("-r, --reference <ref>", "Reference provider to learn from", "antigravity").option("-c, --comment <text>", 'Additional instructions for the AI agent (e.g. "focus on shadow DOM handling")').action(async (typeArg, scripts, options) => {
33608
+ provider.command("fix [type] [scripts...]").description("Auto-implement provider scripts using AI for IDE DOM or CLI PTY parsing").option("-a, --agent <agent>", "AI agent to use (e.g. claude-cli, gemini-cli, codex-cli, or any ACP provider like cline-acp)", "codex-cli").option("-m, --model <model>", "Model override (e.g. claude-sonnet-3.5, gemini-2.0-pro)").option("-r, --reference <ref>", "Reference provider to learn from").option("-c, --comment <text>", "Additional instructions for the AI agent").action(async (typeArg, scripts, options) => {
33102
33609
  try {
33103
33610
  const http3 = await import("http");
33104
33611
  const inquirer2 = (await import("inquirer")).default;
@@ -33113,17 +33620,17 @@ function registerProviderCommands(program2) {
33113
33620
  };
33114
33621
  let type = typeArg;
33115
33622
  if (!type) {
33116
- const ideProviders = allProviders.filter((p) => p.category === "ide");
33117
- if (ideProviders.length === 0) {
33118
- console.log(import_chalk6.default.red("\n\u2717 No IDE providers found.\n"));
33623
+ const supportedProviders = allProviders.filter((p) => AUTO_FIX_SUPPORTED_CATEGORIES.has(p.category));
33624
+ if (supportedProviders.length === 0) {
33625
+ console.log(import_chalk6.default.red("\n\u2717 No IDE/extension/CLI providers found.\n"));
33119
33626
  process.exit(1);
33120
33627
  }
33121
33628
  const typeAnswer = await inquirer2.prompt([{
33122
33629
  type: "list",
33123
33630
  name: "selected",
33124
- message: "Select the IDE provider you want to fix:",
33125
- choices: ideProviders.map((p) => ({
33126
- name: `${p.name} ${isUserProvider(p) ? import_chalk6.default.yellow("[user]") : import_chalk6.default.gray("[upstream]")}`,
33631
+ message: "Select the provider you want to fix:",
33632
+ choices: supportedProviders.map((p) => ({
33633
+ name: `[${p.category}] ${p.name} ${isUserProvider(p) ? import_chalk6.default.yellow("[user]") : import_chalk6.default.gray("[upstream]")}`,
33127
33634
  value: p.type
33128
33635
  }))
33129
33636
  }]);
@@ -33134,6 +33641,18 @@ function registerProviderCommands(program2) {
33134
33641
  process.exit(1);
33135
33642
  }
33136
33643
  const providerToFix = allProviders.find((p) => p.type === type);
33644
+ if (!providerToFix) {
33645
+ console.log(import_chalk6.default.red(`
33646
+ \u2717 Unknown provider: ${type}
33647
+ `));
33648
+ process.exit(1);
33649
+ }
33650
+ if (!AUTO_FIX_SUPPORTED_CATEGORIES.has(providerToFix.category)) {
33651
+ console.log(import_chalk6.default.red(`
33652
+ \u2717 Provider category '${providerToFix.category}' is not supported by adhdev provider fix.
33653
+ `));
33654
+ process.exit(1);
33655
+ }
33137
33656
  if (providerToFix && !isUserProvider(providerToFix)) {
33138
33657
  console.log(import_chalk6.default.yellow(`
33139
33658
  \u26A0\uFE0F [${type}] is an upstream provider.`));
@@ -33194,7 +33713,7 @@ function registerProviderCommands(program2) {
33194
33713
  }
33195
33714
  let agentName = options.agent || "codex-cli";
33196
33715
  const modelName = options.model;
33197
- const reference = options.reference || "antigravity";
33716
+ const reference = options.reference || getDefaultAutoFixReference(providerToFix.category, type, allProviders);
33198
33717
  if (!typeArg && agentName === "codex-cli") {
33199
33718
  const agentAnswer = await inquirer2.prompt([{
33200
33719
  type: "list",
@@ -33211,23 +33730,20 @@ function registerProviderCommands(program2) {
33211
33730
  }
33212
33731
  console.log(import_chalk6.default.bold(`
33213
33732
  \u{1F916} Starting Auto-Implement Agent for [${import_chalk6.default.cyan(type)}]`));
33214
- console.log(import_chalk6.default.gray(` Agent: ${agentName}${modelName ? ` (model: ${modelName})` : ""} | Reference: ${reference}
33733
+ console.log(import_chalk6.default.gray(` Category: ${providerToFix.category} | Agent: ${agentName}${modelName ? ` (model: ${modelName})` : ""} | Reference: ${reference}
33215
33734
  `));
33216
- const allFunctions = [
33217
- "openPanel",
33218
- "sendMessage",
33219
- "readChat",
33220
- "newSession",
33221
- "listSessions",
33222
- "switchSession",
33223
- "resolveAction",
33224
- "listModels",
33225
- "setModel",
33226
- "listModes",
33227
- "setMode",
33228
- "focusEditor"
33229
- ];
33735
+ const allFunctions = getAutoFixFunctions(providerToFix.category);
33230
33736
  let functionsToFix = scripts;
33737
+ if (functionsToFix.length > 0) {
33738
+ const invalid = functionsToFix.filter((fn) => !allFunctions.includes(fn));
33739
+ if (invalid.length > 0) {
33740
+ console.log(import_chalk6.default.red(`
33741
+ \u2717 Unsupported scripts for ${providerToFix.category} provider [${type}]: ${invalid.join(", ")}`));
33742
+ console.log(import_chalk6.default.gray(` Valid scripts: ${allFunctions.join(", ")}
33743
+ `));
33744
+ process.exit(1);
33745
+ }
33746
+ }
33231
33747
  if (!scripts || scripts.length === 0) {
33232
33748
  const inquirer3 = (await import("inquirer")).default;
33233
33749
  const answer = await inquirer3.prompt([{
@@ -33302,7 +33818,8 @@ function registerProviderCommands(program2) {
33302
33818
  const loader2 = new ProviderLoader3();
33303
33819
  loader2.loadAll();
33304
33820
  const providerMeta = loader2.getMeta(type);
33305
- const targetDir = providerMeta ? loader2.getUserProviderDir(providerMeta.category, type) : pathMod.join(loader2.getUserDir(), type);
33821
+ if (!providerMeta) throw new Error(`Unknown provider: ${type}`);
33822
+ const targetDir = loader2.getUserProviderDir(providerMeta.category, type);
33306
33823
  const fsMock = await import("fs");
33307
33824
  const logFile2 = pathMod.join(targetDir, `auto-impl.log`);
33308
33825
  fsMock.writeFileSync(logFile2, `=== Auto-Impl Started ===
@@ -33486,17 +34003,13 @@ function registerProviderCommands(program2) {
33486
34003
  const loader = new ProviderLoader2();
33487
34004
  loader.loadAll();
33488
34005
  const providerMeta = loader.getMeta(type);
34006
+ if (!providerMeta) {
34007
+ return { error: `Provider '${type}' not found` };
34008
+ }
33489
34009
  const possiblePaths = [
33490
- ...providerMeta ? [
33491
- pathMod.join(loader.getUserProviderDir(providerMeta.category, type), "provider.js"),
33492
- pathMod.join(loader.getUpstreamProviderDir(providerMeta.category, type), "provider.js"),
33493
- pathMod.join(loader.getBuiltinProviderDir(providerMeta.category, type), "provider.js")
33494
- ] : [],
33495
- pathMod.join(loader.getUserDir(), type, "provider.js"),
33496
- pathMod.join(loader.getUpstreamDir(), "ide", type, "provider.js"),
33497
- pathMod.join(loader.getUpstreamDir(), "extension", type, "provider.js"),
33498
- pathMod.join(loader.getPrimaryBuiltinDir(), "ide", type, "provider.js"),
33499
- pathMod.join(loader.getPrimaryBuiltinDir(), "extension", type, "provider.js")
34010
+ pathMod.join(loader.getUserProviderDir(providerMeta.category, type), "provider.js"),
34011
+ pathMod.join(loader.getUpstreamProviderDir(providerMeta.category, type), "provider.js"),
34012
+ pathMod.join(loader.getBuiltinProviderDir(providerMeta.category, type), "provider.js")
33500
34013
  ];
33501
34014
  for (const p of possiblePaths) {
33502
34015
  if (fsMod.existsSync(p)) {