adhdev 0.8.34 → 0.8.35

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
@@ -9724,8 +9724,6 @@ ${data.message || ""}`.trim();
9724
9724
  if (blockingModal || this.currentStatus === "waiting_approval") {
9725
9725
  throw new Error(`${this.cliName} is awaiting confirmation before it can accept a prompt`);
9726
9726
  }
9727
- this.committedMessages.push({ role: "user", content: text, timestamp: Date.now() });
9728
- this.syncMessageViews();
9729
9727
  this.isWaitingForResponse = true;
9730
9728
  this.responseBuffer = "";
9731
9729
  this.finishRetryCount = 0;
@@ -9757,6 +9755,13 @@ ${data.message || ""}`.trim();
9757
9755
  const submitDelayMs = this.sendDelayMs + Math.min(2e3, Math.max(0, estimatedLines - 1) * 350);
9758
9756
  const maxEchoWaitMs = submitDelayMs + Math.max(1500, Math.min(5e3, estimatedLines * 500));
9759
9757
  const retryDelayMs = Math.max(350, Math.min(1500, Math.max(this.sendDelayMs, submitDelayMs)));
9758
+ let didCommitUserTurn = false;
9759
+ const commitUserTurn = () => {
9760
+ if (didCommitUserTurn) return;
9761
+ didCommitUserTurn = true;
9762
+ this.committedMessages.push({ role: "user", content: text, timestamp: Date.now() });
9763
+ this.syncMessageViews();
9764
+ };
9760
9765
  if (this.settleTimer) {
9761
9766
  clearTimeout(this.settleTimer);
9762
9767
  this.settleTimer = null;
@@ -9769,109 +9774,128 @@ ${data.message || ""}`.trim();
9769
9774
  if (this.isWaitingForResponse) this.finishResponse();
9770
9775
  }, this.timeouts.maxResponse);
9771
9776
  };
9772
- const submit = () => {
9773
- if (!this.ptyProcess) return;
9774
- this.submitPendingUntil = 0;
9775
- this.recordTrace("submit_write", {
9776
- mode: "submit_key",
9777
- sendKey: this.sendKey,
9778
- screenText: summarizeCliTraceText(this.terminalScreen.getText(), 500)
9779
- });
9780
- this.ptyProcess.write(this.sendKey);
9781
- const retrySubmitIfStuck = (attempt) => {
9782
- this.submitRetryTimer = null;
9783
- if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
9784
- if (this.currentStatus === "waiting_approval") return;
9785
- if ((this.responseBuffer || "").trim()) return;
9777
+ await new Promise((resolve17) => {
9778
+ let resolved = false;
9779
+ const resolveOnce = () => {
9780
+ if (resolved) return;
9781
+ resolved = true;
9782
+ resolve17();
9783
+ };
9784
+ const submit = () => {
9785
+ if (!this.ptyProcess) {
9786
+ resolveOnce();
9787
+ return;
9788
+ }
9789
+ commitUserTurn();
9790
+ this.submitPendingUntil = 0;
9786
9791
  const screenText = this.terminalScreen.getText();
9787
- if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
9788
- if (/Esc to interrupt|Do you want to proceed|This command requires approval|Allow Codex to|Approve and run now|Always approve this session|Running…|Running\.\.\./i.test(screenText)) return;
9789
- this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
9790
- LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
9791
9792
  this.recordTrace("submit_write", {
9792
- mode: "submit_retry",
9793
- attempt,
9793
+ mode: "submit_key",
9794
9794
  sendKey: this.sendKey,
9795
9795
  screenText: summarizeCliTraceText(screenText, 500)
9796
9796
  });
9797
9797
  this.ptyProcess.write(this.sendKey);
9798
- if (attempt >= 3) {
9799
- this.submitRetryUsed = true;
9800
- return;
9801
- }
9802
- this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(attempt + 1), retryDelayMs);
9798
+ const retrySubmitIfStuck = (attempt) => {
9799
+ this.submitRetryTimer = null;
9800
+ if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
9801
+ if (this.currentStatus === "waiting_approval") return;
9802
+ if ((this.responseBuffer || "").trim()) return;
9803
+ const screenText2 = this.terminalScreen.getText();
9804
+ if (!promptLikelyVisible(screenText2, normalizedPromptSnippet)) return;
9805
+ if (/Esc to interrupt|Do you want to proceed|This command requires approval|Allow Codex to|Approve and run now|Always approve this session|Running…|Running\.\.\./i.test(screenText2)) return;
9806
+ this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
9807
+ LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt ${attempt})`);
9808
+ this.recordTrace("submit_write", {
9809
+ mode: "submit_retry",
9810
+ attempt,
9811
+ sendKey: this.sendKey,
9812
+ screenText: summarizeCliTraceText(screenText2, 500)
9813
+ });
9814
+ this.ptyProcess.write(this.sendKey);
9815
+ if (attempt >= 3) {
9816
+ this.submitRetryUsed = true;
9817
+ return;
9818
+ }
9819
+ this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(attempt + 1), retryDelayMs);
9820
+ };
9821
+ this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(1), retryDelayMs);
9822
+ startResponseTimeout();
9823
+ resolveOnce();
9803
9824
  };
