adhdev 0.6.75 → 0.6.77

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
@@ -560,6 +560,10 @@ var init_ide_detector = __esm({
560
560
  });
561
561
 
562
562
  // ../../oss/packages/daemon-core/src/detection/cli-detector.ts
563
+ function parseVersion(raw) {
564
+ const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
565
+ return match ? match[1] : raw.split("\n")[0].slice(0, 100);
566
+ }
563
567
  function execAsync(cmd, timeoutMs = 5e3) {
564
568
  return new Promise((resolve10) => {
565
569
  const child = (0, import_child_process2.exec)(cmd, { encoding: "utf-8", timeout: timeoutMs }, (err, stdout) => {
@@ -584,10 +588,18 @@ async function detectCLIs(providerLoader) {
584
588
  const firstPath = pathResult.split("\n")[0];
585
589
  let version2;
586
590
  try {
587
- const versionResult = await execAsync(`${cli.command} --version 2>/dev/null`, 3e3);
588
- if (versionResult) {
589
- const match = versionResult.match(/(\d+\.\d+[\.\d]*)/);
590
- version2 = match ? match[1] : versionResult.split("\n")[0].slice(0, 30);
591
+ const versionCommands = [
592
+ cli.versionCommand,
593
+ `${cli.command} --version 2>/dev/null`,
594
+ `${cli.command} -V 2>/dev/null`,
595
+ `${cli.command} -v 2>/dev/null`
596
+ ].filter((v2) => !!v2);
597
+ for (const versionCommand of versionCommands) {
598
+ const versionResult = await execAsync(versionCommand, 3e3);
599
+ if (versionResult) {
600
+ version2 = parseVersion(versionResult);
601
+ break;
602
+ }
591
603
  }
592
604
  } catch {
593
605
  }
@@ -3378,7 +3390,7 @@ function buildManagedClis(cliStates) {
3378
3390
  cliType: s15.type,
3379
3391
  cliName: s15.name,
3380
3392
  status: s15.status,
3381
- mode: s15.mode,
3393
+ mode: "terminal",
3382
3394
  workspace: s15.workspace || "",
3383
3395
  activeChat: s15.activeChat
3384
3396
  }));
@@ -4972,8 +4984,13 @@ var init_handler = __esm({
4972
4984
  getCliAdapter(type) {
4973
4985
  const target = type || this._currentIdeType;
4974
4986
  if (!target || !this._ctx.adapters) return null;
4987
+ let normalizedTarget = target;
4988
+ const colonIdx = normalizedTarget.lastIndexOf(":");
4989
+ if (colonIdx >= 0) normalizedTarget = normalizedTarget.substring(colonIdx + 1);
4990
+ const direct = this._ctx.adapters.get(normalizedTarget);
4991
+ if (direct) return direct;
4975
4992
  for (const [key, adapter] of this._ctx.adapters.entries()) {
4976
- if (adapter.cliType === target || key.startsWith(target)) {
4993
+ if (adapter.cliType === target || adapter.cliType === normalizedTarget || key === normalizedTarget || key.startsWith(target) || key.startsWith(normalizedTarget)) {
4977
4994
  return adapter;
4978
4995
  }
4979
4996
  }
@@ -5482,10 +5499,10 @@ var init_readdirp = __esm({
5482
5499
  }
5483
5500
  async _formatEntry(dirent, path15) {
5484
5501
  let entry;
5485
- const basename5 = this._isDirent ? dirent.name : dirent;
5502
+ const basename6 = this._isDirent ? dirent.name : dirent;
5486
5503
  try {
5487
- const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path15, basename5));
5488
- entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename5 };
5504
+ const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path15, basename6));
5505
+ entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename6 };
5489
5506
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
5490
5507
  } catch (err) {
5491
5508
  this._onError(err);
@@ -6016,9 +6033,9 @@ var init_handler2 = __esm({
6016
6033
  _watchWithNodeFs(path15, listener) {
6017
6034
  const opts = this.fsw.options;
6018
6035
  const directory = sp.dirname(path15);
6019
- const basename5 = sp.basename(path15);
6036
+ const basename6 = sp.basename(path15);
6020
6037
  const parent = this.fsw._getWatchedDir(directory);
6021
- parent.add(basename5);
6038
+ parent.add(basename6);
6022
6039
  const absolutePath = sp.resolve(path15);
6023
6040
  const options = {
6024
6041
  persistent: opts.persistent
@@ -6028,7 +6045,7 @@ var init_handler2 = __esm({
6028
6045
  let closer;
6029
6046
  if (opts.usePolling) {
6030
6047
  const enableBin = opts.interval !== opts.binaryInterval;
6031
- options.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
6048
+ options.interval = enableBin && isBinaryPath(basename6) ? opts.binaryInterval : opts.interval;
6032
6049
  closer = setFsWatchFileListener(path15, absolutePath, options, {
6033
6050
  listener,
6034
6051
  rawEmitter: this.fsw._emitRaw
@@ -6051,10 +6068,10 @@ var init_handler2 = __esm({
6051
6068
  return;
6052
6069
  }
6053
6070
  const dirname9 = sp.dirname(file2);
6054
- const basename5 = sp.basename(file2);
6071
+ const basename6 = sp.basename(file2);
6055
6072
  const parent = this.fsw._getWatchedDir(dirname9);
6056
6073
  let prevStats = stats;
6057
- if (parent.has(basename5))
6074
+ if (parent.has(basename6))
6058
6075
  return;
6059
6076
  const listener = async (path15, newStats) => {
6060
6077
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file2, 5))
@@ -6079,9 +6096,9 @@ var init_handler2 = __esm({
6079
6096
  prevStats = newStats2;
6080
6097
  }
6081
6098
  } catch (error48) {
6082
- this.fsw._remove(dirname9, basename5);
6099
+ this.fsw._remove(dirname9, basename6);
6083
6100
  }
6084
- } else if (parent.has(basename5)) {
6101
+ } else if (parent.has(basename6)) {
6085
6102
  const at2 = newStats.atimeMs;
6086
6103
  const mt2 = newStats.mtimeMs;
6087
6104
  if (!at2 || at2 <= mt2 || mt2 !== prevStats.mtimeMs) {
@@ -7236,12 +7253,15 @@ var init_provider_loader = __esm({
7236
7253
  const result = [];
7237
7254
  for (const p of this.providers.values()) {
7238
7255
  if ((p.category === "cli" || p.category === "acp") && p.spawn?.command) {
7256
+ const verCmdConfig = p.versionCommand;
7257
+ const versionCommand = typeof verCmdConfig === "object" && verCmdConfig !== null ? verCmdConfig[process.platform] : verCmdConfig;
7239
7258
  result.push({
7240
7259
  id: p.type,
7241
7260
  displayName: p.displayName || p.name,
7242
7261
  icon: p.icon || "\u{1F527}",
7243
7262
  command: p.spawn.command,
7244
- category: p.category
7263
+ category: p.category,
7264
+ ...typeof versionCommand === "string" && versionCommand.trim() ? { versionCommand: versionCommand.trim() } : {}
7245
7265
  });
7246
7266
  }
7247
7267
  }
@@ -18309,6 +18329,12 @@ __export(provider_cli_adapter_exports, {
18309
18329
  function stripAnsi(str) {
18310
18330
  return str.replace(/\x1B\[\d*[A-HJKSTfG]/g, " ").replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/\x1B\][^\x07]*\x07/g, "").replace(/\x1B\][^\x1B]*\x1B\\/g, "").replace(/ +/g, " ");
18311
18331
  }
18332
+ function stripTerminalNoise(str) {
18333
+ return String(str || "").replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, "").replace(/(^|[\s([])(?:\??\d{1,4}(?:;\d{1,4})*[A-Za-z])(?=$|[\s)\]])/g, "$1").replace(/(^|[\s([])(?:\[\??\d{1,4}(?:;\d{1,4})*[A-Za-z])(?=$|[\s)\]])/g, "$1").replace(/(^|[\s([])(?:\d{1,4};\?)(?=$|[\s)\]])/g, "$1").replace(/\r+/g, "\n").replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n").replace(/ {2,}/g, " ");
18334
+ }
18335
+ function sanitizeTerminalText(str) {
18336
+ return stripTerminalNoise(stripAnsi(str));
18337
+ }
18312
18338
  function findBinary(name) {
18313
18339
  const isWin = os12.platform() === "win32";
18314
18340
  try {
@@ -18541,6 +18567,9 @@ var init_provider_cli_adapter = __esm({
18541
18567
  // Approval state machine
18542
18568
  approvalTransitionBuffer = "";
18543
18569
  approvalExitTimeout = null;
18570
+ pendingScriptStatus = null;
18571
+ pendingScriptStatusSince = 0;
18572
+ pendingScriptStatusTimer = null;
18544
18573
  // Output settle debounce — fires after PTY output goes quiet
18545
18574
  settleTimer = null;
18546
18575
  settledBuffer = "";
@@ -18606,6 +18635,7 @@ var init_provider_cli_adapter = __esm({
18606
18635
  sendDelayMs;
18607
18636
  sendKey;
18608
18637
  submitStrategy;
18638
+ static SCRIPT_STATUS_DEBOUNCE_MS = 1e3;
18609
18639
  /** Inject CLI scripts after construction (e.g. when resolved by ProviderLoader) */
18610
18640
  setCliScripts(scripts) {
18611
18641
  this.cliScripts = scripts;
@@ -18732,7 +18762,7 @@ var init_provider_cli_adapter = __esm({
18732
18762
  handleOutput(rawData) {
18733
18763
  this.terminalScreen.write(rawData);
18734
18764
  this.terminalHistory = mergeTerminalHistory(this.terminalHistory, this.terminalScreen.getText());
18735
- const cleanData = stripAnsi(rawData);
18765
+ const cleanData = sanitizeTerminalText(rawData);
18736
18766
  if (this.isWaitingForResponse && cleanData) {
18737
18767
  this.responseBuffer = (this.responseBuffer + cleanData).slice(-8e3);
18738
18768
  }
@@ -18829,24 +18859,39 @@ var init_provider_cli_adapter = __esm({
18829
18859
  const scriptStatus = rawScriptStatus;
18830
18860
  if (!scriptStatus) return;
18831
18861
  const prevStatus = this.currentStatus;
18832
- if (scriptStatus === "waiting_approval") {
18833
- const modalMessage = modal?.message || "";
18834
- const screenText = this.terminalScreen.getText() || this.accumulatedBuffer;
18835
- const autoAcceptPatterns = [
18836
- /be able to read, edit, and execute/i,
18837
- /Security guide/i,
18838
- /Enter to confirm/i,
18839
- /Quick safety check/i,
18840
- /Do you trust the files/i,
18841
- /Is this a project/i
18842
- ];
18843
- if (autoAcceptPatterns.some((p) => p.test(modalMessage) || p.test(screenText))) {
18844
- LOG.info("CLI", `[${this.cliType}] Auto-accepting startup dialog: ${modalMessage.slice(0, 80)}`);
18845
- setTimeout(() => this.ptyProcess?.write("\r"), 200);
18846
- this.lastApprovalResolvedAt = Date.now();
18847
- this.activeModal = null;
18862
+ const clearPendingScriptStatus = () => {
18863
+ this.pendingScriptStatus = null;
18864
+ this.pendingScriptStatusSince = 0;
18865
+ if (this.pendingScriptStatusTimer) {
18866
+ clearTimeout(this.pendingScriptStatusTimer);
18867
+ this.pendingScriptStatusTimer = null;
18868
+ }
18869
+ };
18870
+ const armPendingScriptStatus = (delayMs) => {
18871
+ if (this.pendingScriptStatusTimer) clearTimeout(this.pendingScriptStatusTimer);
18872
+ this.pendingScriptStatusTimer = setTimeout(() => {
18873
+ this.pendingScriptStatusTimer = null;
18874
+ this.settledBuffer = this.recentOutputBuffer;
18875
+ this.evaluateSettled();
18876
+ }, delayMs);
18877
+ };
18878
+ const shouldDebouncePromotion = (status) => prevStatus === "idle" && !this.isWaitingForResponse && !this.currentTurnScope && (status === "generating" || status === "waiting_approval");
18879
+ if (shouldDebouncePromotion(scriptStatus)) {
18880
+ if (this.pendingScriptStatus !== scriptStatus) {
18881
+ this.pendingScriptStatus = scriptStatus;
18882
+ this.pendingScriptStatusSince = now;
18883
+ armPendingScriptStatus(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS);
18884
+ return;
18885
+ }
18886
+ const elapsed = now - this.pendingScriptStatusSince;
18887
+ if (elapsed < _ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS) {
18888
+ armPendingScriptStatus(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS - elapsed);
18848
18889
  return;
18849
18890
  }
18891
+ } else {
18892
+ clearPendingScriptStatus();
18893
+ }
18894
+ if (scriptStatus === "waiting_approval") {
18850
18895
  const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
18851
18896
  if (!inCooldown) {
18852
18897
  this.isWaitingForResponse = true;
@@ -18859,6 +18904,12 @@ var init_provider_cli_adapter = __esm({
18859
18904
  }
18860
18905
  }
18861
18906
  if (scriptStatus === "generating") {
18907
+ const screenText = this.terminalScreen.getText() || this.accumulatedBuffer;
18908
+ const noActiveTurn = !this.currentTurnScope;
18909
+ const looksIdleChrome = /(^|\n)\s*[❯›>]\s*(?:\n|$)/m.test(screenText) || /accept edits on/i.test(screenText) && (/Update available!/i.test(screenText) || /\/effort/i.test(screenText) || /^.*➜\s+\S+/m.test(screenText));
18910
+ if (prevStatus === "idle" && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome) {
18911
+ return;
18912
+ }
18862
18913
  if (prevStatus === "waiting_approval") {
18863
18914
  if (this.approvalExitTimeout) {
18864
18915
  clearTimeout(this.approvalExitTimeout);
@@ -19316,7 +19367,7 @@ ${data.message || ""}`.trim();
19316
19367
  committedMessages: this.committedMessages.slice(-20),
19317
19368
  structuredMessages: this.structuredMessages.slice(-20),
19318
19369
  messageCount: this.committedMessages.length,
19319
- screenText: this.terminalScreen.getText().slice(-4e3),
19370
+ screenText: sanitizeTerminalText(this.terminalScreen.getText()).slice(-4e3),
19320
19371
  terminalHistory: this.terminalHistory.slice(-8e3),
19321
19372
  currentTurnScope: this.currentTurnScope,
19322
19373
  startupBuffer: this.startupBuffer.slice(-4e3),
@@ -19325,6 +19376,7 @@ ${data.message || ""}`.trim();
19325
19376
  accumulatedBufferLength: this.accumulatedBuffer.length,
19326
19377
  accumulatedRawBufferLength: this.accumulatedRawBuffer.length,
19327
19378
  rawBufferPreview: this.accumulatedRawBuffer.slice(-1e3),
19379
+ sanitizedRawPreview: sanitizeTerminalText(this.accumulatedRawBuffer).slice(-1e3),
19328
19380
  responseBuffer: this.responseBuffer.slice(-1e3),
19329
19381
  isWaitingForResponse: this.isWaitingForResponse,
19330
19382
  activeModal: this.activeModal,
@@ -19408,19 +19460,6 @@ var init_cli_provider_instance = __esm({
19408
19460
  getState() {
19409
19461
  const adapterStatus = this.adapter.getStatus();
19410
19462
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
19411
- const recentMessages = adapterStatus.messages.slice(-50).map((m) => {
19412
- const content = typeof m.content === "string" && m.content.length > 8e3 ? m.content.slice(0, 8e3) + "\n... (truncated)" : m.content;
19413
- return { ...m, content };
19414
- });
19415
- if (recentMessages.length > 0) {
19416
- const dirName2 = this.workingDir.split("/").filter(Boolean).pop() || "session";
19417
- this.historyWriter.appendNewMessages(
19418
- this.type,
19419
- recentMessages,
19420
- `${this.provider.name} \xB7 ${dirName2}`,
19421
- this.instanceId
19422
- );
19423
- }
19424
19463
  if (adapterStatus.terminalHistory?.trim()) {
19425
19464
  this.historyWriter.appendTerminalHistory(
19426
19465
  this.type,
@@ -19434,12 +19473,12 @@ var init_cli_provider_instance = __esm({
19434
19473
  name: this.provider.name,
19435
19474
  category: "cli",
19436
19475
  status: adapterStatus.status,
19437
- mode: this.settings.mode || "terminal",
19476
+ mode: "terminal",
19438
19477
  activeChat: {
19439
19478
  id: `${this.type}_${this.workingDir}`,
19440
19479
  title: `${this.provider.name} \xB7 ${dirName}`,
19441
19480
  status: adapterStatus.status,
19442
- messages: recentMessages,
19481
+ messages: [],
19443
19482
  activeModal: adapterStatus.activeModal,
19444
19483
  terminalHistory: adapterStatus.terminalHistory,
19445
19484
  inputContent: ""
@@ -19453,11 +19492,15 @@ var init_cli_provider_instance = __esm({
19453
19492
  }
19454
19493
  onEvent(event, data) {
19455
19494
  if (event === "send_message" && data?.text) {
19456
- this.adapter.sendMessage(data.text);
19495
+ void this.adapter.sendMessage(data.text).catch((e) => {
19496
+ LOG.warn("CLI", `[${this.type}] send_message failed: ${e?.message || e}`);
19497
+ });
19457
19498
  } else if (event === "server_connected" && data?.serverConn) {
19458
19499
  this.adapter.setServerConn(data.serverConn);
19459
19500
  } else if (event === "resolve_action" && data) {
19460
- this.adapter.resolveAction(data);
19501
+ void this.adapter.resolveAction(data).catch((e) => {
19502
+ LOG.warn("CLI", `[${this.type}] resolve_action failed: ${e?.message || e}`);
19503
+ });
19461
19504
  }
19462
19505
  }
19463
19506
  dispose() {
@@ -37899,18 +37942,18 @@ function findBinary2(name) {
37899
37942
  const result = runCommand(cmd, 5e3);
37900
37943
  return result ? result.split("\n")[0] : null;
37901
37944
  }
37902
- function parseVersion(raw) {
37945
+ function parseVersion2(raw) {
37903
37946
  const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
37904
37947
  return match ? match[1] : raw.split("\n")[0].substring(0, 100);
37905
37948
  }
37906
37949
  function getVersion(binary, versionCommand) {
37907
37950
  if (versionCommand) {
37908
37951
  const raw = runCommand(versionCommand);
37909
- return raw ? parseVersion(raw) : null;
37952
+ return raw ? parseVersion2(raw) : null;
37910
37953
  }
37911
37954
  for (const flag of ["--version", "-V", "-v"]) {
37912
37955
  const raw = runCommand(`"${binary}" ${flag}`);
37913
- if (raw && raw.length < 500) return parseVersion(raw);
37956
+ if (raw && raw.length < 500) return parseVersion2(raw);
37914
37957
  }
37915
37958
  return null;
37916
37959
  }
@@ -40244,16 +40287,16 @@ var init_dev_server = __esm({
40244
40287
  resolveAutoImplWritableProviderDir(category, type, requestedDir) {
40245
40288
  const canonicalUserDir = path12.resolve(this.providerLoader.getUserProviderDir(category, type));
40246
40289
  const desiredDir = requestedDir ? path12.resolve(requestedDir) : canonicalUserDir;
40247
- if (desiredDir !== canonicalUserDir) {
40248
- return null;
40290
+ const upstreamRoot = path12.resolve(this.providerLoader.getUpstreamDir());
40291
+ if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path12.sep}`)) {
40292
+ return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
40249
40293
  }
40250
- const userRoot = path12.resolve(this.providerLoader.getUserDir());
40251
- if (desiredDir !== userRoot && !desiredDir.startsWith(`${userRoot}${path12.sep}`)) {
40252
- return null;
40294
+ if (path12.basename(desiredDir) !== type) {
40295
+ return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
40253
40296
  }
40254
40297
  const sourceDir = this.findProviderDir(type);
40255
40298
  if (!sourceDir) {
40256
- return null;
40299
+ return { dir: null, reason: `Provider source directory not found for '${type}'` };
40257
40300
  }
40258
40301
  if (!fs10.existsSync(desiredDir)) {
40259
40302
  fs10.mkdirSync(path12.dirname(desiredDir), { recursive: true });
@@ -40262,7 +40305,7 @@ var init_dev_server = __esm({
40262
40305
  }
40263
40306
  const providerJson = path12.join(desiredDir, "provider.json");
40264
40307
  if (!fs10.existsSync(providerJson)) {
40265
- return null;
40308
+ return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
40266
40309
  }
40267
40310
  try {
40268
40311
  const providerData = JSON.parse(fs10.readFileSync(providerJson, "utf-8"));
@@ -40270,10 +40313,13 @@ var init_dev_server = __esm({
40270
40313
  providerData.disableUpstream = true;
40271
40314
  fs10.writeFileSync(providerJson, JSON.stringify(providerData, null, 2));
40272
40315
  }
40273
- } catch {
40274
- return null;
40316
+ } catch (error48) {
40317
+ return {
40318
+ dir: null,
40319
+ reason: `Failed to update provider.json in writable provider directory: ${error48.message}`
40320
+ };
40275
40321
  }
40276
- return desiredDir;
40322
+ return { dir: desiredDir };
40277
40323
  }
40278
40324
  loadAutoImplReferenceScripts(referenceType) {
40279
40325
  if (!referenceType) return {};
@@ -40308,13 +40354,14 @@ var init_dev_server = __esm({
40308
40354
  this.json(res, 404, { error: `Provider not found: ${type}` });
40309
40355
  return;
40310
40356
  }
40311
- const providerDir = this.resolveAutoImplWritableProviderDir(provider.category, type, requestedProviderDir);
40312
- if (!providerDir) {
40357
+ const writableProvider = this.resolveAutoImplWritableProviderDir(provider.category, type, requestedProviderDir);
40358
+ if (!writableProvider.dir) {
40313
40359
  this.json(res, 409, {
40314
- error: `Auto-implement only writes to the canonical user provider directory for '${type}'.`
40360
+ error: writableProvider.reason || `Auto-implement only writes to the canonical user provider directory for '${type}'.`
40315
40361
  });
40316
40362
  return;
40317
40363
  }
40364
+ const providerDir = writableProvider.dir;
40318
40365
  try {
40319
40366
  const resolvedReference = this.resolveAutoImplReference(provider.category, reference, type);
40320
40367
  this.sendAutoImplSSE({
@@ -40815,14 +40862,16 @@ var init_dev_server = __esm({
40815
40862
  lines.push("| Status | When to use | How to detect |");
40816
40863
  lines.push("|---|---|---|");
40817
40864
  lines.push("| `idle` | AI is NOT generating, no approval needed | Default state. No stop button, no spinners, no approval pills/buttons |");
40818
- lines.push("| `generating` | AI is actively streaming/thinking | ANY of: (1) Stop/Cancel button visible, (2) CSS animation (animate-spin/pulse/bounce), (3) floating state text like Thinking/Generating/Sailing, (4) streaming indicator class |");
40865
+ lines.push('| `generating` | AI is actively streaming/thinking | ANY of: (1) Submit button icon SVG changes (e.g. arrow\u2192stop square, fill="none"\u2192fill="currentColor"), (2) Stop/Cancel button visible, (3) CSS animation, (4) Structural markers (aria-labels that only appear during generation) |');
40819
40866
  lines.push("| `waiting_approval` | AI stopped and needs user action | Actionable buttons like Run/Skip/Accept/Reject are visible AND clickable |");
40820
40867
  lines.push("");
40821
40868
  lines.push("### \u26A0\uFE0F Status Detection Gotchas (MUST READ!)");
40822
- lines.push('1. **FALSE POSITIVES from old messages**: Chat history may contain text like "Command Awaiting Approval" from PAST turns. If you search the entire chat panel for this text, you will get false matches from parent divs whose innerText includes ALL child text. ONLY match small leaf elements (under 80 chars) or use explicit button/pill selectors.');
40823
- lines.push('2. **Awaiting Approval pill without actions**: Some IDEs show a floating pill/banner saying "Awaiting Approval" that is just a scroll-to indicator (not an actual approval dialog). If this pill exists but NO actionable buttons (Run/Skip/Accept/Reject) exist anywhere in the panel, the status should be `idle`, NOT `waiting_approval`.');
40824
- lines.push("3. **generating detection must be multi-signal**: Do NOT rely on just one indicator. Check ALL of: stop buttons, CSS animations, floating state labels, streaming classes. IDEs differ widely.");
40825
- lines.push("4. **activeModal must include actions**: When `status` is `waiting_approval`, the `activeModal` object MUST include a non-empty `actions` array listing the button labels. If you cannot find any action buttons, the status is NOT `waiting_approval`.");
40869
+ lines.push(`1. **DO NOT rely on button text/labels in the user's language.** OS locale may be Korean, Japanese, etc. Button text like "Cancel" or "Stop" will be localized. Instead, detect STRUCTURAL indicators: SVG icon changes, CSS classes, aria-labels from the extension's own React/Radix UI (which stay in English regardless of OS locale).`);
40870
+ lines.push('2. **Use sendMessage to CREATE a generating state, then CAPTURE the DOM.** Send a LONG prompt (e.g. "Write an extremely detailed 5000-word essay...") so the AI takes 10+ seconds. Then periodically capture the DOM during generation to find which elements appear/change. Compare idle vs generating DOM snapshots to find reliable structural markers.');
40871
+ lines.push("3. **Look for SVG icon changes in the submit button.** Many IDEs change the submit button icon from an arrow (send) to a square (stop) during generation. Check the SVG `fill` attribute or path data.");
40872
+ lines.push('4. **FALSE POSITIVES from old messages**: Chat history may contain text like "Command Awaiting Approval" from PAST turns. ONLY match small leaf elements (under 80 chars) or use explicit button/pill selectors.');
40873
+ lines.push("5. **Awaiting Approval pill without actions**: Some IDEs show a floating pill/banner that is just a scroll-to indicator. If NO actionable buttons exist, the status should be `idle`, NOT `waiting_approval`.");
40874
+ lines.push("6. **activeModal must include actions**: When `status` is `waiting_approval`, the `activeModal` object MUST include a non-empty `actions` array.");
40826
40875
  lines.push("");
40827
40876
  lines.push("## Action");
40828
40877
  lines.push("1. Edit the script files to implement working code");
@@ -40880,10 +40929,10 @@ var init_dev_server = __esm({
40880
40929
  lines.push(`echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); r=d.get('result',d); r=json.loads(r) if isinstance(r,str) else r; assert r.get('status')=='idle', f'Expected idle, got {r.get(chr(34)+chr(115)+chr(116)+chr(97)+chr(116)+chr(117)+chr(115)+chr(34))}'; print('Step 1 PASS: status=idle')"`);
40881
40930
  lines.push("```");
40882
40931
  lines.push("");
40883
- lines.push("### Step 2: Send a message that triggers generation");
40932
+ lines.push("### Step 2: Send a LONG message that triggers extended generation (10+ seconds)");
40884
40933
  lines.push("```bash");
40885
- lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/scripts/run -H "Content-Type: application/json" -d '{"script": "sendMessage", "type": "${type}", "ideType": "${type}", "args": {"message": "Say hello in one word"}}'`);
40886
- lines.push("sleep 2");
40934
+ lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/scripts/run -H "Content-Type: application/json" -d '{"script": "sendMessage", "type": "${type}", "ideType": "${type}", "args": {"message": "Write an extremely detailed 5000-word essay about the history of artificial intelligence from Alan Turing to 2025. Be very thorough and verbose."}}'`);
40935
+ lines.push("sleep 3");
40887
40936
  lines.push("```");
40888
40937
  lines.push("");
40889
40938
  lines.push("### Step 3: Check generating OR completed");
@@ -41007,6 +41056,9 @@ var init_dev_server = __esm({
41007
41056
  lines.push("8. Keep exports compatible with the existing `scripts.js` router (`module.exports = function ...`).");
41008
41057
  lines.push("9. Do not rewrite unrelated provider config. Only touch the scripts needed for this task unless a tiny supporting change is required.");
41009
41058
  lines.push("10. When the verification API returns `instanceId`, keep using that exact instance for follow-up `send`, `resolve`, `raw`, and `stop` calls. Do not assume type-only routing is safe if multiple sessions exist.");
41059
+ lines.push("11. Do NOT repeatedly dump the same target files. Read the target scripts once, reproduce the bug, then move directly to patching.");
41060
+ lines.push("12. If the user instructions include concrete screen text, raw PTY snippets, or a specific repro, treat that as the primary acceptance criteria.");
41061
+ lines.push("13. After the first successful live repro, stop broad diagnosis. Edit the scripts, reload, and verify. Do not burn tokens on repeated re-inspection without code changes.");
41010
41062
  lines.push("");
41011
41063
  lines.push("## Task");
41012
41064
  lines.push(`Edit files in \`${providerDir}\` to implement: **${functions.join(", ")}**`);
@@ -41048,6 +41100,9 @@ var init_dev_server = __esm({
41048
41100
  lines.push("");
41049
41101
  lines.push("Use `resolve` when the parsed modal buttons are correct. Use `raw` when the CLI expects a literal keystroke like `1`, `y`, or Enter. Repeat until idle.");
41050
41102
  lines.push("");
41103
+ lines.push("### Patch Discipline");
41104
+ lines.push("Once the repro is confirmed, immediately edit the target files. Avoid loops where you keep re-reading long files or re-running the same debug commands without changing code.");
41105
+ lines.push("");
41051
41106
  lines.push("### 5. Verify the side effects outside the CLI");
41052
41107
  lines.push("```bash");
41053
41108
  lines.push("test -f tmp/adhdev_provider_fix_test.py");
@@ -43209,7 +43264,7 @@ var init_adhdev_daemon = __esm({
43209
43264
  fs12 = __toESM(require("fs"));
43210
43265
  path14 = __toESM(require("path"));
43211
43266
  import_chalk2 = __toESM(require("chalk"));
43212
- pkgVersion = "0.6.75";
43267
+ pkgVersion = "0.6.77";
43213
43268
  if (pkgVersion === "unknown") {
43214
43269
  try {
43215
43270
  const possiblePaths = [
@@ -44698,13 +44753,21 @@ function hideCommand2(command) {
44698
44753
  command.hideHelp?.();
44699
44754
  return command;
44700
44755
  }
44756
+ async function createConfiguredProviderLoader() {
44757
+ const { ProviderLoader: ProviderLoader2, loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_src(), src_exports));
44758
+ const config2 = loadConfig2();
44759
+ const loader = new ProviderLoader2({
44760
+ userDir: config2.providerDir,
44761
+ disableUpstream: config2.disableUpstream
44762
+ });
44763
+ loader.loadAll();
44764
+ return loader;
44765
+ }
44701
44766
  function registerProviderCommands(program2) {
44702
44767
  const provider = hideCommand2(program2.command("provider").description("\u{1F50C} Provider management \u2014 list, test, reload providers"));
44703
44768
  provider.command("list").description("List all loaded providers").option("-j, --json", "Output raw JSON").action(async (options) => {
44704
44769
  try {
44705
- const { ProviderLoader: ProviderLoader2 } = await Promise.resolve().then(() => (init_src(), src_exports));
44706
- const loader = new ProviderLoader2();
44707
- loader.loadAll();
44770
+ const loader = await createConfiguredProviderLoader();
44708
44771
  const providers = loader.getAll();
44709
44772
  if (options.json) {
44710
44773
  console.log(JSON.stringify(providers.map((p) => ({
@@ -44918,8 +44981,7 @@ function registerProviderCommands(program2) {
44918
44981
  }).catch(async () => {
44919
44982
  const pathMod = await import("path");
44920
44983
  const fsMod = await import("fs");
44921
- const { ProviderLoader: ProviderLoader2 } = await Promise.resolve().then(() => (init_src(), src_exports));
44922
- const loader = new ProviderLoader2();
44984
+ const loader = await createConfiguredProviderLoader();
44923
44985
  let targetDir = location === "builtin" ? loader.getUpstreamProviderDir(category, type) : loader.getUserProviderDir(category, type);
44924
44986
  if (fsMod.existsSync(targetDir)) return { error: `Provider already exists at ${targetDir}` };
44925
44987
  const isExt = category === "extension";
@@ -44972,9 +45034,7 @@ function registerProviderCommands(program2) {
44972
45034
  try {
44973
45035
  const http3 = await import("http");
44974
45036
  const inquirer2 = (await import("inquirer")).default;
44975
- const { ProviderLoader: ProviderLoader2 } = await Promise.resolve().then(() => (init_src(), src_exports));
44976
- const loader = new ProviderLoader2();
44977
- loader.loadAll();
45037
+ const loader = await createConfiguredProviderLoader();
44978
45038
  const allProviders = loader.getAll();
44979
45039
  const fsMod = await import("fs");
44980
45040
  const isUserProvider = (p) => {
@@ -45143,10 +45203,7 @@ function registerProviderCommands(program2) {
45143
45203
  throw new Error(`Unexpected response: ${JSON.stringify(startResult)}`);
45144
45204
  }
45145
45205
  const pathMod = await import("path");
45146
- const { ProviderLoader: ProviderLoader3 } = await Promise.resolve().then(() => (init_src(), src_exports));
45147
- const loader2 = new ProviderLoader3();
45148
- loader2.loadAll();
45149
- const providerMeta = loader2.getMeta(type);
45206
+ const providerMeta = loader.getMeta(type);
45150
45207
  if (!providerMeta) throw new Error(`Unknown provider: ${type}`);
45151
45208
  const fsMock = await import("fs");
45152
45209
  const logFile2 = pathMod.join(targetDir, `auto-impl.log`);
@@ -45327,9 +45384,7 @@ function registerProviderCommands(program2) {
45327
45384
  }).catch(async () => {
45328
45385
  const pathMod = await import("path");
45329
45386
  const fsMod = await import("fs");
45330
- const { ProviderLoader: ProviderLoader2 } = await Promise.resolve().then(() => (init_src(), src_exports));
45331
- const loader = new ProviderLoader2();
45332
- loader.loadAll();
45387
+ const loader = await createConfiguredProviderLoader();
45333
45388
  const providerMeta = loader.getMeta(type);
45334
45389
  if (!providerMeta) {
45335
45390
  return { error: `Provider '${type}' not found` };