adhdev 0.8.33 → 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,12 +41127,15 @@ 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;
41106
- this.userPlan = message.payload.plan || "free";
41107
- this.iceServers = message.payload.iceServers || null;
41108
- this.planLimits = message.payload.limits || null;
41135
+ const payload = message.payload;
41136
+ this.userPlan = payload.plan || "free";
41137
+ this.iceServers = payload.iceServers || null;
41138
+ this.planLimits = payload.limits || null;
41109
41139
  if (this.iceServers?.length) {
41110
41140
  const hasTurn = this.iceServers.some((s) => JSON.stringify(s.urls || "").includes("turn"));
41111
41141
  LOG.info("Server", `[ServerConn] ICE servers: ${this.iceServers.length} (TURN: ${hasTurn ? "\u2705" : "\u274C STUN only"})`);
@@ -41199,6 +41229,7 @@ var init_server_connection = __esm({
41199
41229
  this.reconnectTimer = null;
41200
41230
  }
41201
41231
  this.stopHeartbeat();
41232
+ this.missedPongCount = 0;
41202
41233
  }
41203
41234
  // ─── WS Heartbeat (ping/pong) ─────────────────────
41204
41235
  startHeartbeat() {
@@ -41208,9 +41239,14 @@ var init_server_connection = __esm({
41208
41239
  try {
41209
41240
  this.ws.ping();
41210
41241
  this.pongTimeout = setTimeout(() => {
41211
- LOG.info("Server", "[ServerConn] \u26A0 Pong timeout (15s) \u2014 closing zombie WS");
41212
- 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`);
41213
41247
  this.ws.terminate();
41248
+ } else {
41249
+ LOG.warn("Server", `[ServerConn] Pong timeout (${misses}x) \u2014 keeping WS open and retrying`);
41214
41250
  }
41215
41251
  }, 15e3);
41216
41252
  } catch {
@@ -41222,12 +41258,13 @@ var init_server_connection = __esm({
41222
41258
  clearInterval(this.pingTimer);
41223
41259
  this.pingTimer = null;
41224
41260
  }
41225
- if (this.pongTimeout) {
41226
- clearTimeout(this.pongTimeout);
41227
- this.pongTimeout = null;
41228
- }
41261
+ this.clearPongTimeout();
41229
41262
  }
41230
41263
  onPong() {
41264
+ this.clearPongTimeout();
41265
+ this.missedPongCount = 0;
41266
+ }
41267
+ clearPongTimeout() {
41231
41268
  if (this.pongTimeout) {
41232
41269
  clearTimeout(this.pongTimeout);
41233
41270
  this.pongTimeout = null;
@@ -41577,8 +41614,14 @@ async function initiateConnection(deps, peerId, sharePermission) {
41577
41614
  }
41578
41615
  const pid = peerId || `legacy_${Date.now()}`;
41579
41616
  const existing = deps.peers.get(pid);
41580
- if (existing?.state === "connected") return;
41581
- 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
+ }
41582
41625
  log(`initiateconnection() for peer ${pid}...`);
41583
41626
  const mod = deps.nodeDatachannel;
41584
41627
  const PeerConnectionCtor = mod.PeerConnection || mod.default?.PeerConnection || mod.default || mod;
@@ -41878,6 +41921,7 @@ var init_daemon_p2p = __esm({
41878
41921
  peers = /* @__PURE__ */ new Map();
41879
41922
  nodeDatachannel = null;
41880
41923
  stateListeners = [];
41924
+ lastNotifiedState = null;
41881
41925
  screenshotSender = new ScreenshotSender();
41882
41926
  // Handler storage — exposed to router via DataChannelHandlers interface
41883
41927
  handlers = {
@@ -42015,6 +42059,8 @@ ${e?.stack || ""}`);
42015
42059
  }
42016
42060
  notifyStateChange = () => {
42017
42061
  const state = this.connectionState;
42062
+ if (state === this.lastNotifiedState) return;
42063
+ this.lastNotifiedState = state;
42018
42064
  this.stateListeners.forEach((fn) => fn(state));
42019
42065
  };
42020
42066
  get connectionManagerDeps() {
@@ -42042,6 +42088,7 @@ ${e?.stack || ""}`);
42042
42088
  for (const peerId of Array.from(this.peers.keys())) {
42043
42089
  this.disconnectPeer(peerId);
42044
42090
  }
42091
+ this.lastNotifiedState = null;
42045
42092
  }
42046
42093
  // ─── Delegated data sending ─────────────────────
42047
42094
  sendStatus(status) {
@@ -49352,7 +49399,7 @@ var init_adhdev_daemon = __esm({
49352
49399
  import_ws3 = require("ws");
49353
49400
  import_chalk2 = __toESM(require("chalk"));
49354
49401
  init_version();
49355
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.33" });
49402
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.35" });
49356
49403
  AdhdevDaemon = class {
49357
49404
  localHttpServer = null;
49358
49405
  localWss = null;
@@ -49530,10 +49577,10 @@ ${err?.stack || ""}`);
49530
49577
  if (state === "connected") {
49531
49578
  LOG.info("P2P", "Peer connected \u2192 sending immediate full status report");
49532
49579
  this.statusReporter?.resetP2PHash();
49533
- 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}`));
49534
49581
  setTimeout(() => {
49535
49582
  this.statusReporter?.resetP2PHash();
49536
- this.statusReporter?.sendUnifiedStatusReport().catch(() => {
49583
+ this.statusReporter?.sendUnifiedStatusReport({ reason: "p2p-connect-followup" }).catch(() => {
49537
49584
  });
49538
49585
  }, 2e3);
49539
49586
  }
@@ -49572,7 +49619,7 @@ ${err?.stack || ""}`);
49572
49619
  this.serverConn.onStateChange((state) => {
49573
49620
  if (state === "connected") {
49574
49621
  console.log(import_chalk2.default.green(" \u{1F4E1} Connected to ADHDev server"));
49575
- this.statusReporter?.sendUnifiedStatusReport();
49622
+ this.statusReporter?.sendUnifiedStatusReport({ forceServer: true, reason: "ws-connected" });
49576
49623
  } else if (state === "disconnected") {
49577
49624
  console.log(import_chalk2.default.yellow(" \u26A0 Server disconnected, will reconnect..."));
49578
49625
  }
@@ -51616,6 +51663,51 @@ function registerSetupCommands(program2, providerLoader) {
51616
51663
  console.log(import_chalk4.default.gray(" Auth credentials cleared. IDE and workspace settings preserved."));
51617
51664
  console.log(import_chalk4.default.gray(" Run `adhdev setup` to log in again.\n"));
51618
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
+ });
51619
51711
  }
51620
51712
 
51621
51713
  // src/cli/daemon-commands.ts
@@ -52158,6 +52250,36 @@ function probeNodeDatachannel(packageRoot) {
52158
52250
  };
52159
52251
  }
52160
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
+ }
52161
52283
  function probeConfigAccess() {
52162
52284
  const configDir = path27.join(os24.homedir(), ".adhdev");
52163
52285
  const configPath = path27.join(configDir, "config.json");
@@ -52306,18 +52428,7 @@ function registerDoctorCommands(program2, pkgVersion3) {
52306
52428
  fatal: true
52307
52429
  },
52308
52430
  probeNodeDatachannel(packageRoot),
52309
- {
52310
- label: "sharp",
52311
- ok: Boolean(resolveModuleFromPackage("sharp", packageRoot)),
52312
- detail: resolveModuleFromPackage("sharp", packageRoot) || "module not found",
52313
- fatal: false
52314
- },
52315
- {
52316
- label: nativeSharpPackage,
52317
- ok: Boolean(resolveModuleFromPackage(nativeSharpPackage, packageRoot)),
52318
- detail: resolveModuleFromPackage(nativeSharpPackage, packageRoot) || "module not found",
52319
- fatal: false
52320
- },
52431
+ probeSharpRuntime(packageRoot, nativeSharpPackage),
52321
52432
  ...probeConfigAccess(),
52322
52433
  ...buildBrowseProbeChecks()
52323
52434
  ];
@@ -52332,7 +52443,7 @@ function registerDoctorCommands(program2, pkgVersion3) {
52332
52443
  checks.push({
52333
52444
  label: "Session host IPC",
52334
52445
  ok: probe.reachable,
52335
- 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})`
52336
52447
  });
52337
52448
  } catch (e) {
52338
52449
  checks.push({