adhdev 0.9.1 → 0.9.4

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
@@ -1433,8 +1433,8 @@ async function detectIDEs(providerLoader) {
1433
1433
  if ((0, import_fs3.existsSync)(bundledCli)) resolvedCli = bundledCli;
1434
1434
  }
1435
1435
  if (!resolvedCli && appPath && os31 === "win32") {
1436
- const { dirname: dirname10 } = await import("path");
1437
- const appDir = dirname10(appPath);
1436
+ const { dirname: dirname11 } = await import("path");
1437
+ const appDir = dirname11(appPath);
1438
1438
  const candidates = [
1439
1439
  `${appDir}\\\\bin\\\\${def.cli}.cmd`,
1440
1440
  `${appDir}\\\\bin\\\\${def.cli}`,
@@ -14066,10 +14066,6 @@ ${data.message || ""}`.trim();
14066
14066
  throw new Error(`${this.cliName} is still processing the previous prompt`);
14067
14067
  }
14068
14068
  }
14069
- const blockingModal = this.activeModal || this.getStartupConfirmationModal(this.terminalScreen.getText() || "");
14070
- if (blockingModal || this.currentStatus === "waiting_approval") {
14071
- throw new Error(`${this.cliName} is awaiting confirmation before it can accept a prompt`);
14072
- }
14073
14069
  this.isWaitingForResponse = true;
14074
14070
  this.responseBuffer = "";
14075
14071
  this.finishRetryCount = 0;
@@ -33907,10 +33903,10 @@ var init_readdirp = __esm({
33907
33903
  }
33908
33904
  async _formatEntry(dirent, path35) {
33909
33905
  let entry;
33910
- const basename9 = this._isDirent ? dirent.name : dirent;
33906
+ const basename10 = this._isDirent ? dirent.name : dirent;
33911
33907
  try {
33912
- const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path35, basename9));
33913
- entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename9 };
33908
+ const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path35, basename10));
33909
+ entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename10 };
33914
33910
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
33915
33911
  } catch (err) {
33916
33912
  this._onError(err);
@@ -34441,9 +34437,9 @@ var init_handler2 = __esm({
34441
34437
  _watchWithNodeFs(path35, listener) {
34442
34438
  const opts = this.fsw.options;
34443
34439
  const directory = sp.dirname(path35);
34444
- const basename9 = sp.basename(path35);
34440
+ const basename10 = sp.basename(path35);
34445
34441
  const parent = this.fsw._getWatchedDir(directory);
34446
- parent.add(basename9);
34442
+ parent.add(basename10);
34447
34443
  const absolutePath = sp.resolve(path35);
34448
34444
  const options = {
34449
34445
  persistent: opts.persistent
@@ -34453,7 +34449,7 @@ var init_handler2 = __esm({
34453
34449
  let closer;
34454
34450
  if (opts.usePolling) {
34455
34451
  const enableBin = opts.interval !== opts.binaryInterval;
34456
- options.interval = enableBin && isBinaryPath(basename9) ? opts.binaryInterval : opts.interval;
34452
+ options.interval = enableBin && isBinaryPath(basename10) ? opts.binaryInterval : opts.interval;
34457
34453
  closer = setFsWatchFileListener(path35, absolutePath, options, {
34458
34454
  listener,
34459
34455
  rawEmitter: this.fsw._emitRaw
@@ -34475,11 +34471,11 @@ var init_handler2 = __esm({
34475
34471
  if (this.fsw.closed) {
34476
34472
  return;
34477
34473
  }
34478
- const dirname10 = sp.dirname(file2);
34479
- const basename9 = sp.basename(file2);
34480
- const parent = this.fsw._getWatchedDir(dirname10);
34474
+ const dirname11 = sp.dirname(file2);
34475
+ const basename10 = sp.basename(file2);
34476
+ const parent = this.fsw._getWatchedDir(dirname11);
34481
34477
  let prevStats = stats;
34482
- if (parent.has(basename9))
34478
+ if (parent.has(basename10))
34483
34479
  return;
34484
34480
  const listener = async (path35, newStats) => {
34485
34481
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file2, 5))
@@ -34504,9 +34500,9 @@ var init_handler2 = __esm({
34504
34500
  prevStats = newStats2;
34505
34501
  }
34506
34502
  } catch (error48) {
34507
- this.fsw._remove(dirname10, basename9);
34503
+ this.fsw._remove(dirname11, basename10);
34508
34504
  }
34509
- } else if (parent.has(basename9)) {
34505
+ } else if (parent.has(basename10)) {
34510
34506
  const at = newStats.atimeMs;
34511
34507
  const mt = newStats.mtimeMs;
34512
34508
  if (!at || at <= mt || mt !== prevStats.mtimeMs) {
@@ -37821,9 +37817,82 @@ function appendUpgradeLog(message) {
37821
37817
  } catch {
37822
37818
  }
37823
37819
  }
37824
- function getNpmExecutable() {
37820
+ function resolveSiblingNpmExecutable(nodeExecutable) {
37821
+ const binDir = path17.dirname(nodeExecutable);
37822
+ const candidates = process.platform === "win32" ? ["npm.cmd", "npm.exe", "npm"] : ["npm"];
37823
+ for (const candidate of candidates) {
37824
+ const candidatePath = path17.join(binDir, candidate);
37825
+ if (fs8.existsSync(candidatePath)) {
37826
+ return candidatePath;
37827
+ }
37828
+ }
37825
37829
  return "npm";
37826
37830
  }
37831
+ function findCurrentPackageRoot(currentCliPath, packageName) {
37832
+ if (!currentCliPath) return null;
37833
+ let resolvedPath = currentCliPath;
37834
+ try {
37835
+ resolvedPath = fs8.realpathSync.native(currentCliPath);
37836
+ } catch {
37837
+ }
37838
+ let currentDir = resolvedPath;
37839
+ try {
37840
+ if (fs8.statSync(resolvedPath).isFile()) {
37841
+ currentDir = path17.dirname(resolvedPath);
37842
+ }
37843
+ } catch {
37844
+ currentDir = path17.dirname(resolvedPath);
37845
+ }
37846
+ while (true) {
37847
+ const packageJsonPath = path17.join(currentDir, "package.json");
37848
+ try {
37849
+ if (fs8.existsSync(packageJsonPath)) {
37850
+ const parsed = JSON.parse(fs8.readFileSync(packageJsonPath, "utf8"));
37851
+ if (parsed?.name === packageName) {
37852
+ const normalized = currentDir.replace(/\\/g, "/");
37853
+ return normalized.includes("/node_modules/") ? currentDir : null;
37854
+ }
37855
+ }
37856
+ } catch {
37857
+ }
37858
+ const parentDir = path17.dirname(currentDir);
37859
+ if (parentDir === currentDir) {
37860
+ return null;
37861
+ }
37862
+ currentDir = parentDir;
37863
+ }
37864
+ }
37865
+ function resolveInstallPrefixFromPackageRoot(packageRoot, packageName) {
37866
+ const nodeModulesDir = packageName.startsWith("@") ? path17.dirname(path17.dirname(packageRoot)) : path17.dirname(packageRoot);
37867
+ if (path17.basename(nodeModulesDir) !== "node_modules") {
37868
+ return null;
37869
+ }
37870
+ const maybeLibDir = path17.dirname(nodeModulesDir);
37871
+ if (path17.basename(maybeLibDir) === "lib") {
37872
+ return path17.dirname(maybeLibDir);
37873
+ }
37874
+ return maybeLibDir;
37875
+ }
37876
+ function resolveCurrentGlobalInstallSurface(options) {
37877
+ const packageRoot = findCurrentPackageRoot(options.currentCliPath || process.argv[1], options.packageName);
37878
+ return {
37879
+ npmExecutable: resolveSiblingNpmExecutable(options.nodeExecutable || process.execPath),
37880
+ packageRoot,
37881
+ installPrefix: packageRoot ? resolveInstallPrefixFromPackageRoot(packageRoot, options.packageName) : null
37882
+ };
37883
+ }
37884
+ function buildPinnedGlobalInstallCommand(options) {
37885
+ const surface = resolveCurrentGlobalInstallSurface(options);
37886
+ const args = ["install", "-g", `${options.packageName}@${options.targetVersion || "latest"}`, "--force"];
37887
+ if (surface.installPrefix) {
37888
+ args.push("--prefix", surface.installPrefix);
37889
+ }
37890
+ return {
37891
+ command: surface.npmExecutable,
37892
+ args,
37893
+ surface
37894
+ };
37895
+ }
37827
37896
  function getNpmExecOptions() {
37828
37897
  return { shell: process.platform === "win32" };
37829
37898
  }
@@ -37886,11 +37955,12 @@ function removeDaemonPidFile() {
37886
37955
  } catch {
37887
37956
  }
37888
37957
  }
37889
- function cleanupStaleGlobalInstallDirs(pkgName) {
37958
+ function cleanupStaleGlobalInstallDirs(pkgName, surface) {
37890
37959
  const npmExecOpts = getNpmExecOptions();
37891
- const npmRoot = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["root", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
37960
+ const prefixArgs = surface.installPrefix ? ["--prefix", surface.installPrefix] : [];
37961
+ const npmRoot = (0, import_child_process7.execFileSync)(surface.npmExecutable, ["root", "-g", ...prefixArgs], { encoding: "utf8", ...npmExecOpts }).trim();
37892
37962
  if (!npmRoot) return;
37893
- const npmPrefix = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["prefix", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
37963
+ const npmPrefix = surface.installPrefix || (0, import_child_process7.execFileSync)(surface.npmExecutable, ["prefix", "-g", ...prefixArgs], { encoding: "utf8", ...npmExecOpts }).trim();
37894
37964
  const binDir = process.platform === "win32" ? npmPrefix : path17.join(npmPrefix, "bin");
37895
37965
  const packageBaseName = pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName;
37896
37966
  const binNames = /* @__PURE__ */ new Set([packageBaseName]);
@@ -37915,7 +37985,7 @@ function cleanupStaleGlobalInstallDirs(pkgName) {
37915
37985
  }
37916
37986
  if (fs8.existsSync(binDir)) {
37917
37987
  for (const entry of fs8.readdirSync(binDir)) {
37918
- if (![...binNames].some((name) => entry.startsWith(`.${name}-`))) continue;
37988
+ if (!Array.from(binNames).some((name) => entry.startsWith(`.${name}-`))) continue;
37919
37989
  fs8.rmSync(path17.join(binDir, entry), { recursive: true, force: true });
37920
37990
  appendUpgradeLog(`Removed stale bin staging entry: ${path17.join(binDir, entry)}`);
37921
37991
  }
@@ -37935,19 +38005,27 @@ function spawnDetachedDaemonUpgradeHelper(payload) {
37935
38005
  async function runDaemonUpgradeHelper(payload) {
37936
38006
  const restartArgv = Array.isArray(payload.restartArgv) ? payload.restartArgv : [];
37937
38007
  const sessionHostAppName = payload.sessionHostAppName || process.env.ADHDEV_SESSION_HOST_NAME || "adhdev";
38008
+ const installCommand = buildPinnedGlobalInstallCommand({
38009
+ packageName: payload.packageName,
38010
+ targetVersion: payload.targetVersion
38011
+ });
37938
38012
  appendUpgradeLog(`Upgrade helper started for ${payload.packageName}@${payload.targetVersion}`);
38013
+ appendUpgradeLog(`Using npm executable: ${installCommand.command}`);
38014
+ if (installCommand.surface.installPrefix) {
38015
+ appendUpgradeLog(`Pinned install prefix: ${installCommand.surface.installPrefix}`);
38016
+ }
37939
38017
  if (Number.isFinite(payload.parentPid) && payload.parentPid > 0) {
37940
38018
  appendUpgradeLog(`Waiting for parent pid ${payload.parentPid} to exit`);
37941
38019
  await waitForPidExit(payload.parentPid, 15e3);
37942
38020
  }
37943
38021
  stopSessionHostProcesses(sessionHostAppName);
37944
38022
  removeDaemonPidFile();
37945
- cleanupStaleGlobalInstallDirs(payload.packageName);
38023
+ cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
37946
38024
  const spec = `${payload.packageName}@${payload.targetVersion || "latest"}`;
37947
38025
  appendUpgradeLog(`Installing ${spec}`);
37948
38026
  const installOutput = (0, import_child_process7.execFileSync)(
37949
- getNpmExecutable(),
37950
- ["install", "-g", spec, "--force"],
38027
+ installCommand.command,
38028
+ installCommand.args,
37951
38029
  {
37952
38030
  encoding: "utf8",
37953
38031
  stdio: "pipe",
@@ -37960,7 +38038,7 @@ async function runDaemonUpgradeHelper(payload) {
37960
38038
  }
37961
38039
  if (process.platform === "win32") {
37962
38040
  await new Promise((resolve18) => setTimeout(resolve18, 500));
37963
- cleanupStaleGlobalInstallDirs(payload.packageName);
38041
+ cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
37964
38042
  appendUpgradeLog("Post-install staging cleanup complete");
37965
38043
  }
37966
38044
  if (restartArgv.length > 0) {
@@ -46848,6 +46926,7 @@ __export(src_exports, {
46848
46926
  buildChatMessageSignature: () => buildChatMessageSignature,
46849
46927
  buildChatTailDeliverySignature: () => buildChatTailDeliverySignature,
46850
46928
  buildMachineInfo: () => buildMachineInfo,
46929
+ buildPinnedGlobalInstallCommand: () => buildPinnedGlobalInstallCommand,
46851
46930
  buildRuntimeSystemChatMessage: () => buildRuntimeSystemChatMessage,
46852
46931
  buildSessionEntries: () => buildSessionEntries,
46853
46932
  buildSessionModalDeliverySignature: () => buildSessionModalDeliverySignature,
@@ -46930,6 +47009,7 @@ __export(src_exports, {
46930
47009
  resetDebugRuntimeConfig: () => resetDebugRuntimeConfig,
46931
47010
  resetState: () => resetState,
46932
47011
  resolveChatMessageKind: () => resolveChatMessageKind,
47012
+ resolveCurrentGlobalInstallSurface: () => resolveCurrentGlobalInstallSurface,
46933
47013
  resolveDebugRuntimeConfig: () => resolveDebugRuntimeConfig,
46934
47014
  resolveSessionHostAppName: () => resolveSessionHostAppName,
46935
47015
  resolveSessionHostAppNameResolution: () => resolveSessionHostAppNameResolution,
@@ -78239,6 +78319,33 @@ var init_open = __esm({
78239
78319
  }
78240
78320
  });
78241
78321
 
78322
+ // src/version.ts
78323
+ function resolvePackageVersion(options) {
78324
+ const injectedVersion = options?.injectedVersion || "unknown";
78325
+ const dir = options?.dirname || __dirname;
78326
+ const possiblePaths = [
78327
+ (0, import_path3.join)(dir, "..", "..", "package.json"),
78328
+ (0, import_path3.join)(dir, "..", "package.json"),
78329
+ (0, import_path3.join)(dir, "package.json")
78330
+ ];
78331
+ for (const p of possiblePaths) {
78332
+ try {
78333
+ const data = JSON.parse((0, import_fs6.readFileSync)(p, "utf-8"));
78334
+ if (data.version) return data.version;
78335
+ } catch {
78336
+ }
78337
+ }
78338
+ return injectedVersion;
78339
+ }
78340
+ var import_fs6, import_path3;
78341
+ var init_version = __esm({
78342
+ "src/version.ts"() {
78343
+ "use strict";
78344
+ import_fs6 = require("fs");
78345
+ import_path3 = require("path");
78346
+ }
78347
+ });
78348
+
78242
78349
  // src/server-connection.ts
78243
78350
  var import_ws2, ServerConnection;
78244
78351
  var init_server_connection = __esm({
@@ -86927,33 +87034,6 @@ var init_session_host_controller = __esm({
86927
87034
  }
86928
87035
  });
86929
87036
 
86930
- // src/version.ts
86931
- function resolvePackageVersion(options) {
86932
- const injectedVersion = options?.injectedVersion || "unknown";
86933
- const dir = options?.dirname || __dirname;
86934
- const possiblePaths = [
86935
- (0, import_path3.join)(dir, "..", "..", "package.json"),
86936
- (0, import_path3.join)(dir, "..", "package.json"),
86937
- (0, import_path3.join)(dir, "package.json")
86938
- ];
86939
- for (const p of possiblePaths) {
86940
- try {
86941
- const data = JSON.parse((0, import_fs6.readFileSync)(p, "utf-8"));
86942
- if (data.version) return data.version;
86943
- } catch {
86944
- }
86945
- }
86946
- return injectedVersion;
86947
- }
86948
- var import_fs6, import_path3;
86949
- var init_version = __esm({
86950
- "src/version.ts"() {
86951
- "use strict";
86952
- import_fs6 = require("fs");
86953
- import_path3 = require("path");
86954
- }
86955
- });
86956
-
86957
87037
  // src/adhdev-daemon.ts
86958
87038
  var adhdev_daemon_exports = {};
86959
87039
  __export(adhdev_daemon_exports, {
@@ -87095,7 +87175,7 @@ var init_adhdev_daemon = __esm({
87095
87175
  init_version();
87096
87176
  init_src();
87097
87177
  init_runtime_defaults();
87098
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.1" });
87178
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.4" });
87099
87179
  AdhdevDaemon = class _AdhdevDaemon {
87100
87180
  localHttpServer = null;
87101
87181
  localWss = null;
@@ -88153,6 +88233,36 @@ function hasCloudMachineAuth() {
88153
88233
  const config2 = loadConfig();
88154
88234
  return Boolean(config2.machineSecret && config2.machineSecret.trim());
88155
88235
  }
88236
+ function readLatestPublishedCliVersion(execFileSyncLocal) {
88237
+ const surface = resolveCurrentGlobalInstallSurface({ packageName: "adhdev" });
88238
+ try {
88239
+ return execFileSyncLocal(surface.npmExecutable, ["view", "adhdev", "version"], {
88240
+ encoding: "utf-8",
88241
+ timeout: 5e3,
88242
+ stdio: ["pipe", "pipe", "pipe"]
88243
+ }).trim();
88244
+ } catch {
88245
+ return null;
88246
+ }
88247
+ }
88248
+ function readInstalledGlobalCliVersion(execFileSyncLocal) {
88249
+ const surface = resolveCurrentGlobalInstallSurface({ packageName: "adhdev" });
88250
+ const args = ["list", "-g", "adhdev", "--json"];
88251
+ if (surface.installPrefix) {
88252
+ args.push("--prefix", surface.installPrefix);
88253
+ }
88254
+ try {
88255
+ const result = execFileSyncLocal(surface.npmExecutable, args, {
88256
+ encoding: "utf-8",
88257
+ timeout: 5e3,
88258
+ stdio: ["pipe", "pipe", "pipe"]
88259
+ });
88260
+ const parsed = JSON.parse(result);
88261
+ return parsed.dependencies?.adhdev?.version || null;
88262
+ } catch {
88263
+ return null;
88264
+ }
88265
+ }
88156
88266
  async function runWizard(options = {}) {
88157
88267
  console.log(LOGO);
88158
88268
  if (isSetupComplete() && hasCloudMachineAuth() && !options.force) {
@@ -88168,27 +88278,10 @@ async function runWizard(options = {}) {
88168
88278
  }
88169
88279
  async function checkForUpdate() {
88170
88280
  try {
88171
- const { execSync: execSync8 } = await import("child_process");
88172
- let currentVersion = null;
88173
- try {
88174
- currentVersion = execSync8("adhdev --version", {
88175
- encoding: "utf-8",
88176
- timeout: 3e3,
88177
- stdio: ["pipe", "pipe", "pipe"]
88178
- }).trim();
88179
- } catch {
88180
- return;
88181
- }
88182
- let latestVersion = null;
88183
- try {
88184
- latestVersion = execSync8("npm show adhdev version", {
88185
- encoding: "utf-8",
88186
- timeout: 5e3,
88187
- stdio: ["pipe", "pipe", "pipe"]
88188
- }).trim();
88189
- } catch {
88190
- return;
88191
- }
88281
+ const { execFileSync: execFileSync5 } = await import("child_process");
88282
+ const currentVersion = resolvePackageVersion();
88283
+ const latestVersion = readLatestPublishedCliVersion(execFileSync5);
88284
+ if (!latestVersion) return;
88192
88285
  if (!currentVersion || !latestVersion || currentVersion === latestVersion) return;
88193
88286
  console.log(source_default.yellow(` Update available: ${currentVersion} \u2192 ${latestVersion}`));
88194
88287
  const { doUpdate } = await (await Promise.resolve().then(() => (init_lib(), lib_exports))).default.prompt([{
@@ -88203,7 +88296,8 @@ async function checkForUpdate() {
88203
88296
  }
88204
88297
  const spinner = (await Promise.resolve().then(() => (init_ora(), ora_exports))).default("Updating adhdev CLI...").start();
88205
88298
  try {
88206
- execSync8("npm install -g adhdev@latest", {
88299
+ const installCommand = buildPinnedGlobalInstallCommand({ packageName: "adhdev", targetVersion: "latest" });
88300
+ execFileSync5(installCommand.command, installCommand.args, {
88207
88301
  encoding: "utf-8",
88208
88302
  timeout: 6e4,
88209
88303
  stdio: ["pipe", "pipe", "pipe"]
@@ -88441,18 +88535,8 @@ async function startDaemonFlow() {
88441
88535
  }
88442
88536
  }
88443
88537
  async function installCliOnly() {
88444
- const { execSync: execSyncLocal } = await import("child_process");
88445
- let currentVersion = null;
88446
- try {
88447
- const result = execSyncLocal("npm list -g adhdev --json 2>/dev/null || npm list -g adhdev --json 2>nul", {
88448
- encoding: "utf-8",
88449
- timeout: 5e3,
88450
- stdio: ["pipe", "pipe", "pipe"]
88451
- });
88452
- const parsed = JSON.parse(result);
88453
- currentVersion = parsed.dependencies?.adhdev?.version || null;
88454
- } catch {
88455
- }
88538
+ const { execFileSync: execFileSyncLocal } = await import("child_process");
88539
+ const currentVersion = readInstalledGlobalCliVersion(execFileSyncLocal);
88456
88540
  const isNpx = process.env.npm_execpath?.includes("npx") || process.argv[1]?.includes("npx") || process.argv[1]?.includes("_npx");
88457
88541
  console.log(source_default.bold("\n\u{1F527} ADHDev CLI\n"));
88458
88542
  console.log(source_default.gray(" The `adhdev` command lets you:"));
@@ -88463,15 +88547,7 @@ async function installCliOnly() {
88463
88547
  console.log();
88464
88548
  if (currentVersion) {
88465
88549
  console.log(source_default.green(` \u2713 Currently installed: v${currentVersion}`));
88466
- let latestVersion = null;
88467
- try {
88468
- latestVersion = execSyncLocal("npm show adhdev version", {
88469
- encoding: "utf-8",
88470
- timeout: 5e3,
88471
- stdio: ["pipe", "pipe", "pipe"]
88472
- }).trim();
88473
- } catch {
88474
- }
88550
+ const latestVersion = readLatestPublishedCliVersion(execFileSyncLocal);
88475
88551
  if (latestVersion && currentVersion === latestVersion) {
88476
88552
  console.log(source_default.gray(" (Already up to date)"));
88477
88553
  return;
@@ -88506,20 +88582,13 @@ async function installCliOnly() {
88506
88582
  }
88507
88583
  const installSpinner = ora2("Installing adhdev CLI...").start();
88508
88584
  try {
88509
- execSyncLocal("npm install -g adhdev@latest", {
88585
+ const installCommand = buildPinnedGlobalInstallCommand({ packageName: "adhdev", targetVersion: "latest" });
88586
+ execFileSyncLocal(installCommand.command, installCommand.args, {
88510
88587
  encoding: "utf-8",
88511
88588
  timeout: 6e4,
88512
88589
  stdio: ["pipe", "pipe", "pipe"]
88513
88590
  });
88514
- let newVersion = "latest";
88515
- try {
88516
- newVersion = execSyncLocal("adhdev --version 2>/dev/null || adhdev --version 2>nul", {
88517
- encoding: "utf-8",
88518
- timeout: 5e3,
88519
- stdio: ["pipe", "pipe", "pipe"]
88520
- }).trim();
88521
- } catch {
88522
- }
88591
+ const newVersion = readInstalledGlobalCliVersion(execFileSyncLocal) || "latest";
88523
88592
  installSpinner.succeed(`adhdev CLI ${currentVersion ? "updated" : "installed"} \u2713 (v${newVersion})`);
88524
88593
  console.log(source_default.gray(" Try: adhdev daemon"));
88525
88594
  console.log();
@@ -88543,6 +88612,7 @@ var init_wizard = __esm({
88543
88612
  init_ora();
88544
88613
  init_open();
88545
88614
  init_src();
88615
+ init_version();
88546
88616
  SERVER_URL = process.env.ADHDEV_SERVER_URL || "https://api.adhf.dev";
88547
88617
  LOGO = `
88548
88618
  ${source_default.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557")}
@@ -88901,14 +88971,25 @@ var init_supported = __esm({
88901
88971
  "source": "docs/site/data/provider-catalog.mjs"
88902
88972
  },
88903
88973
  "cursor-cli": {
88904
- "status": "unverified",
88905
- "testedOn": [],
88906
- "testedVersions": [],
88907
- "validatedFlows": [],
88908
- "lastValidated": null,
88909
- "notes": "",
88910
- "evidence": "",
88911
- "owner": "community",
88974
+ "status": "partial",
88975
+ "testedOn": [
88976
+ "macOS 26.4"
88977
+ ],
88978
+ "testedVersions": [
88979
+ "Cursor 3.0.16",
88980
+ "cursor-agent 2026.04.17-787b533"
88981
+ ],
88982
+ "validatedFlows": [
88983
+ "launch",
88984
+ "send_chat",
88985
+ "read_chat",
88986
+ "resume",
88987
+ "list_models"
88988
+ ],
88989
+ "lastValidated": "2026-04-23",
88990
+ "notes": "Provider root was updated to launch `cursor agent` instead of the obsolete standalone `agent` binary. Fresh launch via ADHDev plus headless send/read, explicit UUID resume, plan mode, and model listing were validated locally after Cursor login. Interactive in-session `/model` switching now has provider controls/scripts but still needs a direct ADHDev live-session validation before this provider can move beyond partial.",
88991
+ "evidence": "Manual local validation with `adhdev launch cursor-cli`, `cursor agent --print`, `cursor agent --resume`, `cursor agent --list-models`, and provider contract tests on 2026-04-23",
88992
+ "owner": "core",
88912
88993
  "source": "docs/site/data/provider-catalog.mjs"
88913
88994
  },
88914
88995
  "gemini-cli": {
@@ -89422,7 +89503,7 @@ var init_supported = __esm({
89422
89503
  "aider-cli": "unverified",
89423
89504
  "claude-cli": "partial",
89424
89505
  "codex-cli": "partial",
89425
- "cursor-cli": "unverified",
89506
+ "cursor-cli": "partial",
89426
89507
  "gemini-cli": "unverified",
89427
89508
  "github-copilot-cli": "unverified",
89428
89509
  "goose-cli": "unverified",
@@ -91512,9 +91593,9 @@ function registerDaemonCommands(program2, pkgVersion3) {
91512
91593
  // src/cli/doctor-commands.ts
91513
91594
  init_source();
91514
91595
  var import_child_process13 = require("child_process");
91515
- var fs25 = __toESM(require("fs"));
91516
- var os29 = __toESM(require("os"));
91517
- var path31 = __toESM(require("path"));
91596
+ var fs26 = __toESM(require("fs"));
91597
+ var os30 = __toESM(require("os"));
91598
+ var path33 = __toESM(require("path"));
91518
91599
  init_src();
91519
91600
  init_session_host();
91520
91601
 
@@ -91588,6 +91669,27 @@ function evaluateNodeInstallDrift(probe) {
91588
91669
  detail: `node install drift: current runtime ${currentNodeVersion} vs resolved binary ${commandNodeVersion}`
91589
91670
  };
91590
91671
  }
91672
+ function evaluateServiceDefinitionDrift(probe) {
91673
+ const serviceLabel = probe.serviceKind === "launchd" ? "launchd" : "startup script";
91674
+ const installed = String(probe.installedDefinition || "").trim();
91675
+ const expected = String(probe.expectedDefinition || "").trim();
91676
+ if (!installed || !expected) {
91677
+ return {
91678
+ ok: true,
91679
+ detail: `${serviceLabel} definition unavailable for comparison (${probe.servicePath})`
91680
+ };
91681
+ }
91682
+ if (installed === expected) {
91683
+ return {
91684
+ ok: true,
91685
+ detail: `${serviceLabel} service definition matches current install (${probe.servicePath})`
91686
+ };
91687
+ }
91688
+ return {
91689
+ ok: false,
91690
+ detail: `outdated service definition (${probe.servicePath})`
91691
+ };
91692
+ }
91591
91693
  function buildDoctorAdvice(input) {
91592
91694
  const advice = [];
91593
91695
  if (input.adhdevCheck && !input.adhdevCheck.ok) {
@@ -91602,16 +91704,385 @@ function buildDoctorAdvice(input) {
91602
91704
  if (input.nodeDriftCheck && !input.nodeDriftCheck.ok) {
91603
91705
  advice.push("Align the Node version that launches adhdev with the Node install root that owns the PATH binary (for nvm users: run `nvm use <version>` and reopen the shell).");
91604
91706
  }
91707
+ if (input.serviceCheck && !input.serviceCheck.ok) {
91708
+ advice.push(`Reinstall the background service${input.servicePath ? ` at ${input.servicePath}` : ""} so it points at the current adhdev install. Run: adhdev service install`);
91709
+ }
91605
91710
  return advice;
91606
91711
  }
91607
91712
 
91713
+ // src/cli/service-commands.ts
91714
+ var import_node_fs6 = __toESM(require("fs"));
91715
+ var import_node_path4 = __toESM(require("path"));
91716
+ var import_node_os6 = __toESM(require("os"));
91717
+ var import_node_child_process6 = require("child_process");
91718
+ init_source();
91719
+ init_src();
91720
+ var DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS3 = 1500;
91721
+ var LAUNCHD_LABEL = "dev.adhf.daemon";
91722
+ var ADHDEV_DIR = import_node_path4.default.join(import_node_os6.default.homedir(), ".adhdev");
91723
+ var LOG_OUT = import_node_path4.default.join(ADHDEV_DIR, "daemon-launchd.out");
91724
+ var LOG_ERR = import_node_path4.default.join(ADHDEV_DIR, "daemon-launchd.err");
91725
+ var MAX_LOG_SIZE2 = 10 * 1024 * 1024;
91726
+ function getDarwinPlistPath() {
91727
+ return import_node_path4.default.join(import_node_os6.default.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
91728
+ }
91729
+ function getWindowsStartupDir() {
91730
+ const appData = process.env.APPDATA || import_node_path4.default.join(import_node_os6.default.homedir(), "AppData", "Roaming");
91731
+ return import_node_path4.default.join(appData, "Microsoft", "Windows", "Start Menu", "Programs", "Startup");
91732
+ }
91733
+ function getWindowsVbsPath() {
91734
+ return import_node_path4.default.join(getWindowsStartupDir(), "adhdev-daemon.vbs");
91735
+ }
91736
+ function resolveCliPath() {
91737
+ return import_node_fs6.default.realpathSync(process.argv[1]);
91738
+ }
91739
+ function ensureDir(dir) {
91740
+ if (!import_node_fs6.default.existsSync(dir)) import_node_fs6.default.mkdirSync(dir, { recursive: true });
91741
+ }
91742
+ async function fetchHealth() {
91743
+ const controller = new AbortController();
91744
+ const timer = setTimeout(() => controller.abort(), DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS3);
91745
+ try {
91746
+ const res = await fetch(`http://127.0.0.1:${DEFAULT_DAEMON_PORT}/health`, { signal: controller.signal });
91747
+ if (!res.ok) return null;
91748
+ return await res.json();
91749
+ } catch {
91750
+ return null;
91751
+ } finally {
91752
+ clearTimeout(timer);
91753
+ }
91754
+ }
91755
+ function getProcessInfo(pid) {
91756
+ try {
91757
+ if (process.platform === "win32") {
91758
+ const out = (0, import_node_child_process6.execSync)(`tasklist /FI "PID eq ${pid}" /FO CSV /NH`, { encoding: "utf-8" });
91759
+ const match = out.match(/"(\d[\d,]+)\sK"/);
91760
+ const memKB = match ? parseInt(match[1].replace(/,/g, ""), 10) : 0;
91761
+ return { uptime: "-", memMB: Math.round(memKB / 1024) };
91762
+ } else {
91763
+ const out = (0, import_node_child_process6.execSync)(`ps -o etime=,rss= -p ${pid}`, { encoding: "utf-8" }).trim();
91764
+ const parts = out.split(/\s+/);
91765
+ const etime = parts[0] || "-";
91766
+ const rssKB = parseInt(parts[1] || "0", 10);
91767
+ return { uptime: formatElapsed(etime), memMB: Math.round(rssKB / 1024) };
91768
+ }
91769
+ } catch {
91770
+ return null;
91771
+ }
91772
+ }
91773
+ function formatElapsed(etime) {
91774
+ const parts = etime.replace("-", ":").split(":").map(Number);
91775
+ if (parts.length === 4) return `${parts[0]}d ${parts[1]}h ${parts[2]}m`;
91776
+ if (parts.length === 3) return `${parts[0]}h ${parts[1]}m`;
91777
+ if (parts.length === 2) return `${parts[0]}m ${parts[1]}s`;
91778
+ return etime;
91779
+ }
91780
+ function rotateLogIfNeeded(logPath) {
91781
+ try {
91782
+ if (!import_node_fs6.default.existsSync(logPath)) return;
91783
+ const stat4 = import_node_fs6.default.statSync(logPath);
91784
+ if (stat4.size > MAX_LOG_SIZE2) {
91785
+ const rotated = logPath + ".old";
91786
+ if (import_node_fs6.default.existsSync(rotated)) import_node_fs6.default.unlinkSync(rotated);
91787
+ import_node_fs6.default.renameSync(logPath, rotated);
91788
+ import_node_fs6.default.writeFileSync(logPath, `[log rotated at ${(/* @__PURE__ */ new Date()).toISOString()}]
91789
+ `, "utf-8");
91790
+ }
91791
+ } catch {
91792
+ }
91793
+ }
91794
+ function rotateLogs() {
91795
+ rotateLogIfNeeded(LOG_OUT);
91796
+ rotateLogIfNeeded(LOG_ERR);
91797
+ }
91798
+ function buildPlist(nodeExe, cliExe) {
91799
+ const brewPrefix = import_node_fs6.default.existsSync("/opt/homebrew/bin") ? "/opt/homebrew/bin" : "/usr/local/bin";
91800
+ const nodeDir = import_node_path4.default.dirname(nodeExe);
91801
+ const pathEntries = /* @__PURE__ */ new Set([nodeDir, brewPrefix, "/usr/local/bin", "/usr/bin", "/bin", "/usr/sbin", "/sbin"]);
91802
+ const pathValue = Array.from(pathEntries).join(":");
91803
+ return `<?xml version="1.0" encoding="UTF-8"?>
91804
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
91805
+ <plist version="1.0">
91806
+ <dict>
91807
+ <key>Label</key>
91808
+ <string>${LAUNCHD_LABEL}</string>
91809
+ <key>ProgramArguments</key>
91810
+ <array>
91811
+ <string>${nodeExe}</string>
91812
+ <string>${cliExe}</string>
91813
+ <string>daemon</string>
91814
+ </array>
91815
+ <key>RunAtLoad</key>
91816
+ <true/>
91817
+ <key>KeepAlive</key>
91818
+ <dict>
91819
+ <key>SuccessfulExit</key>
91820
+ <false/>
91821
+ </dict>
91822
+ <key>ThrottleInterval</key>
91823
+ <integer>30</integer>
91824
+ <key>StandardOutPath</key>
91825
+ <string>${LOG_OUT}</string>
91826
+ <key>StandardErrorPath</key>
91827
+ <string>${LOG_ERR}</string>
91828
+ <key>EnvironmentVariables</key>
91829
+ <dict>
91830
+ <key>PATH</key>
91831
+ <string>${pathValue}</string>
91832
+ </dict>
91833
+ </dict>
91834
+ </plist>`;
91835
+ }
91836
+ function installDarwin(nodeExe, cliExe) {
91837
+ const plistPath = getDarwinPlistPath();
91838
+ ensureDir(ADHDEV_DIR);
91839
+ ensureDir(import_node_path4.default.dirname(plistPath));
91840
+ import_node_fs6.default.writeFileSync(plistPath, buildPlist(nodeExe, cliExe), "utf-8");
91841
+ console.log(source_default.gray(` Plist: ${plistPath}`));
91842
+ try {
91843
+ (0, import_node_child_process6.execSync)(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
91844
+ } catch {
91845
+ }
91846
+ try {
91847
+ (0, import_node_child_process6.execSync)(`launchctl load -w "${plistPath}"`, { stdio: "ignore" });
91848
+ console.log(source_default.green("\n \u2713 Registered as LaunchAgent \u2014 daemon will start on login."));
91849
+ console.log(source_default.gray(` Logs: ~/.adhdev/daemon-launchd.{out,err}`));
91850
+ } catch (e) {
91851
+ console.log(source_default.red(`
91852
+ \u2717 launchctl load failed: ${e.message}`));
91853
+ }
91854
+ }
91855
+ function uninstallDarwin() {
91856
+ const plistPath = getDarwinPlistPath();
91857
+ if (!import_node_fs6.default.existsSync(plistPath)) {
91858
+ console.log(source_default.yellow("\n \u26A0 Service is not installed."));
91859
+ return;
91860
+ }
91861
+ try {
91862
+ (0, import_node_child_process6.execSync)(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
91863
+ } catch {
91864
+ }
91865
+ import_node_fs6.default.unlinkSync(plistPath);
91866
+ console.log(source_default.green("\n \u2713 Removed LaunchAgent. Daemon will no longer auto-start."));
91867
+ }
91868
+ function isInstalledDarwin() {
91869
+ return import_node_fs6.default.existsSync(getDarwinPlistPath());
91870
+ }
91871
+ function buildVbs(nodeExe, cliExe) {
91872
+ const logFile = import_node_path4.default.join(ADHDEV_DIR, "daemon-service.log").replace(/\\/g, "\\\\");
91873
+ const escapedNodeExe = nodeExe.replace(/\\/g, "\\\\");
91874
+ const escapedCliExe = cliExe.replace(/\\/g, "\\\\");
91875
+ return `' ADHDev Daemon Auto-Start (generated by adhdev service install)
91876
+ Set WshShell = CreateObject("WScript.Shell")
91877
+ WshShell.Run "cmd.exe /c """"${escapedNodeExe}"""" """"${escapedCliExe}"""" daemon >> """"${logFile}"""" 2>&1", 0, False
91878
+ `;
91879
+ }
91880
+ function installWindows(nodeExe, cliExe) {
91881
+ const vbsPath = getWindowsVbsPath();
91882
+ ensureDir(ADHDEV_DIR);
91883
+ ensureDir(import_node_path4.default.dirname(vbsPath));
91884
+ import_node_fs6.default.writeFileSync(vbsPath, buildVbs(nodeExe, cliExe), "utf-8");
91885
+ console.log(source_default.gray(` Startup script: ${vbsPath}`));
91886
+ console.log(source_default.green("\n \u2713 Registered in Startup folder \u2014 daemon will start on login (hidden)."));
91887
+ console.log(source_default.gray(` Logs: ${import_node_path4.default.join(ADHDEV_DIR, "daemon-service.log")}`));
91888
+ console.log(source_default.gray(" To start now without rebooting, run: adhdev daemon"));
91889
+ }
91890
+ function uninstallWindows() {
91891
+ const vbsPath = getWindowsVbsPath();
91892
+ if (!import_node_fs6.default.existsSync(vbsPath)) {
91893
+ console.log(source_default.yellow("\n \u26A0 Service is not installed."));
91894
+ return;
91895
+ }
91896
+ import_node_fs6.default.unlinkSync(vbsPath);
91897
+ console.log(source_default.green("\n \u2713 Removed Startup script. Daemon will no longer auto-start."));
91898
+ console.log(source_default.gray(" Note: a currently running daemon is not affected. Stop with: adhdev daemon:stop"));
91899
+ }
91900
+ function isInstalledWindows() {
91901
+ return import_node_fs6.default.existsSync(getWindowsVbsPath());
91902
+ }
91903
+ function registerServiceCommands(program2) {
91904
+ const svc = program2.command("service").description("\u{1F50C} Manage ADHDev as an OS background auto-start service");
91905
+ svc.command("install").description("Register ADHDev daemon to start automatically on login").action(async () => {
91906
+ console.log(source_default.bold("\n \u{1F680} Installing ADHDev Background Service"));
91907
+ const platform13 = import_node_os6.default.platform();
91908
+ const nodeExe = process.execPath;
91909
+ const cliExe = resolveCliPath();
91910
+ console.log(source_default.gray(` Node: ${nodeExe}`));
91911
+ console.log(source_default.gray(` CLI: ${cliExe}`));
91912
+ console.log(source_default.gray(` Platform: ${platform13}`));
91913
+ if (platform13 === "darwin") {
91914
+ installDarwin(nodeExe, cliExe);
91915
+ } else if (platform13 === "win32") {
91916
+ installWindows(nodeExe, cliExe);
91917
+ } else {
91918
+ console.log(source_default.yellow("\n \u26A0 Auto-start service install is not supported on this platform."));
91919
+ console.log(source_default.gray(" On Linux, create a systemd user unit manually:"));
91920
+ console.log(source_default.gray(" ~/.config/systemd/user/adhdev-daemon.service"));
91921
+ }
91922
+ console.log();
91923
+ });
91924
+ svc.command("uninstall").description("Remove the OS background service").action(async () => {
91925
+ console.log(source_default.bold("\n \u{1F5D1}\uFE0F Removing ADHDev Background Service"));
91926
+ const platform13 = import_node_os6.default.platform();
91927
+ if (platform13 === "darwin") {
91928
+ uninstallDarwin();
91929
+ } else if (platform13 === "win32") {
91930
+ uninstallWindows();
91931
+ } else {
91932
+ console.log(source_default.yellow("\n \u26A0 Not supported on this platform."));
91933
+ }
91934
+ console.log();
91935
+ });
91936
+ svc.command("status").description("Show service installation state and live daemon health").action(async () => {
91937
+ const platform13 = import_node_os6.default.platform();
91938
+ const installed = platform13 === "darwin" ? isInstalledDarwin() : platform13 === "win32" ? isInstalledWindows() : false;
91939
+ if (installed) {
91940
+ console.log(source_default.green("\n \u2713 Service is installed."));
91941
+ if (platform13 === "darwin") console.log(source_default.gray(` Plist: ${getDarwinPlistPath()}`));
91942
+ else console.log(source_default.gray(` Script: ${getWindowsVbsPath()}`));
91943
+ } else {
91944
+ console.log(source_default.gray("\n \u2717 Service is not installed. Run: adhdev service install"));
91945
+ }
91946
+ const health = await fetchHealth();
91947
+ if (health?.ok && health.pid) {
91948
+ const info = getProcessInfo(health.pid);
91949
+ if (info) {
91950
+ console.log(source_default.green(` \u2713 Daemon running \u2014 PID ${health.pid}, uptime ${info.uptime}, ${info.memMB} MB`));
91951
+ } else {
91952
+ console.log(source_default.green(` \u2713 Daemon running \u2014 PID ${health.pid}`));
91953
+ }
91954
+ } else {
91955
+ console.log(source_default.yellow(" \u2717 Daemon is not running."));
91956
+ }
91957
+ const outSize = import_node_fs6.default.existsSync(LOG_OUT) ? import_node_fs6.default.statSync(LOG_OUT).size : 0;
91958
+ const errSize = import_node_fs6.default.existsSync(LOG_ERR) ? import_node_fs6.default.statSync(LOG_ERR).size : 0;
91959
+ if (outSize > 0 || errSize > 0) {
91960
+ console.log(source_default.gray(` Logs: stdout ${formatBytes(outSize)}, stderr ${formatBytes(errSize)}`));
91961
+ }
91962
+ console.log();
91963
+ });
91964
+ svc.command("logs").description("View daemon service logs").option("--err", "Show stderr log instead of stdout").option("--clear", "Truncate all log files").option("-n, --lines <count>", "Number of lines to show", "30").action(async (options) => {
91965
+ if (options.clear) {
91966
+ for (const f of [LOG_OUT, LOG_ERR]) {
91967
+ if (import_node_fs6.default.existsSync(f)) import_node_fs6.default.writeFileSync(f, "", "utf-8");
91968
+ }
91969
+ console.log(source_default.green("\n \u2713 Logs cleared.\n"));
91970
+ return;
91971
+ }
91972
+ const logFile = options.err ? LOG_ERR : LOG_OUT;
91973
+ if (!import_node_fs6.default.existsSync(logFile)) {
91974
+ console.log(source_default.gray(`
91975
+ No log file found: ${logFile}
91976
+ `));
91977
+ return;
91978
+ }
91979
+ const lines = parseInt(options.lines, 10) || 30;
91980
+ console.log(source_default.gray(`
91981
+ \u2500\u2500 ${options.err ? "stderr" : "stdout"}: ${logFile} (last ${lines} lines) \u2500\u2500
91982
+ `));
91983
+ const content = import_node_fs6.default.readFileSync(logFile, "utf-8");
91984
+ const allLines = content.split("\n");
91985
+ const lastLines = allLines.slice(-lines).join("\n");
91986
+ if (lastLines.trim()) console.log(lastLines);
91987
+ console.log(source_default.gray("\n (watching for new output, Ctrl+C to stop)\n"));
91988
+ let offset = Buffer.byteLength(content, "utf-8");
91989
+ const watcher = import_node_fs6.default.watchFile(logFile, { interval: 500 }, () => {
91990
+ try {
91991
+ const stat4 = import_node_fs6.default.statSync(logFile);
91992
+ if (stat4.size > offset) {
91993
+ const fd = import_node_fs6.default.openSync(logFile, "r");
91994
+ const buf = Buffer.alloc(stat4.size - offset);
91995
+ import_node_fs6.default.readSync(fd, buf, 0, buf.length, offset);
91996
+ import_node_fs6.default.closeSync(fd);
91997
+ process.stdout.write(buf.toString("utf-8"));
91998
+ offset = stat4.size;
91999
+ } else if (stat4.size < offset) {
92000
+ offset = 0;
92001
+ }
92002
+ } catch {
92003
+ }
92004
+ });
92005
+ const cleanup = () => {
92006
+ import_node_fs6.default.unwatchFile(logFile);
92007
+ process.exit(0);
92008
+ };
92009
+ process.on("SIGINT", cleanup);
92010
+ process.on("SIGTERM", cleanup);
92011
+ });
92012
+ svc.command("restart").description("Restart the daemon process (service will auto-relaunch)").action(async () => {
92013
+ const platform13 = import_node_os6.default.platform();
92014
+ const installed = platform13 === "darwin" ? isInstalledDarwin() : platform13 === "win32" ? isInstalledWindows() : false;
92015
+ if (!installed) {
92016
+ console.log(source_default.yellow("\n \u26A0 Service is not installed. Use `adhdev daemon:restart` for manual restart."));
92017
+ console.log(source_default.gray(" Or install the service first: adhdev service install\n"));
92018
+ return;
92019
+ }
92020
+ rotateLogs();
92021
+ const health = await fetchHealth();
92022
+ if (!health?.pid) {
92023
+ console.log(source_default.yellow("\n \u26A0 Daemon is not currently running."));
92024
+ if (platform13 === "darwin") {
92025
+ console.log(source_default.gray(" Starting via launchctl..."));
92026
+ try {
92027
+ (0, import_node_child_process6.execSync)(`launchctl start ${LAUNCHD_LABEL}`, { stdio: "ignore" });
92028
+ console.log(source_default.green(" \u2713 Started.\n"));
92029
+ } catch {
92030
+ console.log(source_default.red(" \u2717 Failed to start. Check: adhdev service logs --err\n"));
92031
+ }
92032
+ } else {
92033
+ console.log(source_default.gray(" Start with: adhdev daemon\n"));
92034
+ }
92035
+ return;
92036
+ }
92037
+ console.log(source_default.cyan(`
92038
+ Stopping daemon (PID ${health.pid})...`));
92039
+ try {
92040
+ process.kill(health.pid, "SIGTERM");
92041
+ } catch {
92042
+ console.log(source_default.yellow(" Could not send SIGTERM. Process may have already exited."));
92043
+ }
92044
+ await new Promise((r) => setTimeout(r, 2e3));
92045
+ if (platform13 === "win32") {
92046
+ const vbsPath = getWindowsVbsPath();
92047
+ if (import_node_fs6.default.existsSync(vbsPath)) {
92048
+ try {
92049
+ (0, import_node_child_process6.execSync)(`wscript.exe "${vbsPath}"`, { stdio: "ignore", windowsHide: true });
92050
+ } catch {
92051
+ }
92052
+ }
92053
+ }
92054
+ let restarted = false;
92055
+ for (let i = 0; i < 8; i++) {
92056
+ await new Promise((r) => setTimeout(r, 1e3));
92057
+ const newHealth = await fetchHealth();
92058
+ if (newHealth?.ok && newHealth.pid !== health.pid) {
92059
+ restarted = true;
92060
+ const info = getProcessInfo(newHealth.pid);
92061
+ console.log(source_default.green(` \u2713 Daemon restarted \u2014 new PID ${newHealth.pid}${info ? `, ${info.memMB} MB` : ""}`));
92062
+ break;
92063
+ }
92064
+ }
92065
+ if (!restarted) {
92066
+ console.log(source_default.yellow(" \u26A0 Daemon did not restart within 10s."));
92067
+ console.log(source_default.gray(" Check: adhdev service logs --err"));
92068
+ }
92069
+ console.log();
92070
+ });
92071
+ }
92072
+ function formatBytes(bytes) {
92073
+ if (bytes === 0) return "0 B";
92074
+ if (bytes < 1024) return `${bytes} B`;
92075
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
92076
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
92077
+ }
92078
+
91608
92079
  // src/cli/doctor-commands.ts
91609
92080
  function resolvePackageRoot() {
91610
- return path31.resolve(__dirname, "..", "..");
92081
+ return path33.resolve(__dirname, "..", "..");
91611
92082
  }
91612
92083
  function isLinkedInstall(packageRoot) {
91613
- const normalized = path31.normalize(packageRoot);
91614
- return !normalized.includes(`${path31.sep}node_modules${path31.sep}adhdev`);
92084
+ const normalized = path33.normalize(packageRoot);
92085
+ return !normalized.includes(`${path33.sep}node_modules${path33.sep}adhdev`);
91615
92086
  }
91616
92087
  function formatCheck(check2) {
91617
92088
  const icon = check2.ok ? source_default.green("\u2713") : source_default.red("\u2717");
@@ -91695,11 +92166,11 @@ function probeSharpRuntime(packageRoot, nativeSharpPackage) {
91695
92166
  }
91696
92167
  }
91697
92168
  function probeConfigAccess() {
91698
- const configDir = path31.join(os29.homedir(), ".adhdev");
91699
- const configPath = path31.join(configDir, "config.json");
92169
+ const configDir = path33.join(os30.homedir(), ".adhdev");
92170
+ const configPath = path33.join(configDir, "config.json");
91700
92171
  const checks = [];
91701
92172
  try {
91702
- fs25.mkdirSync(configDir, { recursive: true });
92173
+ fs26.mkdirSync(configDir, { recursive: true });
91703
92174
  checks.push({
91704
92175
  label: "Config directory",
91705
92176
  ok: true,
@@ -91714,8 +92185,8 @@ function probeConfigAccess() {
91714
92185
  }];
91715
92186
  }
91716
92187
  try {
91717
- if (fs25.existsSync(configPath)) {
91718
- fs25.readFileSync(configPath, "utf-8");
92188
+ if (fs26.existsSync(configPath)) {
92189
+ fs26.readFileSync(configPath, "utf-8");
91719
92190
  checks.push({
91720
92191
  label: "Config file",
91721
92192
  ok: true,
@@ -91736,10 +92207,10 @@ function probeConfigAccess() {
91736
92207
  fatal: true
91737
92208
  });
91738
92209
  }
91739
- const probePath = path31.join(configDir, `.doctor-write-${process.pid}-${Date.now()}.tmp`);
92210
+ const probePath = path33.join(configDir, `.doctor-write-${process.pid}-${Date.now()}.tmp`);
91740
92211
  try {
91741
- fs25.writeFileSync(probePath, "ok", "utf-8");
91742
- fs25.rmSync(probePath, { force: true });
92212
+ fs26.writeFileSync(probePath, "ok", "utf-8");
92213
+ fs26.rmSync(probePath, { force: true });
91743
92214
  checks.push({
91744
92215
  label: "Config write",
91745
92216
  ok: true,
@@ -91747,7 +92218,7 @@ function probeConfigAccess() {
91747
92218
  });
91748
92219
  } catch (error48) {
91749
92220
  try {
91750
- fs25.rmSync(probePath, { force: true });
92221
+ fs26.rmSync(probePath, { force: true });
91751
92222
  } catch {
91752
92223
  }
91753
92224
  checks.push({
@@ -91803,9 +92274,9 @@ function probeCliBinary(commandPath, currentVersion) {
91803
92274
  return probe;
91804
92275
  }
91805
92276
  function readLogHints(logPath) {
91806
- if (!fs25.existsSync(logPath)) return [];
92277
+ if (!fs26.existsSync(logPath)) return [];
91807
92278
  try {
91808
- const content = fs25.readFileSync(logPath, "utf-8");
92279
+ const content = fs26.readFileSync(logPath, "utf-8");
91809
92280
  const lines = content.split(/\r?\n/);
91810
92281
  const recent = lines.slice(-400);
91811
92282
  const hits = recent.filter(
@@ -91819,11 +92290,11 @@ function readLogHints(logPath) {
91819
92290
  function buildBrowseProbeChecks() {
91820
92291
  const probes = process.platform === "win32" ? [
91821
92292
  process.env.SystemDrive ? `${process.env.SystemDrive.replace(/[\\/]+$/, "")}\\` : "C:\\",
91822
- os29.homedir()
91823
- ] : ["/", os29.homedir()];
92293
+ os30.homedir()
92294
+ ] : ["/", os30.homedir()];
91824
92295
  return probes.map((probePath, index) => {
91825
92296
  try {
91826
- const entries = fs25.readdirSync(probePath, { withFileTypes: true });
92297
+ const entries = fs26.readdirSync(probePath, { withFileTypes: true });
91827
92298
  const directoryCount = entries.filter((entry) => entry.isDirectory()).length;
91828
92299
  return {
91829
92300
  label: index === 0 ? "Folder browse root" : "Folder browse home",
@@ -91843,7 +92314,7 @@ function buildBrowseProbeChecks() {
91843
92314
  function registerDoctorCommands(program2, pkgVersion3) {
91844
92315
  program2.command("doctor").description("Diagnose install, native dependencies, CLI resolution, and folder browse access").action(async () => {
91845
92316
  const packageRoot = resolvePackageRoot();
91846
- const cliPath = fs25.realpathSync(process.argv[1]);
92317
+ const cliPath = fs26.realpathSync(process.argv[1]);
91847
92318
  const linked = isLinkedInstall(packageRoot);
91848
92319
  const logPath = getCurrentDaemonLogPath();
91849
92320
  const claudePaths = findCommandPaths("claude");
@@ -91891,6 +92362,8 @@ function registerDoctorCommands(program2, pkgVersion3) {
91891
92362
  let runtimeSurfaceCheck = null;
91892
92363
  let nodeDriftCheck = null;
91893
92364
  let adhmuxHelpCheck = null;
92365
+ let serviceDefinitionCheck = null;
92366
+ let serviceDefinitionPath;
91894
92367
  if (adhdevPaths.length > 0) {
91895
92368
  const runtimeSurfaceProbe = probeCliBinary(adhdevPaths[0], pkgVersion3);
91896
92369
  runtimeSurfaceCheck = evaluateCliDriftCheck(runtimeSurfaceProbe);
@@ -91922,6 +92395,22 @@ function registerDoctorCommands(program2, pkgVersion3) {
91922
92395
  detail: adhmuxHelpCheck.detail
91923
92396
  });
91924
92397
  }
92398
+ if (process.platform === "darwin") {
92399
+ serviceDefinitionPath = path33.join(os30.homedir(), "Library", "LaunchAgents", "dev.adhf.daemon.plist");
92400
+ if (fs26.existsSync(serviceDefinitionPath)) {
92401
+ serviceDefinitionCheck = evaluateServiceDefinitionDrift({
92402
+ serviceKind: "launchd",
92403
+ servicePath: serviceDefinitionPath,
92404
+ installedDefinition: fs26.readFileSync(serviceDefinitionPath, "utf8"),
92405
+ expectedDefinition: buildPlist(process.execPath, cliPath)
92406
+ });
92407
+ checks.push({
92408
+ label: "Service definition",
92409
+ ok: serviceDefinitionCheck.ok,
92410
+ detail: serviceDefinitionCheck.detail
92411
+ });
92412
+ }
92413
+ }
91925
92414
  try {
91926
92415
  const sessionHostPid = getSessionHostPid();
91927
92416
  const probe = await probeSessionHostStatus();
@@ -91976,14 +92465,16 @@ function registerDoctorCommands(program2, pkgVersion3) {
91976
92465
  adhmuxPath: adhmuxPaths[0],
91977
92466
  adhmuxCheck: adhmuxHelpCheck || void 0,
91978
92467
  nodeDriftCheck: nodeDriftCheck || void 0,
92468
+ servicePath: serviceDefinitionPath,
92469
+ serviceCheck: serviceDefinitionCheck || void 0,
91979
92470
  sourceCliExample: "node --import tsx packages/daemon-cloud/src/cli/index.ts doctor"
91980
92471
  });
91981
- const sessionHostLogPath = path31.join(os29.homedir(), ".adhdev", "logs", "session-host.log");
92472
+ const sessionHostLogPath = path33.join(os30.homedir(), ".adhdev", "logs", "session-host.log");
91982
92473
  console.log(source_default.bold("\n\u{1FA7A} ADHDev Doctor\n"));
91983
92474
  console.log(source_default.gray(` Version: ${pkgVersion3}`));
91984
92475
  console.log(source_default.gray(` Platform: ${process.platform} ${process.arch}`));
91985
92476
  console.log(source_default.gray(` Node: ${process.version}`));
91986
- console.log(source_default.gray(` Home: ${os29.homedir()}`));
92477
+ console.log(source_default.gray(` Home: ${os30.homedir()}`));
91987
92478
  console.log(source_default.gray(` Log file: ${logPath}`));
91988
92479
  console.log(source_default.gray(` SH log: ${sessionHostLogPath}`));
91989
92480
  console.log();
@@ -92020,7 +92511,7 @@ function registerDoctorCommands(program2, pkgVersion3) {
92020
92511
 
92021
92512
  // src/cli/provider-commands.ts
92022
92513
  init_source();
92023
- var path33 = __toESM(require("path"));
92514
+ var path34 = __toESM(require("path"));
92024
92515
  init_cdp_utils();
92025
92516
  var DEV_SERVER_PORT3 = 19280;
92026
92517
  var IDE_AUTO_FIX_FUNCTIONS = [
@@ -92184,7 +92675,7 @@ function getProviderSourceCandidatePaths(options) {
92184
92675
  const results = [];
92185
92676
  for (const root of roots) {
92186
92677
  for (const name of relativeNames) {
92187
- results.push(path33.join(root, options.category, options.type, name));
92678
+ results.push(path34.join(root, options.category, options.type, name));
92188
92679
  }
92189
92680
  }
92190
92681
  return results;
@@ -93153,372 +93644,6 @@ function registerCdpCommands(program2) {
93153
93644
  });
93154
93645
  }
93155
93646
 
93156
- // src/cli/service-commands.ts
93157
- var import_node_fs6 = __toESM(require("fs"));
93158
- var import_node_path4 = __toESM(require("path"));
93159
- var import_node_os6 = __toESM(require("os"));
93160
- var import_node_child_process6 = require("child_process");
93161
- init_source();
93162
- init_src();
93163
- var DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS3 = 1500;
93164
- var LAUNCHD_LABEL = "dev.adhf.daemon";
93165
- var ADHDEV_DIR = import_node_path4.default.join(import_node_os6.default.homedir(), ".adhdev");
93166
- var LOG_OUT = import_node_path4.default.join(ADHDEV_DIR, "daemon-launchd.out");
93167
- var LOG_ERR = import_node_path4.default.join(ADHDEV_DIR, "daemon-launchd.err");
93168
- var MAX_LOG_SIZE2 = 10 * 1024 * 1024;
93169
- function getDarwinPlistPath() {
93170
- return import_node_path4.default.join(import_node_os6.default.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
93171
- }
93172
- function getWindowsStartupDir() {
93173
- const appData = process.env.APPDATA || import_node_path4.default.join(import_node_os6.default.homedir(), "AppData", "Roaming");
93174
- return import_node_path4.default.join(appData, "Microsoft", "Windows", "Start Menu", "Programs", "Startup");
93175
- }
93176
- function getWindowsVbsPath() {
93177
- return import_node_path4.default.join(getWindowsStartupDir(), "adhdev-daemon.vbs");
93178
- }
93179
- function resolveCliPath() {
93180
- return import_node_fs6.default.realpathSync(process.argv[1]);
93181
- }
93182
- function ensureDir(dir) {
93183
- if (!import_node_fs6.default.existsSync(dir)) import_node_fs6.default.mkdirSync(dir, { recursive: true });
93184
- }
93185
- async function fetchHealth() {
93186
- const controller = new AbortController();
93187
- const timer = setTimeout(() => controller.abort(), DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS3);
93188
- try {
93189
- const res = await fetch(`http://127.0.0.1:${DEFAULT_DAEMON_PORT}/health`, { signal: controller.signal });
93190
- if (!res.ok) return null;
93191
- return await res.json();
93192
- } catch {
93193
- return null;
93194
- } finally {
93195
- clearTimeout(timer);
93196
- }
93197
- }
93198
- function getProcessInfo(pid) {
93199
- try {
93200
- if (process.platform === "win32") {
93201
- const out = (0, import_node_child_process6.execSync)(`tasklist /FI "PID eq ${pid}" /FO CSV /NH`, { encoding: "utf-8" });
93202
- const match = out.match(/"(\d[\d,]+)\sK"/);
93203
- const memKB = match ? parseInt(match[1].replace(/,/g, ""), 10) : 0;
93204
- return { uptime: "-", memMB: Math.round(memKB / 1024) };
93205
- } else {
93206
- const out = (0, import_node_child_process6.execSync)(`ps -o etime=,rss= -p ${pid}`, { encoding: "utf-8" }).trim();
93207
- const parts = out.split(/\s+/);
93208
- const etime = parts[0] || "-";
93209
- const rssKB = parseInt(parts[1] || "0", 10);
93210
- return { uptime: formatElapsed(etime), memMB: Math.round(rssKB / 1024) };
93211
- }
93212
- } catch {
93213
- return null;
93214
- }
93215
- }
93216
- function formatElapsed(etime) {
93217
- const parts = etime.replace("-", ":").split(":").map(Number);
93218
- if (parts.length === 4) return `${parts[0]}d ${parts[1]}h ${parts[2]}m`;
93219
- if (parts.length === 3) return `${parts[0]}h ${parts[1]}m`;
93220
- if (parts.length === 2) return `${parts[0]}m ${parts[1]}s`;
93221
- return etime;
93222
- }
93223
- function rotateLogIfNeeded(logPath) {
93224
- try {
93225
- if (!import_node_fs6.default.existsSync(logPath)) return;
93226
- const stat4 = import_node_fs6.default.statSync(logPath);
93227
- if (stat4.size > MAX_LOG_SIZE2) {
93228
- const rotated = logPath + ".old";
93229
- if (import_node_fs6.default.existsSync(rotated)) import_node_fs6.default.unlinkSync(rotated);
93230
- import_node_fs6.default.renameSync(logPath, rotated);
93231
- import_node_fs6.default.writeFileSync(logPath, `[log rotated at ${(/* @__PURE__ */ new Date()).toISOString()}]
93232
- `, "utf-8");
93233
- }
93234
- } catch {
93235
- }
93236
- }
93237
- function rotateLogs() {
93238
- rotateLogIfNeeded(LOG_OUT);
93239
- rotateLogIfNeeded(LOG_ERR);
93240
- }
93241
- function buildPlist(nodeExe, cliExe) {
93242
- const brewPrefix = import_node_fs6.default.existsSync("/opt/homebrew/bin") ? "/opt/homebrew/bin" : "/usr/local/bin";
93243
- const nodeDir = import_node_path4.default.dirname(nodeExe);
93244
- const pathEntries = /* @__PURE__ */ new Set([nodeDir, brewPrefix, "/usr/local/bin", "/usr/bin", "/bin", "/usr/sbin", "/sbin"]);
93245
- const pathValue = [...pathEntries].join(":");
93246
- return `<?xml version="1.0" encoding="UTF-8"?>
93247
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
93248
- <plist version="1.0">
93249
- <dict>
93250
- <key>Label</key>
93251
- <string>${LAUNCHD_LABEL}</string>
93252
- <key>ProgramArguments</key>
93253
- <array>
93254
- <string>${nodeExe}</string>
93255
- <string>${cliExe}</string>
93256
- <string>daemon</string>
93257
- </array>
93258
- <key>RunAtLoad</key>
93259
- <true/>
93260
- <key>KeepAlive</key>
93261
- <dict>
93262
- <key>SuccessfulExit</key>
93263
- <false/>
93264
- </dict>
93265
- <key>ThrottleInterval</key>
93266
- <integer>30</integer>
93267
- <key>StandardOutPath</key>
93268
- <string>${LOG_OUT}</string>
93269
- <key>StandardErrorPath</key>
93270
- <string>${LOG_ERR}</string>
93271
- <key>EnvironmentVariables</key>
93272
- <dict>
93273
- <key>PATH</key>
93274
- <string>${pathValue}</string>
93275
- </dict>
93276
- </dict>
93277
- </plist>`;
93278
- }
93279
- function installDarwin(nodeExe, cliExe) {
93280
- const plistPath = getDarwinPlistPath();
93281
- ensureDir(ADHDEV_DIR);
93282
- ensureDir(import_node_path4.default.dirname(plistPath));
93283
- import_node_fs6.default.writeFileSync(plistPath, buildPlist(nodeExe, cliExe), "utf-8");
93284
- console.log(source_default.gray(` Plist: ${plistPath}`));
93285
- try {
93286
- (0, import_node_child_process6.execSync)(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
93287
- } catch {
93288
- }
93289
- try {
93290
- (0, import_node_child_process6.execSync)(`launchctl load -w "${plistPath}"`, { stdio: "ignore" });
93291
- console.log(source_default.green("\n \u2713 Registered as LaunchAgent \u2014 daemon will start on login."));
93292
- console.log(source_default.gray(` Logs: ~/.adhdev/daemon-launchd.{out,err}`));
93293
- } catch (e) {
93294
- console.log(source_default.red(`
93295
- \u2717 launchctl load failed: ${e.message}`));
93296
- }
93297
- }
93298
- function uninstallDarwin() {
93299
- const plistPath = getDarwinPlistPath();
93300
- if (!import_node_fs6.default.existsSync(plistPath)) {
93301
- console.log(source_default.yellow("\n \u26A0 Service is not installed."));
93302
- return;
93303
- }
93304
- try {
93305
- (0, import_node_child_process6.execSync)(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
93306
- } catch {
93307
- }
93308
- import_node_fs6.default.unlinkSync(plistPath);
93309
- console.log(source_default.green("\n \u2713 Removed LaunchAgent. Daemon will no longer auto-start."));
93310
- }
93311
- function isInstalledDarwin() {
93312
- return import_node_fs6.default.existsSync(getDarwinPlistPath());
93313
- }
93314
- function buildVbs(nodeExe, cliExe) {
93315
- const logFile = import_node_path4.default.join(ADHDEV_DIR, "daemon-service.log").replace(/\\/g, "\\\\");
93316
- const escapedNodeExe = nodeExe.replace(/\\/g, "\\\\");
93317
- const escapedCliExe = cliExe.replace(/\\/g, "\\\\");
93318
- return `' ADHDev Daemon Auto-Start (generated by adhdev service install)
93319
- Set WshShell = CreateObject("WScript.Shell")
93320
- WshShell.Run "cmd.exe /c """"${escapedNodeExe}"""" """"${escapedCliExe}"""" daemon >> """"${logFile}"""" 2>&1", 0, False
93321
- `;
93322
- }
93323
- function installWindows(nodeExe, cliExe) {
93324
- const vbsPath = getWindowsVbsPath();
93325
- ensureDir(ADHDEV_DIR);
93326
- ensureDir(import_node_path4.default.dirname(vbsPath));
93327
- import_node_fs6.default.writeFileSync(vbsPath, buildVbs(nodeExe, cliExe), "utf-8");
93328
- console.log(source_default.gray(` Startup script: ${vbsPath}`));
93329
- console.log(source_default.green("\n \u2713 Registered in Startup folder \u2014 daemon will start on login (hidden)."));
93330
- console.log(source_default.gray(` Logs: ${import_node_path4.default.join(ADHDEV_DIR, "daemon-service.log")}`));
93331
- console.log(source_default.gray(" To start now without rebooting, run: adhdev daemon"));
93332
- }
93333
- function uninstallWindows() {
93334
- const vbsPath = getWindowsVbsPath();
93335
- if (!import_node_fs6.default.existsSync(vbsPath)) {
93336
- console.log(source_default.yellow("\n \u26A0 Service is not installed."));
93337
- return;
93338
- }
93339
- import_node_fs6.default.unlinkSync(vbsPath);
93340
- console.log(source_default.green("\n \u2713 Removed Startup script. Daemon will no longer auto-start."));
93341
- console.log(source_default.gray(" Note: a currently running daemon is not affected. Stop with: adhdev daemon:stop"));
93342
- }
93343
- function isInstalledWindows() {
93344
- return import_node_fs6.default.existsSync(getWindowsVbsPath());
93345
- }
93346
- function registerServiceCommands(program2) {
93347
- const svc = program2.command("service").description("\u{1F50C} Manage ADHDev as an OS background auto-start service");
93348
- svc.command("install").description("Register ADHDev daemon to start automatically on login").action(async () => {
93349
- console.log(source_default.bold("\n \u{1F680} Installing ADHDev Background Service"));
93350
- const platform13 = import_node_os6.default.platform();
93351
- const nodeExe = process.execPath;
93352
- const cliExe = resolveCliPath();
93353
- console.log(source_default.gray(` Node: ${nodeExe}`));
93354
- console.log(source_default.gray(` CLI: ${cliExe}`));
93355
- console.log(source_default.gray(` Platform: ${platform13}`));
93356
- if (platform13 === "darwin") {
93357
- installDarwin(nodeExe, cliExe);
93358
- } else if (platform13 === "win32") {
93359
- installWindows(nodeExe, cliExe);
93360
- } else {
93361
- console.log(source_default.yellow("\n \u26A0 Auto-start service install is not supported on this platform."));
93362
- console.log(source_default.gray(" On Linux, create a systemd user unit manually:"));
93363
- console.log(source_default.gray(" ~/.config/systemd/user/adhdev-daemon.service"));
93364
- }
93365
- console.log();
93366
- });
93367
- svc.command("uninstall").description("Remove the OS background service").action(async () => {
93368
- console.log(source_default.bold("\n \u{1F5D1}\uFE0F Removing ADHDev Background Service"));
93369
- const platform13 = import_node_os6.default.platform();
93370
- if (platform13 === "darwin") {
93371
- uninstallDarwin();
93372
- } else if (platform13 === "win32") {
93373
- uninstallWindows();
93374
- } else {
93375
- console.log(source_default.yellow("\n \u26A0 Not supported on this platform."));
93376
- }
93377
- console.log();
93378
- });
93379
- svc.command("status").description("Show service installation state and live daemon health").action(async () => {
93380
- const platform13 = import_node_os6.default.platform();
93381
- const installed = platform13 === "darwin" ? isInstalledDarwin() : platform13 === "win32" ? isInstalledWindows() : false;
93382
- if (installed) {
93383
- console.log(source_default.green("\n \u2713 Service is installed."));
93384
- if (platform13 === "darwin") console.log(source_default.gray(` Plist: ${getDarwinPlistPath()}`));
93385
- else console.log(source_default.gray(` Script: ${getWindowsVbsPath()}`));
93386
- } else {
93387
- console.log(source_default.gray("\n \u2717 Service is not installed. Run: adhdev service install"));
93388
- }
93389
- const health = await fetchHealth();
93390
- if (health?.ok && health.pid) {
93391
- const info = getProcessInfo(health.pid);
93392
- if (info) {
93393
- console.log(source_default.green(` \u2713 Daemon running \u2014 PID ${health.pid}, uptime ${info.uptime}, ${info.memMB} MB`));
93394
- } else {
93395
- console.log(source_default.green(` \u2713 Daemon running \u2014 PID ${health.pid}`));
93396
- }
93397
- } else {
93398
- console.log(source_default.yellow(" \u2717 Daemon is not running."));
93399
- }
93400
- const outSize = import_node_fs6.default.existsSync(LOG_OUT) ? import_node_fs6.default.statSync(LOG_OUT).size : 0;
93401
- const errSize = import_node_fs6.default.existsSync(LOG_ERR) ? import_node_fs6.default.statSync(LOG_ERR).size : 0;
93402
- if (outSize > 0 || errSize > 0) {
93403
- console.log(source_default.gray(` Logs: stdout ${formatBytes(outSize)}, stderr ${formatBytes(errSize)}`));
93404
- }
93405
- console.log();
93406
- });
93407
- svc.command("logs").description("View daemon service logs").option("--err", "Show stderr log instead of stdout").option("--clear", "Truncate all log files").option("-n, --lines <count>", "Number of lines to show", "30").action(async (options) => {
93408
- if (options.clear) {
93409
- for (const f of [LOG_OUT, LOG_ERR]) {
93410
- if (import_node_fs6.default.existsSync(f)) import_node_fs6.default.writeFileSync(f, "", "utf-8");
93411
- }
93412
- console.log(source_default.green("\n \u2713 Logs cleared.\n"));
93413
- return;
93414
- }
93415
- const logFile = options.err ? LOG_ERR : LOG_OUT;
93416
- if (!import_node_fs6.default.existsSync(logFile)) {
93417
- console.log(source_default.gray(`
93418
- No log file found: ${logFile}
93419
- `));
93420
- return;
93421
- }
93422
- const lines = parseInt(options.lines, 10) || 30;
93423
- console.log(source_default.gray(`
93424
- \u2500\u2500 ${options.err ? "stderr" : "stdout"}: ${logFile} (last ${lines} lines) \u2500\u2500
93425
- `));
93426
- const content = import_node_fs6.default.readFileSync(logFile, "utf-8");
93427
- const allLines = content.split("\n");
93428
- const lastLines = allLines.slice(-lines).join("\n");
93429
- if (lastLines.trim()) console.log(lastLines);
93430
- console.log(source_default.gray("\n (watching for new output, Ctrl+C to stop)\n"));
93431
- let offset = Buffer.byteLength(content, "utf-8");
93432
- const watcher = import_node_fs6.default.watchFile(logFile, { interval: 500 }, () => {
93433
- try {
93434
- const stat4 = import_node_fs6.default.statSync(logFile);
93435
- if (stat4.size > offset) {
93436
- const fd = import_node_fs6.default.openSync(logFile, "r");
93437
- const buf = Buffer.alloc(stat4.size - offset);
93438
- import_node_fs6.default.readSync(fd, buf, 0, buf.length, offset);
93439
- import_node_fs6.default.closeSync(fd);
93440
- process.stdout.write(buf.toString("utf-8"));
93441
- offset = stat4.size;
93442
- } else if (stat4.size < offset) {
93443
- offset = 0;
93444
- }
93445
- } catch {
93446
- }
93447
- });
93448
- const cleanup = () => {
93449
- import_node_fs6.default.unwatchFile(logFile);
93450
- process.exit(0);
93451
- };
93452
- process.on("SIGINT", cleanup);
93453
- process.on("SIGTERM", cleanup);
93454
- });
93455
- svc.command("restart").description("Restart the daemon process (service will auto-relaunch)").action(async () => {
93456
- const platform13 = import_node_os6.default.platform();
93457
- const installed = platform13 === "darwin" ? isInstalledDarwin() : platform13 === "win32" ? isInstalledWindows() : false;
93458
- if (!installed) {
93459
- console.log(source_default.yellow("\n \u26A0 Service is not installed. Use `adhdev daemon:restart` for manual restart."));
93460
- console.log(source_default.gray(" Or install the service first: adhdev service install\n"));
93461
- return;
93462
- }
93463
- rotateLogs();
93464
- const health = await fetchHealth();
93465
- if (!health?.pid) {
93466
- console.log(source_default.yellow("\n \u26A0 Daemon is not currently running."));
93467
- if (platform13 === "darwin") {
93468
- console.log(source_default.gray(" Starting via launchctl..."));
93469
- try {
93470
- (0, import_node_child_process6.execSync)(`launchctl start ${LAUNCHD_LABEL}`, { stdio: "ignore" });
93471
- console.log(source_default.green(" \u2713 Started.\n"));
93472
- } catch {
93473
- console.log(source_default.red(" \u2717 Failed to start. Check: adhdev service logs --err\n"));
93474
- }
93475
- } else {
93476
- console.log(source_default.gray(" Start with: adhdev daemon\n"));
93477
- }
93478
- return;
93479
- }
93480
- console.log(source_default.cyan(`
93481
- Stopping daemon (PID ${health.pid})...`));
93482
- try {
93483
- process.kill(health.pid, "SIGTERM");
93484
- } catch {
93485
- console.log(source_default.yellow(" Could not send SIGTERM. Process may have already exited."));
93486
- }
93487
- await new Promise((r) => setTimeout(r, 2e3));
93488
- if (platform13 === "win32") {
93489
- const vbsPath = getWindowsVbsPath();
93490
- if (import_node_fs6.default.existsSync(vbsPath)) {
93491
- try {
93492
- (0, import_node_child_process6.execSync)(`wscript.exe "${vbsPath}"`, { stdio: "ignore", windowsHide: true });
93493
- } catch {
93494
- }
93495
- }
93496
- }
93497
- let restarted = false;
93498
- for (let i = 0; i < 8; i++) {
93499
- await new Promise((r) => setTimeout(r, 1e3));
93500
- const newHealth = await fetchHealth();
93501
- if (newHealth?.ok && newHealth.pid !== health.pid) {
93502
- restarted = true;
93503
- const info = getProcessInfo(newHealth.pid);
93504
- console.log(source_default.green(` \u2713 Daemon restarted \u2014 new PID ${newHealth.pid}${info ? `, ${info.memMB} MB` : ""}`));
93505
- break;
93506
- }
93507
- }
93508
- if (!restarted) {
93509
- console.log(source_default.yellow(" \u26A0 Daemon did not restart within 10s."));
93510
- console.log(source_default.gray(" Check: adhdev service logs --err"));
93511
- }
93512
- console.log();
93513
- });
93514
- }
93515
- function formatBytes(bytes) {
93516
- if (bytes === 0) return "0 B";
93517
- if (bytes < 1024) return `${bytes} B`;
93518
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
93519
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
93520
- }
93521
-
93522
93647
  // src/cli/index.ts
93523
93648
  init_version();
93524
93649
  var pkgVersion2 = resolvePackageVersion();