adhdev 0.6.53 → 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" };
@@ -6183,8 +6204,9 @@ var init_provider_loader = __esm({
6183
6204
  }
6184
6205
  }
6185
6206
  compareVersions(a, b) {
6186
- const pa = a.split(".").map(Number);
6187
- 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);
6188
6210
  for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
6189
6211
  const va = pa[i] || 0;
6190
6212
  const vb = pb[i] || 0;
@@ -7236,6 +7258,246 @@ var init_reporter = __esm({
7236
7258
  }
7237
7259
  });
7238
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
+
7239
7501
  // ../daemon-core/src/cli-adapters/provider-cli-adapter.ts
7240
7502
  var provider_cli_adapter_exports = {};
7241
7503
  __export(provider_cli_adapter_exports, {
@@ -7308,28 +7570,19 @@ function parsePatternEntry(x) {
7308
7570
  }
7309
7571
  return null;
7310
7572
  }
7311
- function coercePatternArray(raw, fallbacks) {
7312
- if (!Array.isArray(raw)) return [...fallbacks];
7313
- const parsed = raw.map(parsePatternEntry).filter((r) => r != null);
7314
- return parsed.length > 0 ? parsed : [...fallbacks];
7315
- }
7316
- function defaultCleanOutput(raw, _lastUserInput) {
7317
- return stripAnsi(raw).trim();
7573
+ function coercePatternArray(raw) {
7574
+ if (!Array.isArray(raw)) return [];
7575
+ return raw.map(parsePatternEntry).filter((r) => r != null);
7318
7576
  }
7319
7577
  function normalizeCliProviderForRuntime(raw) {
7320
7578
  const patterns = raw?.patterns || {};
7321
7579
  return {
7322
- ...raw,
7323
7580
  patterns: {
7324
- prompt: coercePatternArray(patterns.prompt, FALLBACK_PROMPT),
7325
- generating: coercePatternArray(patterns.generating, FALLBACK_GENERATING),
7326
- approval: coercePatternArray(patterns.approval, FALLBACK_APPROVAL),
7327
- ready: coercePatternArray(patterns.ready, [])
7328
- },
7329
- cleanOutput: typeof raw?.cleanOutput === "function" ? raw.cleanOutput : defaultCleanOutput
7581
+ approval: coercePatternArray(patterns.approval)
7582
+ }
7330
7583
  };
7331
7584
  }
7332
- var os11, path9, import_child_process5, pty, FALLBACK_PROMPT, FALLBACK_GENERATING, FALLBACK_APPROVAL, ProviderCliAdapter;
7585
+ var os11, path9, import_child_process5, pty, ProviderCliAdapter;
7333
7586
  var init_provider_cli_adapter = __esm({
7334
7587
  "../daemon-core/src/cli-adapters/provider-cli-adapter.ts"() {
7335
7588
  "use strict";
@@ -7337,6 +7590,7 @@ var init_provider_cli_adapter = __esm({
7337
7590
  path9 = __toESM(require("path"));
7338
7591
  import_child_process5 = require("child_process");
7339
7592
  init_logger();
7593
+ init_terminal_screen();
7340
7594
  try {
7341
7595
  pty = require("node-pty");
7342
7596
  if (os11.platform() !== "win32") {
@@ -7358,40 +7612,10 @@ var init_provider_cli_adapter = __esm({
7358
7612
  } catch {
7359
7613
  LOG.error("CLI", "[ProviderCliAdapter] node-pty not found. Terminal features disabled.");
7360
7614
  }
7361
- FALLBACK_PROMPT = [
7362
- /Type your message/i,
7363
- /for\s*shortcuts/i,
7364
- // Claude Code prompt
7365
- /\?\s*for\s*help/i,
7366
- // Claude Code help prompt
7367
- /Press enter/i,
7368
- /^[>›❯]\s*$/i,
7369
- // Prompt char as the complete evaluated string
7370
- /[>›❯]\s*$/
7371
- // Prompt char at the very end of evaluated string
7372
- ];
7373
- FALLBACK_GENERATING = [
7374
- /[\u2800-\u28ff]/,
7375
- // Braille spinner blocks (universal TUI)
7376
- /esc to (cancel|interrupt|stop)/i,
7377
- // Common TUI generation status line
7378
- /generating\.\.\./i,
7379
- /Claude is (?:thinking|processing|working)/i
7380
- // Specific Claude Code status
7381
- ];
7382
- FALLBACK_APPROVAL = [
7383
- /Allow\s*once/i,
7384
- // ANSI strip may remove spaces
7385
- /Always\s*allow/i,
7386
- /\(y\/n\)/i,
7387
- /\[Y\/n\]/i,
7388
- /Yes,?\s*don'?t\s*ask/i
7389
- // "Yes, don't ask again" (Claude Code)
7390
- ];
7391
- ProviderCliAdapter = class {
7615
+ ProviderCliAdapter = class _ProviderCliAdapter {
7392
7616
  constructor(provider, workingDir, extraArgs = []) {
7393
7617
  this.extraArgs = extraArgs;
7394
- this.provider = normalizeCliProviderForRuntime(provider);
7618
+ this.provider = provider;
7395
7619
  this.cliType = provider.type;
7396
7620
  this.cliName = provider.name;
7397
7621
  this.workingDir = workingDir.startsWith("~") ? workingDir.replace(/^~/, os11.homedir()) : workingDir;
@@ -7408,6 +7632,13 @@ var init_provider_cli_adapter = __esm({
7408
7632
  };
7409
7633
  const rawKeys = provider.approvalKeys;
7410
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
+ }
7411
7642
  }
7412
7643
  cliType;
7413
7644
  cliName;
@@ -7415,6 +7646,7 @@ var init_provider_cli_adapter = __esm({
7415
7646
  provider;
7416
7647
  ptyProcess = null;
7417
7648
  messages = [];
7649
+ structuredMessages = [];
7418
7650
  currentStatus = "starting";
7419
7651
  onStatusChange = null;
7420
7652
  responseBuffer = "";
@@ -7425,7 +7657,6 @@ var init_provider_cli_adapter = __esm({
7425
7657
  idleTimeout = null;
7426
7658
  ready = false;
7427
7659
  startupBuffer = "";
7428
- /** After spawn: briefly skip generating/settle so splash/redraw does not flip status; not used to gate sendMessage */
7429
7660
  startupParseGate = false;
7430
7661
  spawnAt = 0;
7431
7662
  // PTY I/O
@@ -7443,11 +7674,20 @@ var init_provider_cli_adapter = __esm({
7443
7674
  // Output settle debounce — fires after PTY output goes quiet
7444
7675
  settleTimer = null;
7445
7676
  settledBuffer = "";
7446
- // snapshot of recentOutputBuffer at settle time
7447
7677
  // Resize redraw suppression
7448
7678
  resizeSuppressUntil = 0;
7449
7679
  // Debug: status transition history
7450
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;
7451
7691
  setStatus(status, trigger) {
7452
7692
  const prev = this.currentStatus;
7453
7693
  if (prev === status) return;
@@ -7456,10 +7696,16 @@ var init_provider_cli_adapter = __esm({
7456
7696
  if (this.statusHistory.length > 50) this.statusHistory.shift();
7457
7697
  LOG.info("CLI", `[${this.cliType}] status: ${prev} \u2192 ${status}${trigger ? ` (${trigger})` : ""}`);
7458
7698
  }
7459
- // Resolved timeouts (provider defaults + overrides)
7699
+ // Resolved timeouts
7460
7700
  timeouts;
7461
- // Provider approval key mapping (e.g. { 0: '1', 1: '2', 2: '3' }) — loaded from provider.json
7701
+ // Provider approval key mapping
7462
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
+ }
7463
7709
  // ─── Lifecycle ─────────────────────────────────
7464
7710
  setServerConn(serverConn) {
7465
7711
  this.serverConn = serverConn;
@@ -7548,15 +7794,19 @@ var init_provider_cli_adapter = __esm({
7548
7794
  this.spawnAt = Date.now();
7549
7795
  this.startupParseGate = true;
7550
7796
  this.startupBuffer = "";
7797
+ this.terminalScreen.reset(40, 120);
7551
7798
  this.ready = true;
7552
7799
  this.setStatus("idle", "pty_ready");
7553
7800
  this.onStatusChange?.();
7554
7801
  }
7555
- // ─── Output state machine ────────────────────────────
7802
+ // ─── Output Handling ────────────────────────────
7556
7803
  handleOutput(rawData) {
7557
7804
  if (Date.now() < this.resizeSuppressUntil) return;
7805
+ this.terminalScreen.write(rawData);
7558
7806
  const cleanData = stripAnsi(rawData);
7559
- const { patterns } = this.provider;
7807
+ if (this.isWaitingForResponse && cleanData) {
7808
+ this.responseBuffer = (this.responseBuffer + cleanData).slice(-8e3);
7809
+ }
7560
7810
  if (cleanData.trim()) {
7561
7811
  if (this.serverConn) {
7562
7812
  this.serverConn.sendMessage("log", { message: cleanData.trim(), level: "info" });
@@ -7565,9 +7815,10 @@ var init_provider_cli_adapter = __esm({
7565
7815
  }
7566
7816
  }
7567
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);
7568
7820
  if (this.startupParseGate) {
7569
7821
  this.startupBuffer += cleanData;
7570
- LOG.info("CLI", `[${this.cliType}] startup chunk (${cleanData.length} chars): ${cleanData.slice(0, 200).replace(/\n/g, "\\n")}`);
7571
7822
  const dialogPatterns = [
7572
7823
  /Do you want to connect/i,
7573
7824
  /Do you trust the files/i,
@@ -7582,60 +7833,17 @@ var init_provider_cli_adapter = __esm({
7582
7833
  }
7583
7834
  const elapsed = Date.now() - this.spawnAt;
7584
7835
  const bufCap = this.startupBuffer.length > 12e3;
7585
- const promptMatched = patterns.prompt.some((p) => p.test(this.startupBuffer));
7586
- if (promptMatched || elapsed > 8e3 || bufCap) {
7836
+ const scriptStatus = this.runDetectStatus(this.startupBuffer);
7837
+ const isReady = scriptStatus === "idle" || elapsed > 8e3 || bufCap;
7838
+ if (isReady) {
7587
7839
  this.startupParseGate = false;
7588
- if (promptMatched) {
7589
- LOG.info("CLI", `[${this.cliType}] \u2713 Startup gate end (prompt matched)`);
7590
- } else {
7591
- LOG.info("CLI", `[${this.cliType}] startup gate end (${elapsed}ms, cap=${bufCap}, prompt=${promptMatched})`);
7592
- }
7840
+ LOG.info("CLI", `[${this.cliType}] Startup gate end (${elapsed}ms, scriptStatus=${scriptStatus})`);
7593
7841
  } else {
7594
7842
  return;
7595
7843
  }
7596
7844
  }
7597
- if (cleanData.trim().length > 5) {
7598
- LOG.debug("CLI", `[${this.cliType}] output chunk (${cleanData.length}): ${cleanData.slice(0, 300).replace(/\n/g, "\\n")}`);
7599
- }
7600
- if (!this.isWaitingForResponse) {
7601
- if (patterns.generating.some((p) => p.test(cleanData))) {
7602
- if (this.settleTimer) {
7603
- clearTimeout(this.settleTimer);
7604
- this.settleTimer = null;
7605
- }
7606
- this.isWaitingForResponse = true;
7607
- this.responseBuffer = "";
7608
- this.setStatus("generating", "autonomous_gen");
7609
- this.onStatusChange?.();
7610
- }
7611
- }
7612
- if (this.isWaitingForResponse) {
7613
- this.responseBuffer += cleanData;
7614
- if (this.idleTimeout) clearTimeout(this.idleTimeout);
7615
- if (patterns.generating.some((p) => p.test(cleanData))) {
7616
- this.setStatus("generating", "still_generating");
7617
- this.idleTimeout = setTimeout(() => {
7618
- if (this.isWaitingForResponse) this.finishResponse();
7619
- }, this.timeouts.generatingIdle);
7620
- this.onStatusChange?.();
7621
- if (this.settleTimer) {
7622
- clearTimeout(this.settleTimer);
7623
- this.settleTimer = null;
7624
- }
7625
- return;
7626
- }
7627
- }
7628
- if (this.currentStatus === "waiting_approval") {
7629
- this.approvalTransitionBuffer = (this.approvalTransitionBuffer + cleanData).slice(-500);
7630
- this.scheduleSettle();
7631
- return;
7632
- }
7633
7845
  this.scheduleSettle();
7634
7846
  }
7635
- /**
7636
- * Fired after output goes quiet for outputSettle ms.
7637
- * Evaluates the stabilised buffer for approval, prompt (idle), or timeout.
7638
- */
7639
7847
  scheduleSettle() {
7640
7848
  if (this.settleTimer) clearTimeout(this.settleTimer);
7641
7849
  this.settleTimer = setTimeout(() => {
@@ -7645,59 +7853,25 @@ var init_provider_cli_adapter = __esm({
7645
7853
  }, this.timeouts.outputSettle);
7646
7854
  }
7647
7855
  evaluateSettled() {
7648
- const { patterns } = this.provider;
7649
- const buf = this.settledBuffer;
7650
- if (this.currentStatus === "waiting_approval") {
7651
- const genResume = patterns.generating.some((p) => p.test(this.approvalTransitionBuffer));
7652
- const promptResume = patterns.prompt.some((p) => p.test(this.approvalTransitionBuffer));
7653
- if (genResume) {
7654
- if (this.approvalExitTimeout) {
7655
- clearTimeout(this.approvalExitTimeout);
7656
- this.approvalExitTimeout = null;
7657
- }
7658
- this.setStatus("generating", "approval_gen_resume");
7659
- this.activeModal = null;
7660
- this.recentOutputBuffer = "";
7661
- this.approvalTransitionBuffer = "";
7662
- this.lastApprovalResolvedAt = Date.now();
7663
- this.onStatusChange?.();
7664
- } else if (promptResume) {
7665
- if (this.approvalExitTimeout) {
7666
- clearTimeout(this.approvalExitTimeout);
7667
- this.approvalExitTimeout = null;
7668
- }
7669
- this.activeModal = null;
7670
- this.recentOutputBuffer = "";
7671
- this.approvalTransitionBuffer = "";
7672
- this.lastApprovalResolvedAt = Date.now();
7673
- this.finishResponse();
7674
- }
7675
- return;
7676
- }
7677
- const hasApproval = patterns.approval.some((p) => p.test(buf));
7678
- 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") {
7679
7861
  const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
7680
7862
  if (!inCooldown) {
7681
- const ctxLines = buf.split("\n").map((l) => l.trim()).filter((l) => l && !/^[─═╭╮╰╯│]+$/.test(l));
7682
7863
  this.isWaitingForResponse = true;
7683
- this.setStatus("waiting_approval", "approval_pattern");
7684
- this.recentOutputBuffer = "";
7685
- this.approvalTransitionBuffer = "";
7686
- this.activeModal = {
7687
- message: ctxLines.slice(-5).join(" ").slice(0, 200) || "Approval required",
7688
- buttons: this.cliType === "claude-cli" ? ["Yes (y)", "Always allow (a)", "Deny (Esc)"] : ["Allow once", "Always allow", "Deny"]
7689
- };
7864
+ this.setStatus("waiting_approval", "script_detect");
7865
+ const modal = this.runParseApproval(tail);
7866
+ this.activeModal = modal || { message: "Approval required", buttons: ["Allow", "Deny"] };
7690
7867
  if (this.idleTimeout) clearTimeout(this.idleTimeout);
7691
7868
  if (this.approvalExitTimeout) clearTimeout(this.approvalExitTimeout);
7692
7869
  this.approvalExitTimeout = setTimeout(() => {
7693
7870
  if (this.currentStatus === "waiting_approval") {
7694
- LOG.warn("CLI", `[${this.cliType}] Approval timeout \u2014 auto-exiting waiting_approval`);
7871
+ LOG.warn("CLI", `[${this.cliType}] Approval timeout \u2014 auto-clearing`);
7695
7872
  this.activeModal = null;
7696
7873
  this.lastApprovalResolvedAt = Date.now();
7697
- this.recentOutputBuffer = "";
7698
- this.approvalTransitionBuffer = "";
7699
- this.approvalExitTimeout = null;
7700
- this.setStatus(this.isWaitingForResponse ? "generating" : "idle", "approval_cleared");
7874
+ this.setStatus("idle", "approval_timeout");
7701
7875
  this.onStatusChange?.();
7702
7876
  }
7703
7877
  }, 6e4);
@@ -7705,19 +7879,42 @@ var init_provider_cli_adapter = __esm({
7705
7879
  return;
7706
7880
  }
7707
7881
  }
7708
- if (this.isWaitingForResponse) {
7709
- const trailingLines = buf.split("\n").slice(-3).join("\n");
7710
- if (patterns.prompt.some((p) => p.test(trailingLines)) && !hasApproval) {
7711
- this.finishResponse();
7712
- } else if (!patterns.generating.some((p) => p.test(buf))) {
7713
- if (this.idleTimeout) clearTimeout(this.idleTimeout);
7714
- this.idleTimeout = setTimeout(() => {
7715
- if (this.isWaitingForResponse && this.responseBuffer.trim()) {
7716
- this.finishResponse();
7717
- }
7718
- }, 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 = "";
7719
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);
7720
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
+ }
7721
7918
  }
7722
7919
  }
7723
7920
  finishResponse() {
@@ -7733,25 +7930,50 @@ var init_provider_cli_adapter = __esm({
7733
7930
  clearTimeout(this.approvalExitTimeout);
7734
7931
  this.approvalExitTimeout = null;
7735
7932
  }
7736
- const lastUserText = this.messages.filter((m) => m.role === "user").pop()?.content;
7737
- let response = this.provider.cleanOutput(this.responseBuffer, lastUserText);
7738
- if (lastUserText && response) {
7739
- const userTrimmed = lastUserText.trim();
7740
- response = response.split("\n").filter((l) => l.trim() !== userTrimmed).join("\n").trim();
7741
- }
7742
- if (response) {
7743
- this.messages.push({ role: "assistant", content: response, timestamp: Date.now() });
7744
- if (this.messages.length > 200) this.messages = this.messages.slice(-200);
7745
- LOG.info("CLI", `[${this.cliType}] Response (${response.length} chars)`);
7746
- }
7747
7933
  this.responseBuffer = "";
7748
7934
  this.isWaitingForResponse = false;
7749
7935
  this.activeModal = null;
7750
7936
  this.setStatus("idle", "response_finished");
7751
7937
  this.onStatusChange?.();
7752
7938
  }
7753
- // ─── 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) ───────────────────
7754
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
+ }
7755
7977
  return {
7756
7978
  status: this.currentStatus,
7757
7979
  messages: [...this.messages],
@@ -7759,11 +7981,71 @@ var init_provider_cli_adapter = __esm({
7759
7981
  activeModal: this.activeModal
7760
7982
  };
7761
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
+ }
7762
8043
  async sendMessage(text) {
7763
8044
  if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
7764
8045
  if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
7765
8046
  if (this.isWaitingForResponse) return;
7766
8047
  this.messages.push({ role: "user", content: text, timestamp: Date.now() });
8048
+ this.structuredMessages.push({ role: "user", content: text, timestamp: Date.now() });
7767
8049
  this.isWaitingForResponse = true;
7768
8050
  this.responseBuffer = "";
7769
8051
  this.setStatus("generating", "sendMessage");
@@ -7775,8 +8057,7 @@ var init_provider_cli_adapter = __esm({
7775
8057
  }
7776
8058
  getPartialResponse() {
7777
8059
  if (!this.isWaitingForResponse) return "";
7778
- const partial2 = this.provider.cleanOutput(this.responseBuffer);
7779
- return partial2 || (this.isWaitingForResponse ? "(generating...)" : "");
8060
+ return this.responseBuffer;
7780
8061
  }
7781
8062
  cancel() {
7782
8063
  this.shutdown();
@@ -7808,6 +8089,10 @@ var init_provider_cli_adapter = __esm({
7808
8089
  }
7809
8090
  clearHistory() {
7810
8091
  this.messages = [];
8092
+ this.structuredMessages = [];
8093
+ this.accumulatedBuffer = "";
8094
+ this.accumulatedRawBuffer = "";
8095
+ this.terminalScreen.reset();
7811
8096
  this.onStatusChange?.();
7812
8097
  }
7813
8098
  isProcessing() {
@@ -7819,11 +8104,6 @@ var init_provider_cli_adapter = __esm({
7819
8104
  writeRaw(data) {
7820
8105
  this.ptyProcess?.write(data);
7821
8106
  }
7822
- /**
7823
- * Resolve an approval modal by navigating to the button at `buttonIndex` and pressing Enter.
7824
- * Index 0 = first option (already selected by default — just Enter).
7825
- * Index N = press Arrow Down N times, then Enter.
7826
- */
7827
8107
  resolveModal(buttonIndex) {
7828
8108
  if (!this.ptyProcess || this.currentStatus !== "waiting_approval") return;
7829
8109
  if (buttonIndex in this.approvalKeys) {
@@ -7838,25 +8118,13 @@ var init_provider_cli_adapter = __esm({
7838
8118
  if (this.ptyProcess) {
7839
8119
  try {
7840
8120
  this.ptyProcess.resize(cols, rows);
8121
+ this.terminalScreen.resize(rows, cols);
7841
8122
  this.resizeSuppressUntil = Date.now() + 300;
7842
8123
  } catch {
7843
8124
  }
7844
8125
  }
7845
8126
  }
7846
- /**
7847
- * Full debug state — exposes all internal buffers, status, and patterns for debugging.
7848
- * Used by DevServer /api/cli/debug endpoint.
7849
- */
7850
8127
  getDebugState() {
7851
- const sb = this.startupBuffer;
7852
- const testOnStartup = (p) => {
7853
- const flags = p.flags.includes("g") ? p.flags.replace(/g/g, "") : p.flags;
7854
- return new RegExp(p.source, flags).test(sb);
7855
- };
7856
- const promptDiagnostics = this.provider.patterns.prompt.map((p) => ({
7857
- pattern: p.toString(),
7858
- matchedAgainstStartupBuffer: testOnStartup(p)
7859
- }));
7860
8128
  return {
7861
8129
  type: this.cliType,
7862
8130
  name: this.cliName,
@@ -7866,32 +8134,20 @@ var init_provider_cli_adapter = __esm({
7866
8134
  spawnAt: this.spawnAt,
7867
8135
  workingDir: this.workingDir,
7868
8136
  messages: this.messages.slice(-20),
8137
+ structuredMessages: this.structuredMessages.slice(-20),
7869
8138
  messageCount: this.messages.length,
7870
- // Buffers (longer tails here than in periodic logs — for matching provider.json patterns)
7871
- startupBuffer: sb.slice(-4e3),
7872
- startupBufferLength: sb.length,
7873
- promptDiagnostics,
8139
+ startupBuffer: this.startupBuffer.slice(-4e3),
7874
8140
  recentOutputBuffer: this.recentOutputBuffer.slice(-500),
7875
8141
  settledBuffer: this.settledBuffer.slice(-500),
7876
- responseBuffer: this.responseBuffer.slice(-500),
7877
- approvalTransitionBuffer: this.approvalTransitionBuffer.slice(-500),
7878
- // State
8142
+ accumulatedBufferLength: this.accumulatedBuffer.length,
7879
8143
  isWaitingForResponse: this.isWaitingForResponse,
7880
8144
  activeModal: this.activeModal,
7881
8145
  lastApprovalResolvedAt: this.lastApprovalResolvedAt,
7882
8146
  resizeSuppressUntil: this.resizeSuppressUntil,
7883
- // Provider patterns (serialized)
7884
- patterns: {
7885
- prompt: this.provider.patterns.prompt.map((p) => p.toString()),
7886
- generating: this.provider.patterns.generating.map((p) => p.toString()),
7887
- approval: this.provider.patterns.approval.map((p) => p.toString()),
7888
- ready: this.provider.patterns.ready.map((p) => p.toString())
7889
- },
7890
- // Status history
8147
+ hasCliScripts: this.hasCliScripts(),
8148
+ scriptNames: Object.keys(this.cliScripts).filter((k) => typeof this.cliScripts[k] === "function"),
7891
8149
  statusHistory: this.statusHistory.slice(-30),
7892
- // Timeouts config
7893
8150
  timeouts: this.timeouts,
7894
- // PTY alive
7895
8151
  ptyAlive: !!this.ptyProcess
7896
8152
  };
7897
8153
  }
@@ -7957,21 +8213,27 @@ var init_cli_provider_instance = __esm({
7957
8213
  async onTick() {
7958
8214
  }
7959
8215
  getState() {
7960
- 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;
7961
8223
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
7962
- const recentMessages = adapterStatus.messages.slice(-50).map((m) => ({
7963
- role: m.role,
7964
- content: m.content.length > 2e3 ? m.content.slice(0, 2e3) + "\n... (truncated)" : m.content,
7965
- timestamp: m.timestamp
7966
- }));
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
+ });
7967
8228
  const partial2 = this.adapter.getPartialResponse();
7968
8229
  if (adapterStatus.status === "generating" && partial2) {
7969
8230
  const cleaned = partial2.trim();
7970
8231
  if (cleaned && cleaned !== "(generating...)") {
7971
8232
  recentMessages.push({
7972
8233
  role: "assistant",
7973
- content: (cleaned.length > 2e3 ? cleaned.slice(0, 2e3) + "..." : cleaned) + "...",
7974
- timestamp: Date.now()
8234
+ content: (cleaned.length > 8e3 ? cleaned.slice(0, 8e3) + "..." : cleaned) + "...",
8235
+ timestamp: Date.now(),
8236
+ meta: { streaming: true }
7975
8237
  });
7976
8238
  }
7977
8239
  }
@@ -8010,6 +8272,8 @@ var init_cli_provider_instance = __esm({
8010
8272
  this.adapter.sendMessage(data.text);
8011
8273
  } else if (event === "server_connected" && data?.serverConn) {
8012
8274
  this.adapter.setServerConn(data.serverConn);
8275
+ } else if (event === "resolve_action" && data) {
8276
+ this.adapter.resolveAction(data);
8013
8277
  }
8014
8278
  }
8015
8279
  dispose() {
@@ -25396,7 +25660,8 @@ var init_cli_manager = __esm({
25396
25660
  const provider = this.providerLoader.getMeta(normalizedType);
25397
25661
  if (provider && provider.category === "cli" && provider.patterns && provider.spawn) {
25398
25662
  console.log(import_chalk.default.cyan(` \u{1F4E6} Using provider: ${provider.name} (${provider.type})`));
25399
- return new ProviderCliAdapter(provider, workingDir, cliArgs);
25663
+ const resolvedProvider = this.providerLoader.resolve(normalizedType) || provider;
25664
+ return new ProviderCliAdapter(resolvedProvider, workingDir, cliArgs);
25400
25665
  }
25401
25666
  throw new Error(`No CLI provider found for '${cliType}'. Create a provider.js in providers/cli/${cliType}/`);
25402
25667
  }
@@ -25481,7 +25746,8 @@ ${installInfo}`
25481
25746
  }
25482
25747
  const instanceManager = this.deps.getInstanceManager();
25483
25748
  if (provider && instanceManager) {
25484
- 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);
25485
25751
  try {
25486
25752
  await instanceManager.addInstance(key, cliInstance, {
25487
25753
  serverConn: this.deps.getServerConn(),
@@ -28746,6 +29012,46 @@ var init_dev_server = __esm({
28746
29012
  }
28747
29013
  }
28748
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
+ }
28749
29055
  async handleAutoImplement(type, req, res) {
28750
29056
  const body = await this.readBody(req);
28751
29057
  const { agent = "claude-cli", functions, reference = "antigravity", model, comment } = body;
@@ -28768,36 +29074,26 @@ var init_dev_server = __esm({
28768
29074
  return;
28769
29075
  }
28770
29076
  try {
28771
- 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
+ });
28772
29086
  const domContext = null;
28773
- this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "loading_reference", message: `\uB808\uD37C\uB7F0\uC2A4 \uC2A4\uD06C\uB9BD\uD2B8 \uB85C\uB4DC \uC911 (${reference})...` } });
28774
- let referenceScripts = {};
28775
- const builtinDir = this.providerLoader.getPrimaryBuiltinDir();
28776
- const refDir = path12.join(builtinDir, "ide", reference);
28777
- if (fs9.existsSync(refDir)) {
28778
- const scriptsDir = path12.join(refDir, "scripts");
28779
- if (fs9.existsSync(scriptsDir)) {
28780
- const versions = fs9.readdirSync(scriptsDir).filter((d) => {
28781
- try {
28782
- return fs9.statSync(path12.join(scriptsDir, d)).isDirectory();
28783
- } catch {
28784
- return false;
28785
- }
28786
- }).sort().reverse();
28787
- if (versions.length > 0) {
28788
- const latestDir = path12.join(scriptsDir, versions[0]);
28789
- for (const file2 of fs9.readdirSync(latestDir)) {
28790
- if (file2.endsWith(".js")) {
28791
- try {
28792
- referenceScripts[file2] = fs9.readFileSync(path12.join(latestDir, file2), "utf-8");
28793
- } catch {
28794
- }
28795
- }
28796
- }
28797
- }
29087
+ this.sendAutoImplSSE({
29088
+ event: "progress",
29089
+ data: {
29090
+ function: "_init",
29091
+ status: "loading_reference",
29092
+ message: `Loading reference script (${resolvedReference || "none"})...`
28798
29093
  }
28799
- }
28800
- 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);
28801
29097
  const tmpDir = path12.join(os14.tmpdir(), "adhdev-autoimpl");
28802
29098
  if (!fs9.existsSync(tmpDir)) fs9.mkdirSync(tmpDir, { recursive: true });
28803
29099
  const promptFile = path12.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
@@ -28815,7 +29111,7 @@ var init_dev_server = __esm({
28815
29111
  }
28816
29112
  const agentCategory = agentProvider?.category;
28817
29113
  if (agentCategory === "acp") {
28818
- 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(" ")}` } });
28819
29115
  this.autoImplStatus = { running: true, type, progress: [] };
28820
29116
  const { ClientSideConnection: ClientSideConnection2, ndJsonStream: ndJsonStream2, PROTOCOL_VERSION: PROTOCOL_VERSION2 } = await Promise.resolve().then(() => (init_acp(), acp_exports));
28821
29117
  const { Readable: Readable2, Writable: Writable2 } = await import("stream");
@@ -28902,7 +29198,7 @@ var init_dev_server = __esm({
28902
29198
  this.autoImplProcess = null;
28903
29199
  this.autoImplStatus.running = false;
28904
29200
  const success2 = code === 0;
28905
- 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})` } });
28906
29202
  try {
28907
29203
  this.providerLoader.reload();
28908
29204
  } catch {
@@ -28917,16 +29213,16 @@ var init_dev_server = __esm({
28917
29213
  try {
28918
29214
  this.sendAutoImplSSE({ event: "progress", data: { function: "_init", status: "initializing", message: "ACP initialize..." } });
28919
29215
  await connection.initialize({ protocolVersion: PROTOCOL_VERSION2, clientCapabilities: {} });
28920
- 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..." } });
28921
29217
  const session = await connection.newSession({ cwd: providerDir, mcpServers: [] });
28922
29218
  const sessionId = session?.sessionId;
28923
29219
  if (!sessionId) throw new Error("No sessionId returned from session/new");
28924
- 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)...` } });
28925
29221
  await connection.prompt({
28926
29222
  sessionId,
28927
29223
  prompt: [{ type: "text", text: prompt }]
28928
29224
  });
28929
- 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" } });
28930
29226
  } catch (e) {
28931
29227
  this.sendAutoImplSSE({ event: "output", data: { chunk: `[ACP Error] ${e.message}
28932
29228
  `, stream: "stderr" } });
@@ -28978,7 +29274,7 @@ var init_dev_server = __esm({
28978
29274
  const escapedArgs = baseArgs.map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(" ");
28979
29275
  shellCmd = `cat '${promptFile}' | ${command} ${escapedArgs}`;
28980
29276
  }
28981
- 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)` } });
28982
29278
  this.autoImplStatus = { running: true, type, progress: [] };
28983
29279
  const spawnedAt = Date.now();
28984
29280
  let child;
@@ -29074,7 +29370,7 @@ var init_dev_server = __esm({
29074
29370
  const success2 = code === 0;
29075
29371
  this.sendAutoImplSSE({
29076
29372
  event: "complete",
29077
- 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})` }
29078
29374
  });
29079
29375
  try {
29080
29376
  this.providerLoader.reload();
@@ -29109,7 +29405,7 @@ var init_dev_server = __esm({
29109
29405
  success: success2,
29110
29406
  exitCode: code,
29111
29407
  functions,
29112
- 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})`
29113
29409
  }
29114
29410
  });
29115
29411
  try {
@@ -29137,7 +29433,10 @@ var init_dev_server = __esm({
29137
29433
  this.json(res, 500, { error: `Auto-implement failed: ${e.message}` });
29138
29434
  }
29139
29435
  }
29140
- 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
+ }
29141
29440
  const lines = [];
29142
29441
  lines.push("You are implementing browser automation scripts for an IDE provider.");
29143
29442
  lines.push("Be concise. Do NOT explain your reasoning. Just edit files directly.");
@@ -29200,7 +29499,7 @@ var init_dev_server = __esm({
29200
29499
  setMode: "set_mode.js"
29201
29500
  };
29202
29501
  if (Object.keys(referenceScripts).length > 0) {
29203
- lines.push("## Reference Implementation (from Antigravity provider)");
29502
+ lines.push(`## Reference Implementation (from ${referenceType || "antigravity"} provider)`);
29204
29503
  lines.push("These are WORKING scripts from another IDE. Adapt the PATTERNS (not selectors) for the target IDE.");
29205
29504
  lines.push("");
29206
29505
  for (const fn of functions) {
@@ -29349,6 +29648,150 @@ var init_dev_server = __esm({
29349
29648
  lines.push("Start NOW. Do not ask for permission. Explore the DOM -> Code -> Test.");
29350
29649
  return lines.join("\n");
29351
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
+ }
29352
29795
  handleAutoImplSSE(type, req, res) {
29353
29796
  res.writeHead(200, {
29354
29797
  "Content-Type": "text/event-stream",
@@ -29376,7 +29819,7 @@ data: ${JSON.stringify(p.data)}
29376
29819
  setTimeout(() => {
29377
29820
  if (this.autoImplProcess) this.autoImplProcess.kill("SIGKILL");
29378
29821
  }, 3e3);
29379
- 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" } });
29380
29823
  this.autoImplProcess = null;
29381
29824
  this.autoImplStatus.running = false;
29382
29825
  this.json(res, 200, { cancelled: true });
@@ -31188,11 +31631,12 @@ ${e?.stack || ""}`);
31188
31631
  });
31189
31632
 
31190
31633
  // src/screenshot-controller.ts
31191
- var ScreenshotController;
31634
+ var import_sharp, ScreenshotController;
31192
31635
  var init_screenshot_controller = __esm({
31193
31636
  "src/screenshot-controller.ts"() {
31194
31637
  "use strict";
31195
31638
  init_src();
31639
+ import_sharp = __toESM(require("sharp"));
31196
31640
  ScreenshotController = class _ScreenshotController {
31197
31641
  deps;
31198
31642
  timer = null;
@@ -31219,12 +31663,16 @@ var init_screenshot_controller = __esm({
31219
31663
  this.profileDirect = {
31220
31664
  minInterval: Math.max(300, planMinIntervalMs),
31221
31665
  maxInterval: Math.max(2e3, planMinIntervalMs),
31222
- quality: 25
31666
+ quality: 25,
31667
+ maxLongEdge: 1728,
31668
+ firstFrameLongEdge: 1920
31223
31669
  };
31224
31670
  this.profileRelay = {
31225
31671
  minInterval: Math.max(700, planMinIntervalMs),
31226
31672
  maxInterval: Math.max(3e3, planMinIntervalMs),
31227
- quality: 12
31673
+ quality: 12,
31674
+ maxLongEdge: 1152,
31675
+ firstFrameLongEdge: 1280
31228
31676
  };
31229
31677
  this.currentInterval = this.profileDirect.maxInterval;
31230
31678
  this.dailyBudgetMinutes = planLimits?.dailyScreenshotMinutes ?? -1;
@@ -31297,6 +31745,7 @@ var init_screenshot_controller = __esm({
31297
31745
  const sizeMatch = buf.length === this.lastSize;
31298
31746
  const hashMatch = hash2 === this.lastHash;
31299
31747
  const anyNeedsFirstFrame = this.deps.hasAnyNeedingFirstFrame();
31748
+ const resizeTarget = anyNeedsFirstFrame ? profile.firstFrameLongEdge : profile.maxLongEdge;
31300
31749
  if (sizeMatch && hashMatch && !anyNeedsFirstFrame) {
31301
31750
  this.staticFrameCount++;
31302
31751
  if (this.staticFrameCount >= this.STATIC_THRESHOLD) {
@@ -31306,13 +31755,14 @@ var init_screenshot_controller = __esm({
31306
31755
  LOG.debug("Screenshot", `skip (unchanged, static=${this.staticFrameCount}, interval=${this.currentInterval}ms, ${isRelay ? "RELAY" : "DIRECT"})`);
31307
31756
  }
31308
31757
  } else {
31758
+ const normalizedBuf = await this.normalizeBuffer(buf, resizeTarget, profile.quality);
31309
31759
  this.lastSize = buf.length;
31310
31760
  this.lastHash = hash2;
31311
31761
  this.staticFrameCount = 0;
31312
31762
  this.currentInterval = profile.minInterval;
31313
- const sent = this.deps.sendScreenshotBuffer(buf);
31763
+ const sent = this.deps.sendScreenshotBuffer(normalizedBuf);
31314
31764
  if (this.debugCount <= 3 || anyNeedsFirstFrame) {
31315
- 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)" : ""}`);
31316
31766
  }
31317
31767
  }
31318
31768
  } else {
@@ -31360,6 +31810,28 @@ var init_screenshot_controller = __esm({
31360
31810
  }
31361
31811
  return h;
31362
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
+ }
31363
31835
  };
31364
31836
  }
31365
31837
  });
@@ -31422,7 +31894,7 @@ var init_adhdev_daemon = __esm({
31422
31894
  fs11 = __toESM(require("fs"));
31423
31895
  path14 = __toESM(require("path"));
31424
31896
  import_chalk2 = __toESM(require("chalk"));
31425
- pkgVersion = "0.6.53";
31897
+ pkgVersion = "0.6.55";
31426
31898
  if (pkgVersion === "unknown") {
31427
31899
  try {
31428
31900
  const possiblePaths = [
@@ -32823,6 +33295,42 @@ function registerDaemonCommands(program2, pkgVersion3) {
32823
33295
  // src/cli/provider-commands.ts
32824
33296
  var import_chalk6 = __toESM(require("chalk"));
32825
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
+ }
32826
33334
  function hideCommand2(command) {
32827
33335
  command.hideHelp?.();
32828
33336
  return command;
@@ -33097,7 +33605,7 @@ function registerProviderCommands(program2) {
33097
33605
  process.exit(1);
33098
33606
  }
33099
33607
  });
33100
- 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) => {
33101
33609
  try {
33102
33610
  const http3 = await import("http");
33103
33611
  const inquirer2 = (await import("inquirer")).default;
@@ -33112,17 +33620,17 @@ function registerProviderCommands(program2) {
33112
33620
  };
33113
33621
  let type = typeArg;
33114
33622
  if (!type) {
33115
- const ideProviders = allProviders.filter((p) => p.category === "ide");
33116
- if (ideProviders.length === 0) {
33117
- 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"));
33118
33626
  process.exit(1);
33119
33627
  }
33120
33628
  const typeAnswer = await inquirer2.prompt([{
33121
33629
  type: "list",
33122
33630
  name: "selected",
33123
- message: "Select the IDE provider you want to fix:",
33124
- choices: ideProviders.map((p) => ({
33125
- 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]")}`,
33126
33634
  value: p.type
33127
33635
  }))
33128
33636
  }]);
@@ -33133,6 +33641,18 @@ function registerProviderCommands(program2) {
33133
33641
  process.exit(1);
33134
33642
  }
33135
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
+ }
33136
33656
  if (providerToFix && !isUserProvider(providerToFix)) {
33137
33657
  console.log(import_chalk6.default.yellow(`
33138
33658
  \u26A0\uFE0F [${type}] is an upstream provider.`));
@@ -33193,7 +33713,7 @@ function registerProviderCommands(program2) {
33193
33713
  }
33194
33714
  let agentName = options.agent || "codex-cli";
33195
33715
  const modelName = options.model;
33196
- const reference = options.reference || "antigravity";
33716
+ const reference = options.reference || getDefaultAutoFixReference(providerToFix.category, type, allProviders);
33197
33717
  if (!typeArg && agentName === "codex-cli") {
33198
33718
  const agentAnswer = await inquirer2.prompt([{
33199
33719
  type: "list",
@@ -33210,23 +33730,20 @@ function registerProviderCommands(program2) {
33210
33730
  }
33211
33731
  console.log(import_chalk6.default.bold(`
33212
33732
  \u{1F916} Starting Auto-Implement Agent for [${import_chalk6.default.cyan(type)}]`));
33213
- 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}
33214
33734
  `));
33215
- const allFunctions = [
33216
- "openPanel",
33217
- "sendMessage",
33218
- "readChat",
33219
- "newSession",
33220
- "listSessions",
33221
- "switchSession",
33222
- "resolveAction",
33223
- "listModels",
33224
- "setModel",
33225
- "listModes",
33226
- "setMode",
33227
- "focusEditor"
33228
- ];
33735
+ const allFunctions = getAutoFixFunctions(providerToFix.category);
33229
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
+ }
33230
33747
  if (!scripts || scripts.length === 0) {
33231
33748
  const inquirer3 = (await import("inquirer")).default;
33232
33749
  const answer = await inquirer3.prompt([{