9804
- this.submitRetryTimer = setTimeout(() => retrySubmitIfStuck(1), retryDelayMs);
9805
- startResponseTimeout();
9806
- };
9807
- if (this.submitStrategy === "immediate") {
9808
- this.submitPendingUntil = 0;
9825
+ if (this.submitStrategy === "immediate") {
9826
+ commitUserTurn();
9827
+ this.submitPendingUntil = 0;
9828
+ this.recordTrace("submit_write", {
9829
+ mode: "immediate",
9830
+ text: summarizeCliTraceText(text, 500),
9831
+ sendKey: this.sendKey,
9832
+ screenText: summarizeCliTraceText(this.terminalScreen.getText(), 500)
9833
+ });
9834
+ this.ptyProcess.write(text + this.sendKey);
9835
+ this.submitRetryTimer = setTimeout(() => {
9836
+ this.submitRetryTimer = null;
9837
+ if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
9838
+ if (this.currentStatus === "waiting_approval") return;
9839
+ if ((this.responseBuffer || "").trim()) return;
9840
+ const screenText = this.terminalScreen.getText();
9841
+ if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
9842
+ LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
9843
+ this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
9844
+ this.recordTrace("submit_write", {
9845
+ mode: "immediate_retry",
9846
+ attempt: 1,
9847
+ sendKey: this.sendKey,
9848
+ screenText: summarizeCliTraceText(screenText, 500)
9849
+ });
9850
+ this.ptyProcess.write(this.sendKey);
9851
+ this.submitRetryUsed = true;
9852
+ }, retryDelayMs);
9853
+ startResponseTimeout();
9854
+ resolveOnce();
9855
+ return;
9856
+ }
9857
+ if (submitDelayMs > 0) {
9858
+ this.submitPendingUntil = Date.now() + submitDelayMs;
9859
+ }
9860
+ this.ptyProcess.write(text);
9809
9861
  this.recordTrace("submit_write", {
9810
- mode: "immediate",
9862
+ mode: "type_then_submit",
9811
9863
  text: summarizeCliTraceText(text, 500),
9812
9864
  sendKey: this.sendKey,
9813
9865
  screenText: summarizeCliTraceText(this.terminalScreen.getText(), 500)
9814
9866
  });
9815
- this.ptyProcess.write(text + this.sendKey);
9816
- this.submitRetryTimer = setTimeout(() => {
9817
- this.submitRetryTimer = null;
9818
- if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
9819
- if (this.currentStatus === "waiting_approval") return;
9820
- if ((this.responseBuffer || "").trim()) return;
9867
+ const submitStartedAt = Date.now();
9868
+ let lastNormalizedScreen = "";
9869
+ let lastScreenChangeAt = submitStartedAt;
9870
+ const waitForEchoAndSubmit = () => {
9871
+ if (!this.ptyProcess) {
9872
+ resolveOnce();
9873
+ return;
9874
+ }
9875
+ const now = Date.now();
9876
+ const elapsed = now - submitStartedAt;
9821
9877
  const screenText = this.terminalScreen.getText();
9822
- if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
9823
- LOG.info("CLI", `[${this.cliType}] Retrying submit key for stuck prompt (attempt 1)`);
9824
- this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
9825
- this.recordTrace("submit_write", {
9826
- mode: "immediate_retry",
9827
- attempt: 1,
9828
- sendKey: this.sendKey,
9829
- screenText: summarizeCliTraceText(screenText, 500)
9830
- });
9831
- this.ptyProcess.write(this.sendKey);
9832
- this.submitRetryUsed = true;
9833
- }, retryDelayMs);
9834
- startResponseTimeout();
9835
- return;
9836
- }
9837
- if (submitDelayMs > 0) {
9838
- this.submitPendingUntil = Date.now() + submitDelayMs;
9839
- }
9840
- this.ptyProcess.write(text);
9841
- this.recordTrace("submit_write", {
9842
- mode: "type_then_submit",
9843
- text: summarizeCliTraceText(text, 500),
9844
- sendKey: this.sendKey,
9845
- screenText: summarizeCliTraceText(this.terminalScreen.getText(), 500)
9846
- });
9847
- const submitStartedAt = Date.now();
9848
- let lastNormalizedScreen = "";
9849
- let lastScreenChangeAt = submitStartedAt;
9850
- const waitForEchoAndSubmit = () => {
9851
- if (!this.ptyProcess) return;
9852
- const now = Date.now();
9853
- const elapsed = now - submitStartedAt;
9854
- const screenText = this.terminalScreen.getText();
9855
- const normalizedScreen = normalizePromptText(screenText);
9856
- if (normalizedScreen !== lastNormalizedScreen) {
9857
- lastNormalizedScreen = normalizedScreen;
9858
- lastScreenChangeAt = now;
9859
- }
9860
- const echoVisible = !normalizedPromptSnippet || promptLikelyVisible(screenText, normalizedPromptSnippet);
9861
- if (echoVisible) {
9862
- const screenSettled = now - lastScreenChangeAt >= 500;
9863
- if (elapsed >= submitDelayMs && screenSettled) {
9878
+ const normalizedScreen = normalizePromptText(screenText);
9879
+ if (normalizedScreen !== lastNormalizedScreen) {
9880
+ lastNormalizedScreen = normalizedScreen;
9881
+ lastScreenChangeAt = now;
9882
+ }
9883
+ const echoVisible = !normalizedPromptSnippet || promptLikelyVisible(screenText, normalizedPromptSnippet);
9884
+ if (echoVisible) {
9885
+ const screenSettled = now - lastScreenChangeAt >= 500;
9886
+ if (elapsed >= submitDelayMs && screenSettled) {
9887
+ submit();
9888
+ return;
9889
+ }
9890
+ }
9891
+ if (elapsed >= maxEchoWaitMs) {
9864
9892
  submit();
9865
9893
  return;
9866
9894
  }
9867
- }
9868
- if (elapsed >= maxEchoWaitMs) {
9869
- submit();
9870
- return;
9871
- }
9872
- setTimeout(waitForEchoAndSubmit, 50);
9873
- };
9874
- waitForEchoAndSubmit();
9895
+ setTimeout(waitForEchoAndSubmit, 50);
9896
+ };
9897
+ waitForEchoAndSubmit();
9898
+ });
9875
9899
  }
