@adhdev/daemon-core 0.6.75 → 0.6.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -285,7 +285,7 @@ interface ManagedCliEntry {
285
285
  cliType: string;
286
286
  cliName: string;
287
287
  status: string;
288
- mode: 'terminal' | 'chat';
288
+ mode: 'terminal';
289
289
  workspace: string;
290
290
  activeChat: ActiveChatData | null;
291
291
  }
@@ -1075,6 +1075,7 @@ declare class ProviderLoader {
1075
1075
  icon: string;
1076
1076
  command: string;
1077
1077
  category: string;
1078
+ versionCommand?: string;
1078
1079
  }[];
1079
1080
  /**
1080
1081
  * List providers by category
@@ -1245,6 +1246,7 @@ interface CLIInfo {
1245
1246
  displayName: string;
1246
1247
  icon: string;
1247
1248
  command: string;
1249
+ versionCommand?: string;
1248
1250
  installed: boolean;
1249
1251
  version?: string;
1250
1252
  path?: string;
@@ -2710,6 +2712,9 @@ declare class ProviderCliAdapter implements CliAdapter {
2710
2712
  private lastApprovalResolvedAt;
2711
2713
  private approvalTransitionBuffer;
2712
2714
  private approvalExitTimeout;
2715
+ private pendingScriptStatus;
2716
+ private pendingScriptStatusSince;
2717
+ private pendingScriptStatusTimer;
2713
2718
  private settleTimer;
2714
2719
  private settledBuffer;
2715
2720
  private submitPendingUntil;
@@ -2741,6 +2746,7 @@ declare class ProviderCliAdapter implements CliAdapter {
2741
2746
  private readonly sendDelayMs;
2742
2747
  private readonly sendKey;
2743
2748
  private readonly submitStrategy;
2749
+ private static readonly SCRIPT_STATUS_DEBOUNCE_MS;
2744
2750
  constructor(provider: CliProviderModule, workingDir: string, extraArgs?: string[]);
2745
2751
  /** Inject CLI scripts after construction (e.g. when resolved by ProviderLoader) */
2746
2752
  setCliScripts(scripts: CliScripts): void;
package/dist/index.js CHANGED
@@ -692,6 +692,12 @@ __export(provider_cli_adapter_exports, {
692
692
  function stripAnsi(str) {
693
693
  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, " ");
694
694
  }
695
+ function stripTerminalNoise(str) {
696
+ 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, " ");
697
+ }
698
+ function sanitizeTerminalText(str) {
699
+ return stripTerminalNoise(stripAnsi(str));
700
+ }
695
701
  function findBinary(name) {
696
702
  const isWin = os11.platform() === "win32";
697
703
  try {
@@ -924,6 +930,9 @@ var init_provider_cli_adapter = __esm({
924
930
  // Approval state machine
925
931
  approvalTransitionBuffer = "";
926
932
  approvalExitTimeout = null;
933
+ pendingScriptStatus = null;
934
+ pendingScriptStatusSince = 0;
935
+ pendingScriptStatusTimer = null;
927
936
  // Output settle debounce — fires after PTY output goes quiet
928
937
  settleTimer = null;
929
938
  settledBuffer = "";
@@ -989,6 +998,7 @@ var init_provider_cli_adapter = __esm({
989
998
  sendDelayMs;
990
999
  sendKey;
991
1000
  submitStrategy;
1001
+ static SCRIPT_STATUS_DEBOUNCE_MS = 1e3;
992
1002
  /** Inject CLI scripts after construction (e.g. when resolved by ProviderLoader) */
993
1003
  setCliScripts(scripts) {
994
1004
  this.cliScripts = scripts;
@@ -1115,7 +1125,7 @@ var init_provider_cli_adapter = __esm({
1115
1125
  handleOutput(rawData) {
1116
1126
  this.terminalScreen.write(rawData);
1117
1127
  this.terminalHistory = mergeTerminalHistory(this.terminalHistory, this.terminalScreen.getText());
1118
- const cleanData = stripAnsi(rawData);
1128
+ const cleanData = sanitizeTerminalText(rawData);
1119
1129
  if (this.isWaitingForResponse && cleanData) {
1120
1130
  this.responseBuffer = (this.responseBuffer + cleanData).slice(-8e3);
1121
1131
  }
@@ -1212,24 +1222,39 @@ var init_provider_cli_adapter = __esm({
1212
1222
  const scriptStatus = rawScriptStatus;
1213
1223
  if (!scriptStatus) return;
1214
1224
  const prevStatus = this.currentStatus;
1215
- if (scriptStatus === "waiting_approval") {
1216
- const modalMessage = modal?.message || "";
1217
- const screenText = this.terminalScreen.getText() || this.accumulatedBuffer;
1218
- const autoAcceptPatterns = [
1219
- /be able to read, edit, and execute/i,
1220
- /Security guide/i,
1221
- /Enter to confirm/i,
1222
- /Quick safety check/i,
1223
- /Do you trust the files/i,
1224
- /Is this a project/i
1225
- ];
1226
- if (autoAcceptPatterns.some((p) => p.test(modalMessage) || p.test(screenText))) {
1227
- LOG.info("CLI", `[${this.cliType}] Auto-accepting startup dialog: ${modalMessage.slice(0, 80)}`);
1228
- setTimeout(() => this.ptyProcess?.write("\r"), 200);
1229
- this.lastApprovalResolvedAt = Date.now();
1230
- this.activeModal = null;
1225
+ const clearPendingScriptStatus = () => {
1226
+ this.pendingScriptStatus = null;
1227
+ this.pendingScriptStatusSince = 0;
1228
+ if (this.pendingScriptStatusTimer) {
1229
+ clearTimeout(this.pendingScriptStatusTimer);
1230
+ this.pendingScriptStatusTimer = null;
1231
+ }
1232
+ };
1233
+ const armPendingScriptStatus = (delayMs) => {
1234
+ if (this.pendingScriptStatusTimer) clearTimeout(this.pendingScriptStatusTimer);
1235
+ this.pendingScriptStatusTimer = setTimeout(() => {
1236
+ this.pendingScriptStatusTimer = null;
1237
+ this.settledBuffer = this.recentOutputBuffer;
1238
+ this.evaluateSettled();
1239
+ }, delayMs);
1240
+ };
1241
+ const shouldDebouncePromotion = (status) => prevStatus === "idle" && !this.isWaitingForResponse && !this.currentTurnScope && (status === "generating" || status === "waiting_approval");
1242
+ if (shouldDebouncePromotion(scriptStatus)) {
1243
+ if (this.pendingScriptStatus !== scriptStatus) {
1244
+ this.pendingScriptStatus = scriptStatus;
1245
+ this.pendingScriptStatusSince = now;
1246
+ armPendingScriptStatus(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS);
1247
+ return;
1248
+ }
1249
+ const elapsed = now - this.pendingScriptStatusSince;
1250
+ if (elapsed < _ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS) {
1251
+ armPendingScriptStatus(_ProviderCliAdapter.SCRIPT_STATUS_DEBOUNCE_MS - elapsed);
1231
1252
  return;
1232
1253
  }
1254
+ } else {
1255
+ clearPendingScriptStatus();
1256
+ }
1257
+ if (scriptStatus === "waiting_approval") {
1233
1258
  const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
1234
1259
  if (!inCooldown) {
1235
1260
  this.isWaitingForResponse = true;
@@ -1242,6 +1267,12 @@ var init_provider_cli_adapter = __esm({
1242
1267
  }
1243
1268
  }
1244
1269
  if (scriptStatus === "generating") {
1270
+ const screenText = this.terminalScreen.getText() || this.accumulatedBuffer;
1271
+ const noActiveTurn = !this.currentTurnScope;
1272
+ 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));
1273
+ if (prevStatus === "idle" && !this.isWaitingForResponse && noActiveTurn && !modal && looksIdleChrome) {
1274
+ return;
1275
+ }
1245
1276
  if (prevStatus === "waiting_approval") {
1246
1277
  if (this.approvalExitTimeout) {
1247
1278
  clearTimeout(this.approvalExitTimeout);
@@ -1699,7 +1730,7 @@ ${data.message || ""}`.trim();
1699
1730
  committedMessages: this.committedMessages.slice(-20),
1700
1731
  structuredMessages: this.structuredMessages.slice(-20),
1701
1732
  messageCount: this.committedMessages.length,
1702
- screenText: this.terminalScreen.getText().slice(-4e3),
1733
+ screenText: sanitizeTerminalText(this.terminalScreen.getText()).slice(-4e3),
1703
1734
  terminalHistory: this.terminalHistory.slice(-8e3),
1704
1735
  currentTurnScope: this.currentTurnScope,
1705
1736
  startupBuffer: this.startupBuffer.slice(-4e3),
@@ -1708,6 +1739,7 @@ ${data.message || ""}`.trim();
1708
1739
  accumulatedBufferLength: this.accumulatedBuffer.length,
1709
1740
  accumulatedRawBufferLength: this.accumulatedRawBuffer.length,
1710
1741
  rawBufferPreview: this.accumulatedRawBuffer.slice(-1e3),
1742
+ sanitizedRawPreview: sanitizeTerminalText(this.accumulatedRawBuffer).slice(-1e3),
1711
1743
  responseBuffer: this.responseBuffer.slice(-1e3),
1712
1744
  isWaitingForResponse: this.isWaitingForResponse,
1713
1745
  activeModal: this.activeModal,
@@ -1946,6 +1978,10 @@ async function detectIDEs() {
1946
1978
  // src/detection/cli-detector.ts
1947
1979
  var import_child_process2 = require("child_process");
1948
1980
  var os2 = __toESM(require("os"));
1981
+ function parseVersion(raw) {
1982
+ const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
1983
+ return match ? match[1] : raw.split("\n")[0].slice(0, 100);
1984
+ }
1949
1985
  function execAsync(cmd, timeoutMs = 5e3) {
1950
1986
  return new Promise((resolve8) => {
1951
1987
  const child = (0, import_child_process2.exec)(cmd, { encoding: "utf-8", timeout: timeoutMs }, (err, stdout) => {
@@ -1970,10 +2006,18 @@ async function detectCLIs(providerLoader) {
1970
2006
  const firstPath = pathResult.split("\n")[0];
1971
2007
  let version;
1972
2008
  try {
1973
- const versionResult = await execAsync(`${cli.command} --version 2>/dev/null`, 3e3);
1974
- if (versionResult) {
1975
- const match = versionResult.match(/(\d+\.\d+[\.\d]*)/);
1976
- version = match ? match[1] : versionResult.split("\n")[0].slice(0, 30);
2009
+ const versionCommands = [
2010
+ cli.versionCommand,
2011
+ `${cli.command} --version 2>/dev/null`,
2012
+ `${cli.command} -V 2>/dev/null`,
2013
+ `${cli.command} -v 2>/dev/null`
2014
+ ].filter((v) => !!v);
2015
+ for (const versionCommand of versionCommands) {
2016
+ const versionResult = await execAsync(versionCommand, 3e3);
2017
+ if (versionResult) {
2018
+ version = parseVersion(versionResult);
2019
+ break;
2020
+ }
1977
2021
  }
1978
2022
  } catch {
1979
2023
  }
@@ -4479,7 +4523,7 @@ function buildManagedClis(cliStates) {
4479
4523
  cliType: s.type,
4480
4524
  cliName: s.name,
4481
4525
  status: s.status,
4482
- mode: s.mode,
4526
+ mode: "terminal",
4483
4527
  workspace: s.workspace || "",
4484
4528
  activeChat: s.activeChat
4485
4529
  }));
@@ -6035,8 +6079,13 @@ var DaemonCommandHandler = class {
6035
6079
  getCliAdapter(type) {
6036
6080
  const target = type || this._currentIdeType;
6037
6081
  if (!target || !this._ctx.adapters) return null;
6082
+ let normalizedTarget = target;
6083
+ const colonIdx = normalizedTarget.lastIndexOf(":");
6084
+ if (colonIdx >= 0) normalizedTarget = normalizedTarget.substring(colonIdx + 1);
6085
+ const direct = this._ctx.adapters.get(normalizedTarget);
6086
+ if (direct) return direct;
6038
6087
  for (const [key, adapter] of this._ctx.adapters.entries()) {
6039
- if (adapter.cliType === target || key.startsWith(target)) {
6088
+ if (adapter.cliType === target || adapter.cliType === normalizedTarget || key === normalizedTarget || key.startsWith(target) || key.startsWith(normalizedTarget)) {
6040
6089
  return adapter;
6041
6090
  }
6042
6091
  }
@@ -6549,12 +6598,15 @@ var ProviderLoader = class _ProviderLoader {
6549
6598
  const result = [];
6550
6599
  for (const p of this.providers.values()) {
6551
6600
  if ((p.category === "cli" || p.category === "acp") && p.spawn?.command) {
6601
+ const verCmdConfig = p.versionCommand;
6602
+ const versionCommand = typeof verCmdConfig === "object" && verCmdConfig !== null ? verCmdConfig[process.platform] : verCmdConfig;
6552
6603
  result.push({
6553
6604
  id: p.type,
6554
6605
  displayName: p.displayName || p.name,
6555
6606
  icon: p.icon || "\u{1F527}",
6556
6607
  command: p.spawn.command,
6557
- category: p.category
6608
+ category: p.category,
6609
+ ...typeof versionCommand === "string" && versionCommand.trim() ? { versionCommand: versionCommand.trim() } : {}
6558
6610
  });
6559
6611
  }
6560
6612
  }
@@ -8472,19 +8524,6 @@ var CliProviderInstance = class {
8472
8524
  getState() {
8473
8525
  const adapterStatus = this.adapter.getStatus();
8474
8526
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
8475
- const recentMessages = adapterStatus.messages.slice(-50).map((m) => {
8476
- const content = typeof m.content === "string" && m.content.length > 8e3 ? m.content.slice(0, 8e3) + "\n... (truncated)" : m.content;
8477
- return { ...m, content };
8478
- });
8479
- if (recentMessages.length > 0) {
8480
- const dirName2 = this.workingDir.split("/").filter(Boolean).pop() || "session";
8481
- this.historyWriter.appendNewMessages(
8482
- this.type,
8483
- recentMessages,
8484
- `${this.provider.name} \xB7 ${dirName2}`,
8485
- this.instanceId
8486
- );
8487
- }
8488
8527
  if (adapterStatus.terminalHistory?.trim()) {
8489
8528
  this.historyWriter.appendTerminalHistory(
8490
8529
  this.type,
@@ -8498,12 +8537,12 @@ var CliProviderInstance = class {
8498
8537
  name: this.provider.name,
8499
8538
  category: "cli",
8500
8539
  status: adapterStatus.status,
8501
- mode: this.settings.mode || "terminal",
8540
+ mode: "terminal",
8502
8541
  activeChat: {
8503
8542
  id: `${this.type}_${this.workingDir}`,
8504
8543
  title: `${this.provider.name} \xB7 ${dirName}`,
8505
8544
  status: adapterStatus.status,
8506
- messages: recentMessages,
8545
+ messages: [],
8507
8546
  activeModal: adapterStatus.activeModal,
8508
8547
  terminalHistory: adapterStatus.terminalHistory,
8509
8548
  inputContent: ""
@@ -8517,11 +8556,15 @@ var CliProviderInstance = class {
8517
8556
  }
8518
8557
  onEvent(event, data) {
8519
8558
  if (event === "send_message" && data?.text) {
8520
- this.adapter.sendMessage(data.text);
8559
+ void this.adapter.sendMessage(data.text).catch((e) => {
8560
+ LOG.warn("CLI", `[${this.type}] send_message failed: ${e?.message || e}`);
8561
+ });
8521
8562
  } else if (event === "server_connected" && data?.serverConn) {
8522
8563
  this.adapter.setServerConn(data.serverConn);
8523
8564
  } else if (event === "resolve_action" && data) {
8524
- this.adapter.resolveAction(data);
8565
+ void this.adapter.resolveAction(data).catch((e) => {
8566
+ LOG.warn("CLI", `[${this.type}] resolve_action failed: ${e?.message || e}`);
8567
+ });
8525
8568
  }
8526
8569
  }
8527
8570
  dispose() {
@@ -10635,18 +10678,18 @@ function findBinary2(name) {
10635
10678
  const result = runCommand(cmd, 5e3);
10636
10679
  return result ? result.split("\n")[0] : null;
10637
10680
  }
10638
- function parseVersion(raw) {
10681
+ function parseVersion2(raw) {
10639
10682
  const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
10640
10683
  return match ? match[1] : raw.split("\n")[0].substring(0, 100);
10641
10684
  }
10642
10685
  function getVersion(binary, versionCommand) {
10643
10686
  if (versionCommand) {
10644
10687
  const raw = runCommand(versionCommand);
10645
- return raw ? parseVersion(raw) : null;
10688
+ return raw ? parseVersion2(raw) : null;
10646
10689
  }
10647
10690
  for (const flag of ["--version", "-V", "-v"]) {
10648
10691
  const raw = runCommand(`"${binary}" ${flag}`);
10649
- if (raw && raw.length < 500) return parseVersion(raw);
10692
+ if (raw && raw.length < 500) return parseVersion2(raw);
10650
10693
  }
10651
10694
  return null;
10652
10695
  }
@@ -12907,16 +12950,16 @@ var DevServer = class _DevServer {
12907
12950
  resolveAutoImplWritableProviderDir(category, type, requestedDir) {
12908
12951
  const canonicalUserDir = path12.resolve(this.providerLoader.getUserProviderDir(category, type));
12909
12952
  const desiredDir = requestedDir ? path12.resolve(requestedDir) : canonicalUserDir;
12910
- if (desiredDir !== canonicalUserDir) {
12911
- return null;
12953
+ const upstreamRoot = path12.resolve(this.providerLoader.getUpstreamDir());
12954
+ if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path12.sep}`)) {
12955
+ return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
12912
12956
  }
12913
- const userRoot = path12.resolve(this.providerLoader.getUserDir());
12914
- if (desiredDir !== userRoot && !desiredDir.startsWith(`${userRoot}${path12.sep}`)) {
12915
- return null;
12957
+ if (path12.basename(desiredDir) !== type) {
12958
+ return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
12916
12959
  }
12917
12960
  const sourceDir = this.findProviderDir(type);
12918
12961
  if (!sourceDir) {
12919
- return null;
12962
+ return { dir: null, reason: `Provider source directory not found for '${type}'` };
12920
12963
  }
12921
12964
  if (!fs9.existsSync(desiredDir)) {
12922
12965
  fs9.mkdirSync(path12.dirname(desiredDir), { recursive: true });
@@ -12925,7 +12968,7 @@ var DevServer = class _DevServer {
12925
12968
  }
12926
12969
  const providerJson = path12.join(desiredDir, "provider.json");
12927
12970
  if (!fs9.existsSync(providerJson)) {
12928
- return null;
12971
+ return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
12929
12972
  }
12930
12973
  try {
12931
12974
  const providerData = JSON.parse(fs9.readFileSync(providerJson, "utf-8"));
@@ -12933,10 +12976,13 @@ var DevServer = class _DevServer {
12933
12976
  providerData.disableUpstream = true;
12934
12977
  fs9.writeFileSync(providerJson, JSON.stringify(providerData, null, 2));
12935
12978
  }
12936
- } catch {
12937
- return null;
12979
+ } catch (error) {
12980
+ return {
12981
+ dir: null,
12982
+ reason: `Failed to update provider.json in writable provider directory: ${error.message}`
12983
+ };
12938
12984
  }
12939
- return desiredDir;
12985
+ return { dir: desiredDir };
12940
12986
  }
12941
12987
  loadAutoImplReferenceScripts(referenceType) {
12942
12988
  if (!referenceType) return {};
@@ -12971,13 +13017,14 @@ var DevServer = class _DevServer {
12971
13017
  this.json(res, 404, { error: `Provider not found: ${type}` });
12972
13018
  return;
12973
13019
  }
12974
- const providerDir = this.resolveAutoImplWritableProviderDir(provider.category, type, requestedProviderDir);
12975
- if (!providerDir) {
13020
+ const writableProvider = this.resolveAutoImplWritableProviderDir(provider.category, type, requestedProviderDir);
13021
+ if (!writableProvider.dir) {
12976
13022
  this.json(res, 409, {
12977
- error: `Auto-implement only writes to the canonical user provider directory for '${type}'.`
13023
+ error: writableProvider.reason || `Auto-implement only writes to the canonical user provider directory for '${type}'.`
12978
13024
  });
12979
13025
  return;
12980
13026
  }
13027
+ const providerDir = writableProvider.dir;
12981
13028
  try {
12982
13029
  const resolvedReference = this.resolveAutoImplReference(provider.category, reference, type);
12983
13030
  this.sendAutoImplSSE({
@@ -13670,6 +13717,9 @@ var DevServer = class _DevServer {
13670
13717
  lines.push("8. Keep exports compatible with the existing `scripts.js` router (`module.exports = function ...`).");
13671
13718
  lines.push("9. Do not rewrite unrelated provider config. Only touch the scripts needed for this task unless a tiny supporting change is required.");
13672
13719
  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.");
13720
+ lines.push("11. Do NOT repeatedly dump the same target files. Read the target scripts once, reproduce the bug, then move directly to patching.");
13721
+ 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.");
13722
+ 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.");
13673
13723
  lines.push("");
13674
13724
  lines.push("## Task");
13675
13725
  lines.push(`Edit files in \`${providerDir}\` to implement: **${functions.join(", ")}**`);
@@ -13711,6 +13761,9 @@ var DevServer = class _DevServer {
13711
13761
  lines.push("");
13712
13762
  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.");
13713
13763
  lines.push("");
13764
+ lines.push("### Patch Discipline");
13765
+ 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.");
13766
+ lines.push("");
13714
13767
  lines.push("### 5. Verify the side effects outside the CLI");
13715
13768
  lines.push("```bash");
13716
13769
  lines.push("test -f tmp/adhdev_provider_fix_test.py");