9876
9900
  getPartialResponse() {
9877
9901
  if (!this.isWaitingForResponse) return "";
@@ -33216,6 +33240,7 @@ var init_reporter = __esm({
33216
33240
  lastStatusSentAt = 0;
33217
33241
  statusPendingThrottle = false;
33218
33242
  lastP2PStatusHash = "";
33243
+ lastServerStatusHash = "";
33219
33244
  lastStatusSummary = "";
33220
33245
  statusTimer = null;
33221
33246
  p2pTimer = null;
@@ -33226,11 +33251,11 @@ var init_reporter = __esm({
33226
33251
  // ─── Lifecycle ───────────────────────────────────
33227
33252
  startReporting() {
33228
33253
  setTimeout(() => {
33229
- this.sendUnifiedStatusReport().catch((e) => LOG.warn("Status", `Initial report failed: ${e?.message}`));
33254
+ this.sendUnifiedStatusReport({ forceServer: true, reason: "initial" }).catch((e) => LOG.warn("Status", `Initial report failed: ${e?.message}`));
33230
33255
  }, 2e3);
33231
33256
  const scheduleServerReport = () => {
33232
33257
  this.statusTimer = setTimeout(() => {
33233
- this.sendUnifiedStatusReport().catch((e) => LOG.warn("Status", `Periodic report failed: ${e?.message}`));
33258
+ this.sendUnifiedStatusReport({ forceServer: true, reason: "periodic" }).catch((e) => LOG.warn("Status", `Periodic report failed: ${e?.message}`));
33234
33259
  scheduleServerReport();
33235
33260
  }, 3e4);
33236
33261
  };
@@ -33377,24 +33402,24 @@ var init_reporter = __esm({
33377
33402
  cdpConnected: session.cdpConnected,
33378
33403
  currentModel: session.currentModel,
33379
33404
  currentPlan: session.currentPlan,
33380
- currentAutoApprove: session.currentAutoApprove,
33381
- lastUpdated: session.lastUpdated,
33382
- unread: session.unread,
33383
- lastSeenAt: session.lastSeenAt,
33384
- inboxBucket: session.inboxBucket,
33385
- surfaceHidden: session.surfaceHidden,
33386
- controlValues: session.controlValues,
33387
- providerControls: session.providerControls,
33388
- acpConfigOptions: session.acpConfigOptions,
33389
- acpModes: session.acpModes
33405
+ currentAutoApprove: session.currentAutoApprove
33390
33406
  })),
33391
33407
  p2p: payload.p2p,
33392
33408
  timestamp: now,
33393
33409
  detectedIdes: payload.detectedIdes,
33394
33410
  availableProviders: payload.availableProviders
33395
33411
  };
33412
+ const wsHash = this.simpleHash(JSON.stringify({
33413
+ ...wsPayload,
33414
+ timestamp: void 0
33415
+ }));
33416
+ if (!opts?.forceServer && wsHash === this.lastServerStatusHash) {
33417
+ LOG.debug("Server", `skip duplicate status_report${opts?.reason ? ` (${opts.reason})` : ""}`);
33418
+ return;
33419
+ }
33420
+ this.lastServerStatusHash = wsHash;
33396
33421
  serverConn.sendMessage("status_report", wsPayload);
33397
- LOG.debug("Server", `sent status_report (${JSON.stringify(wsPayload).length} bytes)`);
33422
+ LOG.debug("Server", `sent status_report (${JSON.stringify(wsPayload).length} bytes)${opts?.reason ? ` [${opts.reason}]` : ""}`);
33398
33423
  }
33399
33424
  // ─── P2P ─────────────────────────────────────────
33400
33425
  sendP2PPayload(payload) {
@@ -40990,6 +41015,7 @@ var init_server_connection = __esm({
40990
41015
  reconnectTimer = null;
40991
41016
  pingTimer = null;
40992
41017
  pongTimeout = null;
41018
+ missedPongCount = 0;
40993
41019
  messageHandlers = /* @__PURE__ */ new Map();
40994
41020
  stateChangeCallbacks = [];
40995
41021
  /** Fallback handler for message types without specific on() registration */
@@ -41088,6 +41114,7 @@ var init_server_connection = __esm({
41088
41114
  // --- Private ---
41089
41115
  onOpen() {
41090
41116
  LOG.info("Server", `[ServerConn] WebSocket open, sending auth...`);
41117
+ this.missedPongCount = 0;
41091
41118
  this.setState("authenticating");
41092
41119
  this.send({
41093
41120
  type: "auth",
@@ -41100,6 +41127,8 @@ var init_server_connection = __esm({
41100
41127
  }
41101
41128
  onMessage(text) {
41102
41129
  try {
41130
+ this.clearPongTimeout();
41131
+ this.missedPongCount = 0;
41103
41132
  const message = JSON.parse(text);
41104
41133
  if (message.type === "auth_ok") {
41105
41134
  this.reconnectAttempts = 0;
@@ -41200,6 +41229,7 @@ var init_server_connection = __esm({
41200
41229
  this.reconnectTimer = null;
41201
41230
  }
41202
41231
  this.stopHeartbeat();
41232
+ this.missedPongCount = 0;
41203
41233
  }
41204
41234
  // ─── WS Heartbeat (ping/pong) ─────────────────────
41205
41235
  startHeartbeat() {
@@ -41209,9 +41239,14 @@ var init_server_connection = __esm({
41209
41239
  try {
41210
41240
  this.ws.ping();
41211
41241
  this.pongTimeout = setTimeout(() => {
41212
- LOG.info("Server", "[ServerConn] \u26A0 Pong timeout (15s) \u2014 closing zombie WS");
41213
- if (this.ws) {
41242
+ this.pongTimeout = null;
41243
+ this.missedPongCount += 1;
41244
+ const misses = this.missedPongCount;
41245
+ if (misses >= 3 && this.ws) {
41246
+ LOG.warn("Server", `[ServerConn] Pong timeout (${misses}x) \u2014 closing stale WS`);
41214
41247
  this.ws.terminate();
41248
+ } else {
41249
+ LOG.warn("Server", `[ServerConn] Pong timeout (${misses}x) \u2014 keeping WS open and retrying`);
41215
41250
  }
41216
41251
  }, 15e3);
41217
41252
  } catch {
@@ -41223,12 +41258,13 @@ var init_server_connection = __esm({
41223
41258
  clearInterval(this.pingTimer);
41224
41259
  this.pingTimer = null;
41225
41260
  }
41226
- if (this.pongTimeout) {
41227
- clearTimeout(this.pongTimeout);
41228
- this.pongTimeout = null;
41229
- }
41261
+ this.clearPongTimeout();
41230
41262
  }
41231
41263
  onPong() {
41264
+ this.clearPongTimeout();
41265
+ this.missedPongCount = 0;
41266
+ }
41267
+ clearPongTimeout() {
41232
41268
  if (this.pongTimeout) {
41233
41269
  clearTimeout(this.pongTimeout);
41234
41270
  this.pongTimeout = null;
@@ -41578,8 +41614,14 @@ async function initiateConnection(deps, peerId, sharePermission) {
41578
41614
  }
41579
41615
  const pid = peerId || `legacy_${Date.now()}`;
41580
41616
  const existing = deps.peers.get(pid);
41581
- if (existing?.state === "connected") return;
41582
- if (existing?.state === "connecting") disconnectPeer(deps.peers, pid, deps.notifyStateChange);
41617
+ if (existing?.state === "connected") {
41618
+ log(`initiateconnection() ignored for peer ${pid} \u2014 already connected`);
41619
+ return;
41620
+ }
41621
+ if (existing?.state === "connecting") {
41622
+ log(`initiateconnection() ignored for peer ${pid} \u2014 connection already in progress`);
41623
+ return;
41624
+ }
41583
41625
  log(`initiateconnection() for peer ${pid}...`);
41584
41626
  const mod = deps.nodeDatachannel;
41585
41627
  const PeerConnectionCtor = mod.PeerConnection || mod.default?.PeerConnection || mod.default || mod;
@@ -41879,6 +41921,7 @@ var init_daemon_p2p = __esm({
41879
41921
  peers = /* @__PURE__ */ new Map();
41880
41922
  nodeDatachannel = null;
41881
41923
  stateListeners = [];
41924
+ lastNotifiedState = null;
41882
41925
  screenshotSender = new ScreenshotSender();
41883
41926
  // Handler storage — exposed to router via DataChannelHandlers interface
41884
41927
  handlers = {
@@ -42016,6 +42059,8 @@ ${e?.stack || ""}`);
42016
42059
  }
42017
42060
  notifyStateChange = () => {
42018
42061
  const state = this.connectionState;
42062
+ if (state === this.lastNotifiedState) return;
42063
+ this.lastNotifiedState = state;
42019
42064
  this.stateListeners.forEach((fn) => fn(state));
42020
42065
  };
42021
42066
  get connectionManagerDeps() {
@@ -42043,6 +42088,7 @@ ${e?.stack || ""}`);
42043
42088
  for (const peerId of Array.from(this.peers.keys())) {
42044
42089
  this.disconnectPeer(peerId);
42045
42090
  }
42091
+ this.lastNotifiedState = null;
42046
42092
  }
42047
42093
  // ─── Delegated data sending ─────────────────────
42048
42094
  sendStatus(status) {
@@ -49353,7 +49399,7 @@ var init_adhdev_daemon = __esm({
49353
49399
  import_ws3 = require("ws");
49354
49400
  import_chalk2 = __toESM(require("chalk"));
49355
49401
  init_version();
49356
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.34" });
49402
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.35" });
49357
49403
  AdhdevDaemon = class {
49358
49404
  localHttpServer = null;
49359
49405
  localWss = null;
@@ -49531,10 +49577,10 @@ ${err?.stack || ""}`);
49531
49577
  if (state === "connected") {
49532
49578
  LOG.info("P2P", "Peer connected \u2192 sending immediate full status report");
49533
49579
  this.statusReporter?.resetP2PHash();
49534
- this.statusReporter?.sendUnifiedStatusReport().catch((e) => LOG.warn("P2P", `Immediate status report failed: ${e?.message}`));
49580
+ this.statusReporter?.sendUnifiedStatusReport({ reason: "p2p-connect" }).catch((e) => LOG.warn("P2P", `Immediate status report failed: ${e?.message}`));
49535
49581
  setTimeout(() => {
49536
49582
  this.statusReporter?.resetP2PHash();
49537
- this.statusReporter?.sendUnifiedStatusReport().catch(() => {
49583
+ this.statusReporter?.sendUnifiedStatusReport({ reason: "p2p-connect-followup" }).catch(() => {
49538
49584
  });
49539
49585
  }, 2e3);
49540
49586
  }
@@ -49573,7 +49619,7 @@ ${err?.stack || ""}`);
49573
49619
  this.serverConn.onStateChange((state) => {
49574
49620
  if (state === "connected") {
49575
49621
  console.log(import_chalk2.default.green(" \u{1F4E1} Connected to ADHDev server"));
49576
- this.statusReporter?.sendUnifiedStatusReport();
49622
+ this.statusReporter?.sendUnifiedStatusReport({ forceServer: true, reason: "ws-connected" });
49577
49623
  } else if (state === "disconnected") {
49578
49624
  console.log(import_chalk2.default.yellow(" \u26A0 Server disconnected, will reconnect..."));
49579
49625
  }
@@ -51617,6 +51663,51 @@ function registerSetupCommands(program2, providerLoader) {
51617
51663
  console.log(import_chalk4.default.gray(" Auth credentials cleared. IDE and workspace settings preserved."));
51618
51664
  console.log(import_chalk4.default.gray(" Run `adhdev setup` to log in again.\n"));
51619
51665
  });
51666
+ program2.command("uninstall").description("Completely wipe all setting, configuration, stop daemon and uninstall service").option("-f, --force", "Skip confirmation prompt").action(async (options) => {
51667
+ const inquirer2 = await import("inquirer");
51668
+ const fs20 = await import("fs");
51669
+ const path29 = await import("path");
51670
+ const os26 = await import("os");
51671
+ const { spawnSync: spawnSync2 } = await import("child_process");
51672
+ if (!options.force) {
51673
+ const { confirm } = await inquirer2.default.prompt([
51674
+ {
51675
+ type: "confirm",
51676
+ name: "confirm",
51677
+ message: "Are you sure you want to completely wipe ADHDev settings, uninstall the daemon service, and remove all downloaded data? This cannot be undone.",
51678
+ default: false
51679
+ }
51680
+ ]);
51681
+ if (!confirm) return;
51682
+ }
51683
+ console.log(import_chalk4.default.bold("\n\u{1F5D1}\uFE0F Uninstalling ADHDev\n"));
51684
+ try {
51685
+ const { isDaemonRunning: isDaemonRunning2, stopDaemon: stopDaemon2 } = await Promise.resolve().then(() => (init_adhdev_daemon(), adhdev_daemon_exports));
51686
+ if (isDaemonRunning2()) {
51687
+ console.log(import_chalk4.default.gray(" Stopping daemon..."));
51688
+ stopDaemon2();
51689
+ console.log(import_chalk4.default.green(" \u2713 Daemon stopped."));
51690
+ }
51691
+ } catch {
51692
+ }
51693
+ console.log(import_chalk4.default.gray(" Removing OS background service..."));
51694
+ spawnSync2(process.execPath, [process.argv[1], "service", "uninstall"], { stdio: "inherit" });
51695
+ const adhdevDir = path29.join(os26.homedir(), ".adhdev");
51696
+ if (fs20.existsSync(adhdevDir)) {
51697
+ console.log(import_chalk4.default.gray(` Deleting ${adhdevDir}...`));
51698
+ try {
51699
+ fs20.rmSync(adhdevDir, { recursive: true, force: true });
51700
+ console.log(import_chalk4.default.green(" \u2713 Data wiped."));
51701
+ } catch (e) {
51702
+ console.log(import_chalk4.default.red(` \u2717 Failed to delete ~/.adhdev: ${e.message}`));
51703
+ }
51704
+ } else {
51705
+ console.log(import_chalk4.default.green(" \u2713 ~/.adhdev not found or already deleted."));
51706
+ }
51707
+ console.log(import_chalk4.default.green("\n\u2713 Uninstall complete."));
51708
+ console.log(import_chalk4.default.whiteBright(" To finish uninstalling the CLI tool, please run:"));
51709
+ console.log(import_chalk4.default.cyan(" npm uninstall -g adhdev\n"));
51710
+ });
51620
51711
  }
51621
51712
 
51622
51713
  // src/cli/daemon-commands.ts
@@ -52159,6 +52250,36 @@ function probeNodeDatachannel(packageRoot) {
52159
52250
  };
52160
52251
  }
52161
52252
  }
52253
+ function probeSharpRuntime(packageRoot, nativeSharpPackage) {
52254
+ const resolved = resolveModuleFromPackage("sharp", packageRoot);
52255
+ if (!resolved) {
52256
+ return {
52257
+ label: "sharp",
52258
+ ok: false,
52259
+ detail: "module not found",
52260
+ fatal: false
52261
+ };
52262
+ }
52263
+ try {
52264
+ const sharp = require(resolved);
52265
+ const nativeResolved = resolveModuleFromPackage(nativeSharpPackage, packageRoot);
52266
+ const runtimeVersion = sharp?.versions?.sharp || sharp?.version || "unknown";
52267
+ const nativeDetail = nativeResolved ? `${nativeSharpPackage} resolved` : `${nativeSharpPackage} not separately resolvable`;
52268
+ return {
52269
+ label: "sharp",
52270
+ ok: typeof sharp === "function" || typeof sharp?.default === "function",
52271
+ detail: `${resolved} (runtime=${runtimeVersion}; ${nativeDetail})`,
52272
+ fatal: false
52273
+ };
52274
+ } catch (error48) {
52275
+ return {
52276
+ label: "sharp",
52277
+ ok: false,
52278
+ detail: `${resolved} (${error48?.message || "load failed"})`,
52279
+ fatal: false
52280
+ };
52281
+ }
52282
+ }
52162
52283
  function probeConfigAccess() {
52163
52284
  const configDir = path27.join(os24.homedir(), ".adhdev");
52164
52285
  const configPath = path27.join(configDir, "config.json");
@@ -52307,18 +52428,7 @@ function registerDoctorCommands(program2, pkgVersion3) {
52307
52428
  fatal: true
52308
52429
  },
52309
52430
  probeNodeDatachannel(packageRoot),
52310
- {
52311
- label: "sharp",
52312
- ok: Boolean(resolveModuleFromPackage("sharp", packageRoot)),
52313
- detail: resolveModuleFromPackage("sharp", packageRoot) || "module not found",
52314
- fatal: false
52315
- },
52316
- {
52317
- label: nativeSharpPackage,
52318
- ok: Boolean(resolveModuleFromPackage(nativeSharpPackage, packageRoot)),
52319
- detail: resolveModuleFromPackage(nativeSharpPackage, packageRoot) || "module not found",
52320
- fatal: false
52321
- },
52431
+ probeSharpRuntime(packageRoot, nativeSharpPackage),
52322
52432
  ...probeConfigAccess(),
52323
52433
  ...buildBrowseProbeChecks()
52324
52434
  ];
@@ -52333,7 +52443,7 @@ function registerDoctorCommands(program2, pkgVersion3) {
52333
52443
  checks.push({
52334
52444
  label: "Session host IPC",
52335
52445
  ok: probe.reachable,
52336
- detail: probe.reachable ? `reachable (${probe.runtimeCount} runtime(s), ${probe.endpoint.kind}:${probe.endpoint.path})` : `unreachable (${probe.endpoint.kind}:${probe.endpoint.path})`
52446
+ detail: probe.reachable ? `reachable (${probe.runtimeCount} runtime(s), ${probe.endpoint.kind}:${probe.endpoint.path})` : `${sessionHostPid ? "unreachable (stale PID or lazy host)" : "unreachable (host not running)"} (${probe.endpoint.kind}:${probe.endpoint.path})`
52337
52447
  });
52338
52448
  } catch (e) {
52339
52449
  checks.push({