@dosu/cli 0.56.1 → 0.57.0

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.
Files changed (3) hide show
  1. package/README.md +3 -2
  2. package/bin/dosu.js +370 -189
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -96,15 +96,16 @@ Or right-click the binary, select "Open", and click "Open" in the dialog.
96
96
  | `dosu login` | Authenticate with Dosu via browser OAuth |
97
97
  | `dosu logout` | Clear saved credentials |
98
98
  | `dosu status [--json]` | Show current authentication and MCP status |
99
- | `dosu upgrade` | Update Dosu through the package manager that installed it |
99
+ | `dosu upgrade` | Update Dosu through the package manager that installed it, then re-run `dosu setup` on the new version |
100
100
  | `dosu mcp list` | List supported AI tools |
101
101
  | `dosu mcp add <tool>` | Add the Dosu MCP server to a specific tool |
102
+ | `dosu mcp refresh` | Rewrite the Dosu MCP entry in every already-configured tool from the current setup |
102
103
  | `dosu logs` | View or manage debug logs (`--tail`, `--clear`) |
103
104
  | `dosu telemetry` | Manage usage analytics and error diagnostics (`status`, `enable`, `disable`, `reset`) |
104
105
 
105
106
  `dosu mcp add` takes `-g, --global` to install for all projects instead of project-local, and `--show-secret` to print the full manual config.
106
107
 
107
- `dosu upgrade` delegates to npm, pnpm, Yarn Classic, or Homebrew only after confirming which manager owns the current installation. Temporary package-runner invocations stay ephemeral, ambiguous or local installs are left unchanged, and standalone binaries receive the latest safe manual download path.
108
+ `dosu upgrade` delegates to npm, pnpm, Yarn Classic, or Homebrew only after confirming which manager owns the current installation. Temporary package-runner invocations stay ephemeral, ambiguous or local installs are left unchanged, and standalone binaries receive the latest safe manual download path. After a successful update it re-invokes the new version to run `dosu setup`, so every configured tool gets the current MCP entry, hooks, status line, rules, and skill; without a TTY it runs the non-interactive `dosu mcp refresh` instead. Upgrades done outside `dosu upgrade` (npm, brew, `npx @dosu/cli@latest`) get a safety net: when the new version changed the shape of the MCP entry, the first command on it silently rewrites configured tools' MCP entries and prompts you to run `dosu setup` for the rest.
108
109
 
109
110
  ### Platform commands
110
111
 
package/bin/dosu.js CHANGED
@@ -636,7 +636,18 @@ function numberValue(value) {
636
636
  var init_config_v1_migration = () => {};
637
637
 
638
638
  // src/config/config.ts
639
- import { existsSync as existsSync2, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
639
+ import {
640
+ closeSync,
641
+ constants,
642
+ existsSync as existsSync2,
643
+ fstatSync,
644
+ mkdirSync,
645
+ openSync,
646
+ readFileSync,
647
+ readSync,
648
+ renameSync,
649
+ writeFileSync
650
+ } from "node:fs";
640
651
  import { homedir as homedir2 } from "node:os";
641
652
  import { join as join2 } from "node:path";
642
653
  function getConfigUserID(cfg) {
@@ -701,6 +712,26 @@ function loadConfig() {
701
712
  } catch {}
702
713
  return parsed;
703
714
  }
715
+ function loadConfigNonBlocking() {
716
+ let fd;
717
+ try {
718
+ fd = openSync(getConfigPath(), constants.O_RDONLY | constants.O_NONBLOCK);
719
+ const file = fstatSync(fd);
720
+ if (!file.isFile() || file.size > MAX_BACKGROUND_CONFIG_BYTES)
721
+ return;
722
+ const content = Buffer.alloc(MAX_BACKGROUND_CONFIG_BYTES);
723
+ const bytesRead = readSync(fd, content, 0, content.byteLength, 0);
724
+ return parseConfig(JSON.parse(content.subarray(0, bytesRead).toString("utf8")));
725
+ } catch {
726
+ return;
727
+ } finally {
728
+ if (fd !== undefined) {
729
+ try {
730
+ closeSync(fd);
731
+ } catch {}
732
+ }
733
+ }
734
+ }
704
735
  function parseConfig(raw) {
705
736
  if (isConfigV2(raw))
706
737
  return normalizeV2(raw);
@@ -796,8 +827,10 @@ function stringValue2(value) {
796
827
  function numberValue2(value) {
797
828
  return typeof value === "number" && Number.isFinite(value) ? value : undefined;
798
829
  }
830
+ var MAX_BACKGROUND_CONFIG_BYTES;
799
831
  var init_config = __esm(() => {
800
832
  init_config_v1_migration();
833
+ MAX_BACKGROUND_CONFIG_BYTES = 64 * 1024;
801
834
  });
802
835
 
803
836
  // src/mcp/config-helpers.ts
@@ -1050,7 +1083,7 @@ function isNpxInvocation(channel = INSTALL_CHANNEL, env = process.env) {
1050
1083
  function getVersionString() {
1051
1084
  return `v${VERSION}`;
1052
1085
  }
1053
- var VERSION = "0.56.1", INSTALL_CHANNEL = "npm";
1086
+ var VERSION = "0.57.0", INSTALL_CHANNEL = "npm";
1054
1087
 
1055
1088
  // src/debug/logger.ts
1056
1089
  import {
@@ -1829,21 +1862,21 @@ var init_read = __esm(() => {
1829
1862
  });
1830
1863
 
1831
1864
  // src/sync/incognito.ts
1832
- import { closeSync, fstatSync, openSync, readSync } from "node:fs";
1865
+ import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readSync as readSync2 } from "node:fs";
1833
1866
  function textHasIncognitoMarker(text) {
1834
1867
  return text.includes(INCOGNITO_MARKER) || text.includes(COMMAND_NAME_MARKER);
1835
1868
  }
1836
1869
  function transcriptHasIncognitoMarker(path, maxBytes = MAX_SCAN_BYTES) {
1837
1870
  let fd;
1838
1871
  try {
1839
- fd = openSync(path, "r");
1840
- const size = Math.min(fstatSync(fd).size, maxBytes);
1872
+ fd = openSync2(path, "r");
1873
+ const size = Math.min(fstatSync2(fd).size, maxBytes);
1841
1874
  if (size === 0)
1842
1875
  return false;
1843
1876
  const buffer = Buffer.allocUnsafe(size);
1844
1877
  let offset = 0;
1845
1878
  while (offset < size) {
1846
- const read = readSync(fd, buffer, offset, size - offset, offset);
1879
+ const read = readSync2(fd, buffer, offset, size - offset, offset);
1847
1880
  if (read === 0)
1848
1881
  break;
1849
1882
  offset += read;
@@ -1854,7 +1887,7 @@ function transcriptHasIncognitoMarker(path, maxBytes = MAX_SCAN_BYTES) {
1854
1887
  } finally {
1855
1888
  if (fd !== undefined) {
1856
1889
  try {
1857
- closeSync(fd);
1890
+ closeSync2(fd);
1858
1891
  } catch {}
1859
1892
  }
1860
1893
  }
@@ -4205,11 +4238,11 @@ var init_session_errors = __esm(() => {
4205
4238
  // src/config/config-lock.ts
4206
4239
  import { randomUUID } from "node:crypto";
4207
4240
  import {
4208
- closeSync as closeSync2,
4241
+ closeSync as closeSync3,
4209
4242
  existsSync as existsSync8,
4210
4243
  linkSync,
4211
4244
  mkdirSync as mkdirSync5,
4212
- openSync as openSync2,
4245
+ openSync as openSync3,
4213
4246
  readFileSync as readFileSync7,
4214
4247
  statSync as statSync3,
4215
4248
  unlinkSync,
@@ -4262,12 +4295,12 @@ async function acquireConfigRefreshLock(options) {
4262
4295
  }
4263
4296
  }
4264
4297
  function createLockFile(lockPath, metadata) {
4265
- const fd = openSync2(lockPath, "wx", 384);
4298
+ const fd = openSync3(lockPath, "wx", 384);
4266
4299
  try {
4267
4300
  writeFileSync4(fd, JSON.stringify(metadata));
4268
4301
  } catch (cause) {
4269
4302
  try {
4270
- closeSync2(fd);
4303
+ closeSync3(fd);
4271
4304
  } catch {}
4272
4305
  try {
4273
4306
  unlinkSync(lockPath);
@@ -4275,7 +4308,7 @@ function createLockFile(lockPath, metadata) {
4275
4308
  throw cause;
4276
4309
  }
4277
4310
  try {
4278
- closeSync2(fd);
4311
+ closeSync3(fd);
4279
4312
  } catch (cause) {
4280
4313
  try {
4281
4314
  unlinkSync(lockPath);
@@ -11821,12 +11854,12 @@ var init_integrations = __esm(() => {
11821
11854
 
11822
11855
  // src/sessions/project-dir.ts
11823
11856
  import {
11824
- closeSync as closeSync3,
11857
+ closeSync as closeSync4,
11825
11858
  existsSync as existsSync11,
11826
11859
  mkdirSync as mkdirSync7,
11827
- openSync as openSync3,
11860
+ openSync as openSync4,
11828
11861
  readFileSync as readFileSync11,
11829
- readSync as readSync2,
11862
+ readSync as readSync3,
11830
11863
  renameSync as renameSync3,
11831
11864
  statSync as statSync4,
11832
11865
  writeFileSync as writeFileSync7
@@ -11859,18 +11892,18 @@ function saveCacheFile(configDir, entries) {
11859
11892
  function readHead(path, bytes = HEAD_BYTES) {
11860
11893
  let fd;
11861
11894
  try {
11862
- fd = openSync3(path, "r");
11895
+ fd = openSync4(path, "r");
11863
11896
  } catch {
11864
11897
  return null;
11865
11898
  }
11866
11899
  try {
11867
11900
  const buf = Buffer.alloc(bytes);
11868
- const read = readSync2(fd, buf, 0, bytes, 0);
11901
+ const read = readSync3(fd, buf, 0, bytes, 0);
11869
11902
  return buf.subarray(0, read).toString("utf-8");
11870
11903
  } catch {
11871
11904
  return null;
11872
11905
  } finally {
11873
- closeSync3(fd);
11906
+ closeSync4(fd);
11874
11907
  }
11875
11908
  }
11876
11909
  function cwdFromJsonlHead(text) {
@@ -13680,7 +13713,7 @@ var init_sync = __esm(() => {
13680
13713
  });
13681
13714
 
13682
13715
  // src/telemetry/debug-id.ts
13683
- var BUNDLE_DEBUG_ID_PLACEHOLDER = "93db7db7-907f-4d14-6475-6e2164756e21";
13716
+ var BUNDLE_DEBUG_ID_PLACEHOLDER = "6712b11a-8117-30cb-6475-6e2164756e21";
13684
13717
 
13685
13718
  // src/telemetry/telemetry.ts
13686
13719
  import { randomUUID as randomUUID2 } from "node:crypto";
@@ -15205,7 +15238,7 @@ var init_env = __esm(() => {
15205
15238
  });
15206
15239
 
15207
15240
  // src/learner/executable.ts
15208
- import { accessSync, constants, existsSync as existsSync16 } from "node:fs";
15241
+ import { accessSync, constants as constants2, existsSync as existsSync16 } from "node:fs";
15209
15242
  import { createRequire as createRequire3 } from "node:module";
15210
15243
  import { homedir as homedir6 } from "node:os";
15211
15244
  import { delimiter as delimiter2, dirname as dirname6, join as join16 } from "node:path";
@@ -15224,7 +15257,7 @@ function sdkNativeBinaryExists() {
15224
15257
  }
15225
15258
  function isExecutable(path) {
15226
15259
  try {
15227
- accessSync(path, constants.X_OK);
15260
+ accessSync(path, constants2.X_OK);
15228
15261
  return true;
15229
15262
  } catch {
15230
15263
  return false;
@@ -60724,6 +60757,10 @@ function runUpgrade(channel = INSTALL_CHANNEL, options = {}) {
60724
60757
  }
60725
60758
  manager = owningPackageManager(options.entrypoint ?? process.argv[1], platform, options.comSpec, env, cwd);
60726
60759
  if (!manager) {
60760
+ if (env.DOSU_DEV === "true") {
60761
+ console.log("DOSU_DEV=true and this copy is not a global package: skipping the package install.");
60762
+ return 0;
60763
+ }
60727
60764
  printNonGlobalPackageGuidance();
60728
60765
  return 1;
60729
60766
  }
@@ -60756,6 +60793,41 @@ Could not update Dosu automatically.`);
60756
60793
  console.log('Run "dosu --version" to verify.');
60757
60794
  return 0;
60758
60795
  }
60796
+ function postUpgradeArgs(interactive) {
60797
+ return interactive ? ["setup"] : ["mcp", "refresh"];
60798
+ }
60799
+ function newBinaryInvocation(channel, args, entrypoint = process.argv[1], execPath = process.execPath) {
60800
+ if (channel === "npm") {
60801
+ return entrypoint ? { command: execPath, args: [entrypoint, ...args] } : null;
60802
+ }
60803
+ if (channel === "homebrew") {
60804
+ return { command: "dosu", args };
60805
+ }
60806
+ return null;
60807
+ }
60808
+ function isInteractiveTerminal() {
60809
+ return process.stdin.isTTY === true && process.stdout.isTTY === true;
60810
+ }
60811
+ function setupAgentsWithNewBinary(channel, options) {
60812
+ const interactive = options.interactive ?? isInteractiveTerminal();
60813
+ const invocation = newBinaryInvocation(channel, postUpgradeArgs(interactive), options.entrypoint ?? process.argv[1]);
60814
+ if (!invocation) {
60815
+ console.error(SETUP_FALLBACK);
60816
+ return;
60817
+ }
60818
+ console.log(interactive ? `
60819
+ Running setup with the new version to update your AI agents...
60820
+ ` : `
60821
+ Refreshing agent MCP configs with the new version...`);
60822
+ const result = spawnSync(invocation.command, invocation.args, {
60823
+ cwd: options.cwd ?? homedir9(),
60824
+ shell: false,
60825
+ stdio: "inherit"
60826
+ });
60827
+ if (result.error || result.status !== 0) {
60828
+ console.error(SETUP_FALLBACK);
60829
+ }
60830
+ }
60759
60831
  async function completeUpgrade(channel = INSTALL_CHANNEL, options = {}) {
60760
60832
  const status = runUpgrade(channel, options);
60761
60833
  if (status !== 0)
@@ -60771,16 +60843,17 @@ async function completeUpgrade(channel = INSTALL_CHANNEL, options = {}) {
60771
60843
  } catch {
60772
60844
  console.error('Skills could not be refreshed. Run "dosu skill update" to retry.');
60773
60845
  }
60846
+ setupAgentsWithNewBinary(channel, options);
60774
60847
  return 0;
60775
60848
  }
60776
60849
  function upgradeCommand() {
60777
- return new Command("upgrade").description("Update Dosu to the latest version and refresh agent skills").action(async () => {
60850
+ return new Command("upgrade").description("Update Dosu to the latest version, then re-run setup to update your AI agents").action(async () => {
60778
60851
  const status = await completeUpgrade();
60779
60852
  if (status !== 0)
60780
60853
  process.exitCode = status;
60781
60854
  });
60782
60855
  }
60783
- var import_picocolors31, PACKAGE_NAME = "@dosu/cli", LATEST_PACKAGE, BREW_MANUAL_COMMAND = "brew upgrade dosu-ai/dosu/dosu", NPX_COMMAND = "npx -y @dosu/cli@latest", PROBE_TIMEOUT_MS = 5000, RELEASES_URL = "https://github.com/dosu-ai/dosu-cli/releases/latest", PACKAGE_MANAGERS;
60856
+ var import_picocolors31, PACKAGE_NAME = "@dosu/cli", LATEST_PACKAGE, BREW_MANUAL_COMMAND = "brew upgrade dosu-ai/dosu/dosu", NPX_COMMAND = "npx -y @dosu/cli@latest", PROBE_TIMEOUT_MS = 5000, RELEASES_URL = "https://github.com/dosu-ai/dosu-cli/releases/latest", PACKAGE_MANAGERS, SETUP_FALLBACK = 'Run "dosu setup" to finish updating your AI agents.';
60784
60857
  var init_upgrade = __esm(() => {
60785
60858
  init_commander();
60786
60859
  init_skill();
@@ -60806,7 +60879,7 @@ var init_upgrade = __esm(() => {
60806
60879
  });
60807
60880
 
60808
60881
  // src/debug/follow.ts
60809
- import { closeSync as closeSync5, openSync as openSync5, readSync as readSync4, statSync as statSync8 } from "node:fs";
60882
+ import { closeSync as closeSync6, openSync as openSync6, readSync as readSync5, statSync as statSync8 } from "node:fs";
60810
60883
  function createLogFollower(path, emit) {
60811
60884
  const sizeOf = () => {
60812
60885
  try {
@@ -60824,15 +60897,15 @@ function createLogFollower(path, emit) {
60824
60897
  if (size === offset)
60825
60898
  return;
60826
60899
  try {
60827
- const fd = openSync5(path, "r");
60900
+ const fd = openSync6(path, "r");
60828
60901
  try {
60829
60902
  const buf = Buffer.alloc(size - offset);
60830
- const read = readSync4(fd, buf, 0, buf.length, offset);
60903
+ const read = readSync5(fd, buf, 0, buf.length, offset);
60831
60904
  offset += read;
60832
60905
  if (read > 0)
60833
60906
  emit(buf.toString("utf-8", 0, read));
60834
60907
  } finally {
60835
- closeSync5(fd);
60908
+ closeSync6(fd);
60836
60909
  }
60837
60910
  } catch {}
60838
60911
  }
@@ -61510,6 +61583,36 @@ var init_providers = __esm(() => {
61510
61583
  init_zed();
61511
61584
  });
61512
61585
 
61586
+ // src/mcp/refresh.ts
61587
+ function configuredProviders() {
61588
+ return allSetupProviders().filter((provider) => {
61589
+ try {
61590
+ return provider.isInstalled() && provider.isConfigured();
61591
+ } catch {
61592
+ return false;
61593
+ }
61594
+ });
61595
+ }
61596
+ function refreshConfiguredProviders(cfg) {
61597
+ const result = { updated: [], failed: [] };
61598
+ for (const provider of configuredProviders()) {
61599
+ try {
61600
+ provider.install(cfg, true);
61601
+ logger.info("mcp", `Refreshed ${provider.name()} MCP entry`);
61602
+ result.updated.push(provider);
61603
+ } catch (err) {
61604
+ const error = err instanceof Error ? err : new Error(String(err));
61605
+ logger.error("mcp", `Refresh failed for ${provider.name()}: ${error.message}`);
61606
+ result.failed.push({ provider, error });
61607
+ }
61608
+ }
61609
+ return result;
61610
+ }
61611
+ var init_refresh = __esm(() => {
61612
+ init_logger();
61613
+ init_providers();
61614
+ });
61615
+
61513
61616
  // src/version/update-check.ts
61514
61617
  import { existsSync as existsSync23, mkdirSync as mkdirSync13, readFileSync as readFileSync22, writeFileSync as writeFileSync14 } from "node:fs";
61515
61618
  import { join as join39 } from "node:path";
@@ -61665,15 +61768,98 @@ var init_update_check = __esm(() => {
61665
61768
  SEMVER_PATTERN = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
61666
61769
  });
61667
61770
 
61771
+ // src/version/mcp-refresh-check.ts
61772
+ import { existsSync as existsSync24, mkdirSync as mkdirSync14, readFileSync as readFileSync23, writeFileSync as writeFileSync15 } from "node:fs";
61773
+ import { join as join40 } from "node:path";
61774
+ function needsMcpRefresh(previous, current) {
61775
+ if (previous === null)
61776
+ return true;
61777
+ if (previous === current)
61778
+ return false;
61779
+ const [older, newer] = isNewerVersion(current, previous) ? [previous, current] : [current, previous];
61780
+ return MCP_FORMAT_CHANGES.some((change) => isNewerVersion(change, older) && !isNewerVersion(change, newer));
61781
+ }
61782
+ function getCachePath4() {
61783
+ return join40(getConfigDir(), CACHE_FILENAME5);
61784
+ }
61785
+ function readMcpRefreshCache() {
61786
+ try {
61787
+ const path = getCachePath4();
61788
+ if (!existsSync24(path))
61789
+ return null;
61790
+ const data = JSON.parse(readFileSync23(path, "utf-8"));
61791
+ if (typeof data?.version === "string")
61792
+ return { version: data.version };
61793
+ return null;
61794
+ } catch {
61795
+ return null;
61796
+ }
61797
+ }
61798
+ function writeMcpRefreshCache(cache) {
61799
+ try {
61800
+ const dir = getConfigDir();
61801
+ if (!existsSync24(dir)) {
61802
+ mkdirSync14(dir, { recursive: true, mode: 448 });
61803
+ }
61804
+ writeFileSync15(getCachePath4(), JSON.stringify(cache), { mode: 384 });
61805
+ } catch {}
61806
+ }
61807
+ function canRefreshMcp(cfg) {
61808
+ const target = cfg.active_account?.target;
61809
+ if (!target?.api_key)
61810
+ return false;
61811
+ return cfg.mode === MODE_OSS || Boolean(target.deployment_id);
61812
+ }
61813
+ function displayNotice3(names) {
61814
+ console.error(`
61815
+ ${import_picocolors33.default.green(`✓ Dosu ${VERSION}: refreshed MCP config for ${names.join(", ")}`)}
61816
+ ` + `${import_picocolors33.default.dim(` Run ${import_picocolors33.default.cyan('"dosu setup"')} to also update skills, hooks, and rules, then restart your AI agents.`)}
61817
+ `);
61818
+ }
61819
+ function checkForMcpRefresh(options = {}) {
61820
+ const notify = options.notify ?? true;
61821
+ try {
61822
+ const previous = readMcpRefreshCache()?.version ?? null;
61823
+ if (previous === VERSION)
61824
+ return;
61825
+ if (!needsMcpRefresh(previous, VERSION)) {
61826
+ writeMcpRefreshCache({ version: VERSION });
61827
+ return;
61828
+ }
61829
+ const cfg = loadConfigNonBlocking();
61830
+ if (!cfg || !canRefreshMcp(cfg)) {
61831
+ logger.debug("mcp-refresh", "Skipping MCP refresh: no API key or deployment configured");
61832
+ return;
61833
+ }
61834
+ const result = refreshConfiguredProviders(cfg);
61835
+ writeMcpRefreshCache({ version: VERSION });
61836
+ logger.info("mcp-refresh", `Version ${VERSION}: refreshed ${result.updated.length}, failed ${result.failed.length}`);
61837
+ if (notify && result.updated.length > 0) {
61838
+ displayNotice3(result.updated.map((provider) => provider.name()));
61839
+ }
61840
+ } catch (err) {
61841
+ logger.error("mcp-refresh", `MCP refresh check failed: ${err}`);
61842
+ }
61843
+ }
61844
+ var import_picocolors33, CACHE_FILENAME5 = "mcp-refresh.json", MCP_FORMAT_CHANGES;
61845
+ var init_mcp_refresh_check = __esm(() => {
61846
+ init_config();
61847
+ init_logger();
61848
+ init_refresh();
61849
+ init_update_check();
61850
+ import_picocolors33 = __toESM(require_picocolors(), 1);
61851
+ MCP_FORMAT_CHANGES = ["0.53.0"];
61852
+ });
61853
+
61668
61854
  // src/rules/installer.ts
61669
- import { existsSync as existsSync24, mkdirSync as mkdirSync14, readFileSync as readFileSync23, unlinkSync as unlinkSync5, writeFileSync as writeFileSync15 } from "node:fs";
61855
+ import { existsSync as existsSync25, mkdirSync as mkdirSync15, readFileSync as readFileSync24, unlinkSync as unlinkSync5, writeFileSync as writeFileSync16 } from "node:fs";
61670
61856
  import { homedir as homedir12 } from "node:os";
61671
- import { dirname as dirname11, join as join40 } from "node:path";
61857
+ import { dirname as dirname11, join as join41 } from "node:path";
61672
61858
  function claudeConfigDir4() {
61673
- return process.env.CLAUDE_CONFIG_DIR || join40(homedir12(), ".claude");
61859
+ return process.env.CLAUDE_CONFIG_DIR || join41(homedir12(), ".claude");
61674
61860
  }
61675
61861
  function codexHome4() {
61676
- return process.env.CODEX_HOME || join40(homedir12(), ".codex");
61862
+ return process.env.CODEX_HOME || join41(homedir12(), ".codex");
61677
61863
  }
61678
61864
  function isRuleAgent(agent) {
61679
61865
  return Object.hasOwn(RULE_TARGETS, agent);
@@ -61699,17 +61885,17 @@ async function fetchDosuRule(fetchImpl = globalThis.fetch) {
61699
61885
  return normalizeRule(FALLBACK_DOSU_RULE);
61700
61886
  }
61701
61887
  function ensureParent(path) {
61702
- mkdirSync14(dirname11(path), { recursive: true });
61888
+ mkdirSync15(dirname11(path), { recursive: true });
61703
61889
  }
61704
61890
  function writeIfChanged(path, content) {
61705
- if (!existsSync24(path)) {
61891
+ if (!existsSync25(path)) {
61706
61892
  ensureParent(path);
61707
- writeFileSync15(path, content, "utf-8");
61893
+ writeFileSync16(path, content, "utf-8");
61708
61894
  return "created";
61709
61895
  }
61710
- if (readFileSync23(path, "utf-8") === content)
61896
+ if (readFileSync24(path, "utf-8") === content)
61711
61897
  return "unchanged";
61712
- writeFileSync15(path, content, "utf-8");
61898
+ writeFileSync16(path, content, "utf-8");
61713
61899
  return "updated";
61714
61900
  }
61715
61901
  function findSection(content) {
@@ -61735,14 +61921,14 @@ function buildSection(content, lineEnding = `
61735
61921
  return `${DOSU_RULE_SECTION_START}${lineEnding}${body}${lineEnding}${DOSU_RULE_SECTION_END}`;
61736
61922
  }
61737
61923
  function upsertSection(path, content) {
61738
- if (!existsSync24(path)) {
61924
+ if (!existsSync25(path)) {
61739
61925
  const section = buildSection(content);
61740
61926
  ensureParent(path);
61741
- writeFileSync15(path, `${section}
61927
+ writeFileSync16(path, `${section}
61742
61928
  `, "utf-8");
61743
61929
  return "created";
61744
61930
  }
61745
- const existing = readFileSync23(path, "utf-8");
61931
+ const existing = readFileSync24(path, "utf-8");
61746
61932
  const lineEnding = lineEndingFor(existing);
61747
61933
  const section = buildSection(content, lineEnding);
61748
61934
  const location = findSection(existing);
@@ -61755,7 +61941,7 @@ function upsertSection(path, content) {
61755
61941
  }
61756
61942
  if (next === existing)
61757
61943
  return "unchanged";
61758
- writeFileSync15(path, next, "utf-8");
61944
+ writeFileSync16(path, next, "utf-8");
61759
61945
  return "updated";
61760
61946
  }
61761
61947
  function installRuleForAgent(agent, content) {
@@ -61772,13 +61958,13 @@ function removeRuleForAgent(agent) {
61772
61958
  return null;
61773
61959
  const target = RULE_TARGETS[agent];
61774
61960
  const path = target.path();
61775
- if (!existsSync24(path))
61961
+ if (!existsSync25(path))
61776
61962
  return { agent, path, action: "not_found" };
61777
61963
  if (target.kind === "file") {
61778
61964
  unlinkSync5(path);
61779
61965
  return { agent, path, action: "removed" };
61780
61966
  }
61781
- const existing = readFileSync23(path, "utf-8");
61967
+ const existing = readFileSync24(path, "utf-8");
61782
61968
  const lineEnding = lineEndingFor(existing);
61783
61969
  const location = findSection(existing);
61784
61970
  if (!location)
@@ -61787,7 +61973,7 @@ function removeRuleForAgent(agent) {
61787
61973
  if (!next) {
61788
61974
  unlinkSync5(path);
61789
61975
  } else {
61790
- writeFileSync15(path, `${next}${lineEnding}`, "utf-8");
61976
+ writeFileSync16(path, `${next}${lineEnding}`, "utf-8");
61791
61977
  }
61792
61978
  return { agent, path, action: "removed" };
61793
61979
  }
@@ -61815,36 +62001,36 @@ var init_installer = __esm(() => {
61815
62001
  RULE_TARGETS = {
61816
62002
  claude: {
61817
62003
  kind: "file",
61818
- path: () => join40(claudeConfigDir4(), "rules", "dosu.md")
62004
+ path: () => join41(claudeConfigDir4(), "rules", "dosu.md")
61819
62005
  },
61820
62006
  cursor: {
61821
62007
  kind: "file",
61822
- path: () => join40(homedir12(), ".cursor", "rules", "dosu.mdc"),
62008
+ path: () => join41(homedir12(), ".cursor", "rules", "dosu.mdc"),
61823
62009
  cursorFrontmatter: true
61824
62010
  },
61825
62011
  codex: {
61826
62012
  kind: "section",
61827
- path: () => join40(codexHome4(), "AGENTS.md")
62013
+ path: () => join41(codexHome4(), "AGENTS.md")
61828
62014
  },
61829
62015
  opencode: {
61830
62016
  kind: "section",
61831
- path: () => join40(homedir12(), ".config", "opencode", "AGENTS.md")
62017
+ path: () => join41(homedir12(), ".config", "opencode", "AGENTS.md")
61832
62018
  },
61833
62019
  gemini: {
61834
62020
  kind: "section",
61835
- path: () => join40(homedir12(), ".gemini", "GEMINI.md")
62021
+ path: () => join41(homedir12(), ".gemini", "GEMINI.md")
61836
62022
  },
61837
62023
  antigravity: {
61838
62024
  kind: "section",
61839
- path: () => join40(homedir12(), ".gemini", "GEMINI.md")
62025
+ path: () => join41(homedir12(), ".gemini", "GEMINI.md")
61840
62026
  }
61841
62027
  };
61842
62028
  });
61843
62029
 
61844
62030
  // src/setup/agents-md-step.ts
61845
62031
  import { execSync as execSync3 } from "node:child_process";
61846
- import { existsSync as existsSync25, readFileSync as readFileSync24, writeFileSync as writeFileSync16 } from "node:fs";
61847
- import { join as join41 } from "node:path";
62032
+ import { existsSync as existsSync26, readFileSync as readFileSync25, writeFileSync as writeFileSync17 } from "node:fs";
62033
+ import { join as join42 } from "node:path";
61848
62034
  function inGitWorkTree(cwd = process.cwd()) {
61849
62035
  try {
61850
62036
  const stdout = execSync3("git rev-parse --is-inside-work-tree", {
@@ -61858,10 +62044,10 @@ function inGitWorkTree(cwd = process.cwd()) {
61858
62044
  }
61859
62045
  function dosuAgentsSectionState(cwd = process.cwd()) {
61860
62046
  try {
61861
- const path = join41(cwd, "AGENTS.md");
61862
- if (!existsSync25(path))
62047
+ const path = join42(cwd, "AGENTS.md");
62048
+ if (!existsSync26(path))
61863
62049
  return "missing";
61864
- const content = readFileSync24(path, "utf-8");
62050
+ const content = readFileSync25(path, "utf-8");
61865
62051
  const startMatch = content.match(SECTION_START_RE);
61866
62052
  if (!startMatch || !content.includes(DOSU_SECTION_END))
61867
62053
  return "missing";
@@ -61896,14 +62082,14 @@ function findSection2(content) {
61896
62082
  return { start, end };
61897
62083
  }
61898
62084
  function upsertDosuAgentsSection(cwd = process.cwd(), content = FALLBACK_DOSU_RULE) {
61899
- const path = join41(cwd, "AGENTS.md");
61900
- if (!existsSync25(path)) {
62085
+ const path = join42(cwd, "AGENTS.md");
62086
+ if (!existsSync26(path)) {
61901
62087
  const section = buildDosuAgentsSection(content);
61902
- writeFileSync16(path, `${section}
62088
+ writeFileSync17(path, `${section}
61903
62089
  `);
61904
62090
  return { path, action: "created" };
61905
62091
  }
61906
- const existing = readFileSync24(path, "utf-8");
62092
+ const existing = readFileSync25(path, "utf-8");
61907
62093
  const lineEnding = lineEndingFor2(existing);
61908
62094
  const section = renderDosuAgentsSection(content, lineEnding);
61909
62095
  const location = findSection2(existing);
@@ -61915,7 +62101,7 @@ function upsertDosuAgentsSection(cwd = process.cwd(), content = FALLBACK_DOSU_RU
61915
62101
  }
61916
62102
  if (next === existing)
61917
62103
  return { path, action: "unchanged" };
61918
- writeFileSync16(path, next);
62104
+ writeFileSync17(path, next);
61919
62105
  return { path, action: "updated" };
61920
62106
  }
61921
62107
  async function stepUpdateAgentsMd(cwd = process.cwd(), content) {
@@ -61971,15 +62157,15 @@ var init_errors3 = __esm(() => {
61971
62157
  var MCP_PROVIDER_SLUG2 = "dosu_mcp";
61972
62158
 
61973
62159
  // src/sessions/session-title.ts
61974
- import { existsSync as existsSync26, mkdirSync as mkdirSync15, readFileSync as readFileSync25, renameSync as renameSync6, statSync as statSync9, writeFileSync as writeFileSync17 } from "node:fs";
62160
+ import { existsSync as existsSync27, mkdirSync as mkdirSync16, readFileSync as readFileSync26, renameSync as renameSync6, statSync as statSync9, writeFileSync as writeFileSync18 } from "node:fs";
61975
62161
  import { homedir as homedir13 } from "node:os";
61976
- import { join as join42 } from "node:path";
62162
+ import { join as join43 } from "node:path";
61977
62163
  function cachePath2(configDir) {
61978
- return join42(configDir, CACHE_FILENAME5);
62164
+ return join43(configDir, CACHE_FILENAME6);
61979
62165
  }
61980
62166
  function loadCacheFile2(configDir) {
61981
62167
  try {
61982
- const raw = JSON.parse(readFileSync25(cachePath2(configDir), "utf-8"));
62168
+ const raw = JSON.parse(readFileSync26(cachePath2(configDir), "utf-8"));
61983
62169
  if (raw.schema_version !== CACHE_SCHEMA_VERSION2 || typeof raw.entries !== "object")
61984
62170
  return {};
61985
62171
  return raw.entries;
@@ -61989,12 +62175,12 @@ function loadCacheFile2(configDir) {
61989
62175
  }
61990
62176
  function saveCacheFile2(configDir, entries) {
61991
62177
  try {
61992
- if (!existsSync26(configDir))
61993
- mkdirSync15(configDir, { recursive: true, mode: 448 });
62178
+ if (!existsSync27(configDir))
62179
+ mkdirSync16(configDir, { recursive: true, mode: 448 });
61994
62180
  const path = cachePath2(configDir);
61995
62181
  const tmp = `${path}.${process.pid}.tmp`;
61996
62182
  const file = { schema_version: CACHE_SCHEMA_VERSION2, entries };
61997
- writeFileSync17(tmp, JSON.stringify(file), { mode: 384 });
62183
+ writeFileSync18(tmp, JSON.stringify(file), { mode: 384 });
61998
62184
  renameSync6(tmp, path);
61999
62185
  } catch {}
62000
62186
  }
@@ -62027,15 +62213,15 @@ function claudeSummaryFromHead(text) {
62027
62213
  }
62028
62214
  return null;
62029
62215
  }
62030
- function reconstructSession(harness, id, slug, home = homedir13(), exists = existsSync26) {
62216
+ function reconstructSession(harness, id, slug, home = homedir13(), exists = existsSync27) {
62031
62217
  if (!slug)
62032
62218
  return null;
62033
62219
  const candidates = [];
62034
62220
  if (harness === "claude") {
62035
- candidates.push(join42(home, ".claude", "projects", slug, `${id}.jsonl`));
62221
+ candidates.push(join43(home, ".claude", "projects", slug, `${id}.jsonl`));
62036
62222
  } else if (harness === "cursor") {
62037
- const project = join42(home, ".cursor", "projects", slug);
62038
- candidates.push(join42(project, "agent-transcripts", id, `${id}.jsonl`), join42(project, "agent-transcripts", `${id}.jsonl`));
62223
+ const project = join43(home, ".cursor", "projects", slug);
62224
+ candidates.push(join43(project, "agent-transcripts", id, `${id}.jsonl`), join43(project, "agent-transcripts", `${id}.jsonl`));
62039
62225
  } else {
62040
62226
  return null;
62041
62227
  }
@@ -62047,7 +62233,7 @@ function reconstructSession(harness, id, slug, home = homedir13(), exists = exis
62047
62233
  }
62048
62234
  function readHead2(path) {
62049
62235
  try {
62050
- return readFileSync25(path, "utf-8").slice(0, 128 * 1024);
62236
+ return readFileSync26(path, "utf-8").slice(0, 128 * 1024);
62051
62237
  } catch {
62052
62238
  return null;
62053
62239
  }
@@ -62098,7 +62284,7 @@ function createSessionTitleResolver(configDir = getConfigDir(), deps = {}) {
62098
62284
  }
62099
62285
  };
62100
62286
  }
62101
- var CACHE_FILENAME5 = "session-titles.json", CACHE_SCHEMA_VERSION2 = 3, HEAD_LINES2 = 50, SESSION_NAME_MAX = 80;
62287
+ var CACHE_FILENAME6 = "session-titles.json", CACHE_SCHEMA_VERSION2 = 3, HEAD_LINES2 = 50, SESSION_NAME_MAX = 80;
62102
62288
  var init_session_title = __esm(() => {
62103
62289
  init_config();
62104
62290
  init_read();
@@ -62127,7 +62313,7 @@ var init_alt_screen = __esm(() => {
62127
62313
  });
62128
62314
 
62129
62315
  // src/tui/activity-view.ts
62130
- import { existsSync as existsSync27, readFileSync as readFileSync26 } from "node:fs";
62316
+ import { existsSync as existsSync28, readFileSync as readFileSync27 } from "node:fs";
62131
62317
  import { basename as basename5 } from "node:path";
62132
62318
  function reduceActivityViewKey(key) {
62133
62319
  if (key === "q" || key === ESC7 || key === CTRL_C4)
@@ -62268,15 +62454,15 @@ function statusLine(status) {
62268
62454
  if (status.running) {
62269
62455
  const since = status.startedAt ? ` · since ${localTime(status.startedAt)}` : "";
62270
62456
  const pid = status.pid !== undefined ? ` (pid ${status.pid})` : "";
62271
- return `\uD83D\uDCDA ${import_picocolors33.default.bold(brand("Studying sessions..."))}${import_picocolors33.default.dim(`${pid}${since}`)}`;
62457
+ return `\uD83D\uDCDA ${import_picocolors34.default.bold(brand("Studying sessions..."))}${import_picocolors34.default.dim(`${pid}${since}`)}`;
62272
62458
  }
62273
62459
  if (status.state.paused) {
62274
- return `${import_picocolors33.default.yellow("○")} ${import_picocolors33.default.bold("Paused")} ${import_picocolors33.default.dim("· studying stays off until you resume")}`;
62460
+ return `${import_picocolors34.default.yellow("○")} ${import_picocolors34.default.bold("Paused")} ${import_picocolors34.default.dim("· studying stays off until you resume")}`;
62275
62461
  }
62276
62462
  if (status.staleLock) {
62277
- return `${import_picocolors33.default.yellow("○")} ${import_picocolors33.default.bold("Not running")} ${import_picocolors33.default.dim("· a previous run exited without cleaning up")}`;
62463
+ return `${import_picocolors34.default.yellow("○")} ${import_picocolors34.default.bold("Not running")} ${import_picocolors34.default.dim("· a previous run exited without cleaning up")}`;
62278
62464
  }
62279
- return `${import_picocolors33.default.dim("○")} ${import_picocolors33.default.bold("Idle")} ${import_picocolors33.default.dim("· hooks study new sessions automatically")}`;
62465
+ return `${import_picocolors34.default.dim("○")} ${import_picocolors34.default.bold("Idle")} ${import_picocolors34.default.dim("· hooks study new sessions automatically")}`;
62280
62466
  }
62281
62467
  function progressLine(done, ready, width, notes = 0) {
62282
62468
  const total = done + ready;
@@ -62285,7 +62471,7 @@ function progressLine(done, ready, width, notes = 0) {
62285
62471
  const cells = Math.max(10, Math.min(30, width - 44));
62286
62472
  const ratio = Math.max(0, Math.min(1, done / total));
62287
62473
  const filled = Math.min(cells, Math.round(ratio * cells));
62288
- const bar = brand("█".repeat(filled)) + import_picocolors33.default.dim("░".repeat(cells - filled));
62474
+ const bar = brand("█".repeat(filled)) + import_picocolors34.default.dim("░".repeat(cells - filled));
62289
62475
  const pct = Math.floor(ratio * 100);
62290
62476
  const suffix = notes > 0 ? ` · ${notes} suggested page${notes === 1 ? "" : "s"}` : "";
62291
62477
  return `${bar} ${done}/${total} studied · ${pct}%${suffix}`;
@@ -62324,17 +62510,17 @@ function confirmBox(queuedCount, backlog, width, mode = "start") {
62324
62510
  const verb = mode === "clear" ? "clear" : mode === "stop" ? "stop" : mode === "resume" ? "resume" : "start";
62325
62511
  const maxInner = Math.max(20, Math.min(width, contentWidth()) - 4);
62326
62512
  const rows = [
62327
- import_picocolors33.default.bold(brand(title)),
62328
- ...wrapLine(scope, maxInner, "").map((line) => import_picocolors33.default.dim(line)),
62513
+ import_picocolors34.default.bold(brand(title)),
62514
+ ...wrapLine(scope, maxInner, "").map((line) => import_picocolors34.default.dim(line)),
62329
62515
  "",
62330
- `${import_picocolors33.default.bold("enter")} ${verb} · ${import_picocolors33.default.bold("esc")} cancel`
62516
+ `${import_picocolors34.default.bold("enter")} ${verb} · ${import_picocolors34.default.bold("esc")} cancel`
62331
62517
  ];
62332
62518
  const inner = Math.min(maxInner, Math.max(...rows.map(visibleWidth2)));
62333
62519
  const pad = (row) => row + " ".repeat(Math.max(0, inner - visibleWidth2(row)));
62334
62520
  return [
62335
- import_picocolors33.default.dim(`╭${"─".repeat(inner + 2)}╮`),
62336
- ...rows.map((row) => `${import_picocolors33.default.dim("│")} ${pad(row)} ${import_picocolors33.default.dim("│")}`),
62337
- import_picocolors33.default.dim(`╰${"─".repeat(inner + 2)}╯`)
62521
+ import_picocolors34.default.dim(`╭${"─".repeat(inner + 2)}╮`),
62522
+ ...rows.map((row) => `${import_picocolors34.default.dim("│")} ${pad(row)} ${import_picocolors34.default.dim("│")}`),
62523
+ import_picocolors34.default.dim(`╰${"─".repeat(inner + 2)}╯`)
62338
62524
  ];
62339
62525
  }
62340
62526
  function renderActivityFrame(status, activity, width, backlog = null, pane = DEFAULT_PANE, queued = [], studiedBeforeRun = 0, open = [], runProgress = null, projectNames = {}, sessionNames = {}) {
@@ -62350,7 +62536,7 @@ function renderActivityFrame(status, activity, width, backlog = null, pane = DEF
62350
62536
  progress = progressLine(studiedDelta + stepDone, Math.max(0, backlog.ready - stepDone), width, runProgress?.notes ?? 0);
62351
62537
  }
62352
62538
  const refusal = !status.running && status.state.last_refusal;
62353
- const refusalLines = refusal ? wrapLine(`! Studying paused: ${refusal.message} (${localTime(refusal.at)})`, width).map((line) => import_picocolors33.default.yellow(line)) : [];
62539
+ const refusalLines = refusal ? wrapLine(`! Studying paused: ${refusal.message} (${localTime(refusal.at)})`, width).map((line) => import_picocolors34.default.yellow(line)) : [];
62354
62540
  const fullRows = Boolean(pane.fullRows);
62355
62541
  const withName = (item, key) => projectNames[key] ? { ...item, project: projectNames[key] } : item;
62356
62542
  const latestPass = new Map;
@@ -62375,7 +62561,7 @@ function renderActivityFrame(status, activity, width, backlog = null, pane = DEF
62375
62561
  const height = fullRows ? ACTIVITY_VIEW_FULL_LIST_ROWS : ACTIVITY_VIEW_LIST_LINES;
62376
62562
  const { visible, above, below } = windowList(source, pane.scroll, height);
62377
62563
  const display = fullRows ? visible.flatMap((row) => wrapRow(row, width)) : visible;
62378
- const listRows = display.length > 0 ? display.map((row) => import_picocolors33.default.dim(row)) : [import_picocolors33.default.dim(empty)];
62564
+ const listRows = display.length > 0 ? display.map((row) => import_picocolors34.default.dim(row)) : [import_picocolors34.default.dim(empty)];
62379
62565
  const scrollParts = [];
62380
62566
  if (above > 0)
62381
62567
  scrollParts.push(`↑ ${above} earlier`);
@@ -62385,14 +62571,14 @@ function renderActivityFrame(status, activity, width, backlog = null, pane = DEF
62385
62571
  breadcrumb(["home", "activity"], width),
62386
62572
  "",
62387
62573
  statusLine(status),
62388
- import_picocolors33.default.dim(studied),
62389
- ...queueDetail.length > 0 ? wrapLine(queueDetail.join(" · "), width).map((line) => import_picocolors33.default.dim(line)) : [],
62574
+ import_picocolors34.default.dim(studied),
62575
+ ...queueDetail.length > 0 ? wrapLine(queueDetail.join(" · "), width).map((line) => import_picocolors34.default.dim(line)) : [],
62390
62576
  ...progress ? [progress] : [],
62391
62577
  ...refusalLines,
62392
62578
  "",
62393
62579
  ...tabBar(pane.tab, queued.length, open.length, latestPass.size, width),
62394
62580
  ...listRows,
62395
- ...scrollParts.length > 0 ? [import_picocolors33.default.dim(clip(scrollParts.join(" · "), width))] : [],
62581
+ ...scrollParts.length > 0 ? [import_picocolors34.default.dim(clip(scrollParts.join(" · "), width))] : [],
62396
62582
  "",
62397
62583
  ...pane.confirm ? centerBlock(confirmBox(queued.length, backlog, width, pane.confirm), width) : [
62398
62584
  ...wrapLine([
@@ -62402,7 +62588,7 @@ function renderActivityFrame(status, activity, width, backlog = null, pane = DEF
62402
62588
  status.running ? "s stop" : status.state.paused ? "s resume" : "s sync now",
62403
62589
  ...!status.running && status.state.watermark ? ["c clear"] : [],
62404
62590
  "esc back"
62405
- ].join(" · "), width, "").map((line) => import_picocolors33.default.dim(line))
62591
+ ].join(" · "), width, "").map((line) => import_picocolors34.default.dim(line))
62406
62592
  ]
62407
62593
  ];
62408
62594
  return lines.join(`
@@ -62436,7 +62622,7 @@ function createRowNamer() {
62436
62622
  names.titles[key] = t;
62437
62623
  };
62438
62624
  for (const s of [...backlog.queued, ...backlog.open]) {
62439
- if (!existsSync27(s.path))
62625
+ if (!existsSync28(s.path))
62440
62626
  continue;
62441
62627
  const key = `${s.harness}/${s.id}`;
62442
62628
  const dir = dirs.resolve(s);
@@ -62473,7 +62659,7 @@ function createRowNamer() {
62473
62659
  }
62474
62660
  function defaultReadLog2() {
62475
62661
  try {
62476
- return readFileSync26(logger.getLogPath(), "utf-8");
62662
+ return readFileSync27(logger.getLogPath(), "utf-8");
62477
62663
  } catch {
62478
62664
  return "";
62479
62665
  }
@@ -62654,7 +62840,7 @@ ${CLEAR_BELOW}`);
62654
62840
  input.on("data", onData);
62655
62841
  });
62656
62842
  }
62657
- var import_picocolors33, ESC7, HIDE_CURSOR3, SHOW_CURSOR3, CTRL_C4, KEY_UP4, KEY_DOWN4, KEY_RIGHT3, KEY_LEFT3, CURSOR_HOME, CLEAR_BELOW, CLEAR_EOL, ACTIVITY_VIEW_POLL_MS = 500, ACTIVITY_VIEW_LIST_LINES = 10, ACTIVITY_VIEW_FULL_LIST_ROWS = 5, ACTIVITY_VIEW_BUFFER_LINES = 200, ACTIVITY_VIEW_TABS, DEFAULT_PANE, TITLE_READS_PER_DRAW = 25;
62843
+ var import_picocolors34, ESC7, HIDE_CURSOR3, SHOW_CURSOR3, CTRL_C4, KEY_UP4, KEY_DOWN4, KEY_RIGHT3, KEY_LEFT3, CURSOR_HOME, CLEAR_BELOW, CLEAR_EOL, ACTIVITY_VIEW_POLL_MS = 500, ACTIVITY_VIEW_LIST_LINES = 10, ACTIVITY_VIEW_FULL_LIST_ROWS = 5, ACTIVITY_VIEW_BUFFER_LINES = 200, ACTIVITY_VIEW_TABS, DEFAULT_PANE, TITLE_READS_PER_DRAW = 25;
62658
62844
  var init_activity_view = __esm(() => {
62659
62845
  init_follow();
62660
62846
  init_logger();
@@ -62669,7 +62855,7 @@ var init_activity_view = __esm(() => {
62669
62855
  init_alt_screen();
62670
62856
  init_layout();
62671
62857
  init_menu();
62672
- import_picocolors33 = __toESM(require_picocolors(), 1);
62858
+ import_picocolors34 = __toESM(require_picocolors(), 1);
62673
62859
  ESC7 = String.fromCharCode(27);
62674
62860
  HIDE_CURSOR3 = `${ESC7}[?25l`;
62675
62861
  SHOW_CURSOR3 = `${ESC7}[?25h`;
@@ -63450,6 +63636,7 @@ async function runSetupFlow(opts = {}) {
63450
63636
  }));
63451
63637
  return;
63452
63638
  }
63639
+ writeMcpRefreshCache({ version: VERSION });
63453
63640
  const configuredProviders = configured.filter((result) => (result.action === "install" || result.action === "skip") && !result.error);
63454
63641
  const mcpCompleted = configuredProviders.length > 0;
63455
63642
  if (mcpCompleted) {
@@ -63894,25 +64081,14 @@ async function runSwitchTarget(scope = "org") {
63894
64081
  return;
63895
64082
  updateTarget(cfg, { api_key: apiKey });
63896
64083
  saveConfig(cfg);
63897
- const configured = allSetupProviders().filter((provider) => {
63898
- try {
63899
- return provider.isInstalled() && provider.isConfigured();
63900
- } catch {
63901
- return false;
63902
- }
63903
- });
63904
- for (const provider of configured) {
63905
- try {
63906
- provider.install(cfg, true);
63907
- logger.info("setup", `Switch: updated ${provider.name()}`);
63908
- log2.success(`${provider.name()} ${dim("· updated")}`);
63909
- } catch (err) {
63910
- const error = err instanceof Error ? err : new Error(String(err));
63911
- logger.error("setup", `Switch: update failed for ${provider.name()}: ${error.message}`);
63912
- log2.error(`Failed to update ${provider.name()}: ${error.message}`);
63913
- }
64084
+ const refreshed = refreshConfiguredProviders(cfg);
64085
+ for (const provider of refreshed.updated) {
64086
+ log2.success(`${provider.name()} ${dim("· updated")}`);
64087
+ }
64088
+ for (const { provider, error } of refreshed.failed) {
64089
+ log2.error(`Failed to update ${provider.name()}: ${error.message}`);
63914
64090
  }
63915
- if (configured.length > 0) {
64091
+ if (refreshed.updated.length + refreshed.failed.length > 0) {
63916
64092
  log2.info("Restart your AI agents so they pick up the new MCP target.");
63917
64093
  }
63918
64094
  }
@@ -64162,7 +64338,7 @@ async function stepSelectTools(detected) {
64162
64338
  initialValues: preselected,
64163
64339
  statusFor: (id, picked) => {
64164
64340
  if (configuredMap.get(id))
64165
- return picked ? dim("configured") : import_picocolors34.default.yellow("will remove");
64341
+ return picked ? dim("configured") : import_picocolors35.default.yellow("will remove");
64166
64342
  return picked ? brand("will configure") : undefined;
64167
64343
  },
64168
64344
  summary: (picked) => {
@@ -64337,7 +64513,7 @@ ${dim(wrapLog("Run it inside a session to keep that session out of studying. Rem
64337
64513
  log2.success("All agents already configured. No changes needed.");
64338
64514
  }
64339
64515
  }
64340
- var import_picocolors34, OAUTH_ANALYTICS_REASONS, SETUP_HANDSHAKE_TIMEOUT_MS = 1800000, LIBRARY_SELECT_CANCELLED;
64516
+ var import_picocolors35, OAUTH_ANALYTICS_REASONS, SETUP_HANDSHAKE_TIMEOUT_MS = 1800000, LIBRARY_SELECT_CANCELLED;
64341
64517
  var init_flow2 = __esm(() => {
64342
64518
  init_dist();
64343
64519
  init_errors3();
@@ -64348,6 +64524,7 @@ var init_flow2 = __esm(() => {
64348
64524
  init_agents();
64349
64525
  init_agents3();
64350
64526
  init_providers();
64527
+ init_refresh();
64351
64528
  init_agents4();
64352
64529
  init_detach();
64353
64530
  init_sync();
@@ -64355,12 +64532,13 @@ var init_flow2 = __esm(() => {
64355
64532
  init_activity_view();
64356
64533
  init_layout();
64357
64534
  init_prompts();
64535
+ init_mcp_refresh_check();
64358
64536
  init_agents_md_step();
64359
64537
  init_analytics2();
64360
64538
  init_github_step();
64361
64539
  init_rules_step();
64362
64540
  init_styles();
64363
- import_picocolors34 = __toESM(require_picocolors(), 1);
64541
+ import_picocolors35 = __toESM(require_picocolors(), 1);
64364
64542
  OAUTH_ANALYTICS_REASONS = new Set([
64365
64543
  "access_denied",
64366
64544
  "bad_oauth_state",
@@ -64488,7 +64666,7 @@ function renderAnalyticsFrame(state, tab, scroll, pageStats = null, pagesPending
64488
64666
  const rows = analyticsTabRows(tab, state, pageStats);
64489
64667
  const { visible, above, below } = windowReport(rows, scroll);
64490
64668
  const empty = tab === "overview" ? "No analytics yet. They appear after the first study run." : tab === "projects" ? "No per-project history yet. It fills in as sessions are studied." : pagesPending ? "Loading page analytics..." : "No page analytics yet.";
64491
- const listRows = visible.length > 0 ? visible.map((row) => import_picocolors35.default.dim(row)) : [import_picocolors35.default.dim(empty)];
64669
+ const listRows = visible.length > 0 ? visible.map((row) => import_picocolors36.default.dim(row)) : [import_picocolors36.default.dim(empty)];
64492
64670
  const scrollParts = [];
64493
64671
  if (above > 0)
64494
64672
  scrollParts.push(`↑ ${above} earlier`);
@@ -64504,9 +64682,9 @@ function renderAnalyticsFrame(state, tab, scroll, pageStats = null, pagesPending
64504
64682
  ], tab, width, { spread: false }),
64505
64683
  "",
64506
64684
  ...listRows,
64507
- ...scrollParts.length > 0 ? [import_picocolors35.default.dim(scrollParts.join(" · "))] : [],
64685
+ ...scrollParts.length > 0 ? [import_picocolors36.default.dim(scrollParts.join(" · "))] : [],
64508
64686
  "",
64509
- import_picocolors35.default.dim("tab switch · ↑↓ scroll · esc back")
64687
+ import_picocolors36.default.dim("tab switch · ↑↓ scroll · esc back")
64510
64688
  ];
64511
64689
  return lines.join(`
64512
64690
  `);
@@ -64601,7 +64779,7 @@ ${CLEAR_BELOW2}`);
64601
64779
  input.on("data", onData);
64602
64780
  });
64603
64781
  }
64604
- var import_picocolors35, ESC8, HIDE_CURSOR4, SHOW_CURSOR4, CTRL_C5, KEY_UP5, KEY_DOWN5, KEY_RIGHT4, KEY_LEFT4, CURSOR_HOME2, CLEAR_BELOW2, CLEAR_EOL2, ANALYTICS_VIEW_POLL_MS = 1000, ANALYTICS_VIEW_LINES = 14, ANALYTICS_VIEW_TABS, TOP_PAGES_LIMIT = 5, TOP_CITED_DAYS = 30, label = (text) => text.padEnd(26);
64782
+ var import_picocolors36, ESC8, HIDE_CURSOR4, SHOW_CURSOR4, CTRL_C5, KEY_UP5, KEY_DOWN5, KEY_RIGHT4, KEY_LEFT4, CURSOR_HOME2, CLEAR_BELOW2, CLEAR_EOL2, ANALYTICS_VIEW_POLL_MS = 1000, ANALYTICS_VIEW_LINES = 14, ANALYTICS_VIEW_TABS, TOP_PAGES_LIMIT = 5, TOP_CITED_DAYS = 30, label = (text) => text.padEnd(26);
64605
64783
  var init_analytics_view = __esm(() => {
64606
64784
  init_trpc();
64607
64785
  init_config();
@@ -64609,7 +64787,7 @@ var init_analytics_view = __esm(() => {
64609
64787
  init_alt_screen();
64610
64788
  init_layout();
64611
64789
  init_menu();
64612
- import_picocolors35 = __toESM(require_picocolors(), 1);
64790
+ import_picocolors36 = __toESM(require_picocolors(), 1);
64613
64791
  ESC8 = String.fromCharCode(27);
64614
64792
  HIDE_CURSOR4 = `${ESC8}[?25l`;
64615
64793
  SHOW_CURSOR4 = `${ESC8}[?25h`;
@@ -64627,7 +64805,7 @@ var init_analytics_view = __esm(() => {
64627
64805
  // src/tui/banner.ts
64628
64806
  function paintLogoRow(row) {
64629
64807
  return row.map(([tone, art]) => {
64630
- if (!import_picocolors36.default.isColorSupported)
64808
+ if (!import_picocolors37.default.isColorSupported)
64631
64809
  return art;
64632
64810
  const { fg, fallback } = LOGO_TONES[tone];
64633
64811
  return hasTruecolor() ? `${fg}${art}\x1B[39m` : fallback(art);
@@ -64635,14 +64813,14 @@ function paintLogoRow(row) {
64635
64813
  }
64636
64814
  function checklistRows(ctx) {
64637
64815
  const on = brand(CHECK2);
64638
- const off = import_picocolors36.default.dim(CIRCLE);
64816
+ const off = import_picocolors37.default.dim(CIRCLE);
64639
64817
  const rows = [["workspace", ctx.directory]];
64640
64818
  rows.push([
64641
64819
  "account",
64642
- ctx.sessionExpired ? `${off} ${import_picocolors36.default.yellow("session expired")} ${import_picocolors36.default.dim(`${DOT3} run Log in`)}` : ctx.signedIn ? `${on} signed in` : `${off} ${import_picocolors36.default.dim(`not signed in ${DOT3} run Setup`)}`
64820
+ ctx.sessionExpired ? `${off} ${import_picocolors37.default.yellow("session expired")} ${import_picocolors37.default.dim(`${DOT3} run Log in`)}` : ctx.signedIn ? `${on} signed in` : `${off} ${import_picocolors37.default.dim(`not signed in ${DOT3} run Setup`)}`
64643
64821
  ]);
64644
64822
  const missing = new Set(ctx.setupMissing ?? []);
64645
- const warnRow = `${off} ${import_picocolors36.default.yellow("not configured")} ${import_picocolors36.default.dim(`${DOT3} run Setup`)}`;
64823
+ const warnRow = `${off} ${import_picocolors37.default.yellow("not configured")} ${import_picocolors37.default.dim(`${DOT3} run Setup`)}`;
64646
64824
  if (missing.has("MCP"))
64647
64825
  rows.push(["mcp", warnRow]);
64648
64826
  else if (ctx.deploymentName)
@@ -64654,7 +64832,7 @@ function checklistRows(ctx) {
64654
64832
  if (ctx.repoAgentsMd) {
64655
64833
  rows.push([
64656
64834
  "repo",
64657
- ctx.repoAgentsMd === "current" ? `${on} AGENTS.md has the Dosu section` : `${off} ${import_picocolors36.default.yellow(ctx.repoAgentsMd === "outdated" ? "AGENTS.md Dosu section outdated" : "AGENTS.md missing the Dosu section")} ${import_picocolors36.default.dim(`${DOT3} run Setup`)}`
64835
+ ctx.repoAgentsMd === "current" ? `${on} AGENTS.md has the Dosu section` : `${off} ${import_picocolors37.default.yellow(ctx.repoAgentsMd === "outdated" ? "AGENTS.md Dosu section outdated" : "AGENTS.md missing the Dosu section")} ${import_picocolors37.default.dim(`${DOT3} run Setup`)}`
64658
64836
  ]);
64659
64837
  }
64660
64838
  if (missing.has("agents"))
@@ -64666,24 +64844,24 @@ function checklistRows(ctx) {
64666
64844
  if (ctx.studying) {
64667
64845
  rows.push([
64668
64846
  "sync",
64669
- `\uD83D\uDCDA ${brand("studying sessions...")} ${import_picocolors36.default.dim(`${DOT3} see activity`)}`
64847
+ `\uD83D\uDCDA ${brand("studying sessions...")} ${import_picocolors37.default.dim(`${DOT3} see activity`)}`
64670
64848
  ]);
64671
64849
  }
64672
64850
  if (ctx.update) {
64673
64851
  rows.push([
64674
64852
  "update",
64675
- `${import_picocolors36.default.yellow(`↑ ${ctx.update.version} available`)} ${import_picocolors36.default.dim(`${DOT3} ${ctx.update.hint}`)}`
64853
+ `${import_picocolors37.default.yellow(`↑ ${ctx.update.version} available`)} ${import_picocolors37.default.dim(`${DOT3} ${ctx.update.hint}`)}`
64676
64854
  ]);
64677
64855
  }
64678
64856
  const labelWidth = Math.max(...rows.map(([label]) => label.length));
64679
- return rows.map(([label, value]) => `${import_picocolors36.default.dim(label.padEnd(labelWidth))} ${value}`);
64857
+ return rows.map(([label, value]) => `${import_picocolors37.default.dim(label.padEnd(labelWidth))} ${value}`);
64680
64858
  }
64681
64859
  function renderBanner(ctx) {
64682
64860
  const logo = LOGO_ROWS.map(paintLogoRow);
64683
64861
  const text = [
64684
64862
  ...checklistRows(ctx),
64685
64863
  "",
64686
- `${brandBadge("dosu-cli")} ${import_picocolors36.default.dim(`${ctx.version} ${DOT3} ${ctx.webAppHost}`)}`
64864
+ `${brandBadge("dosu-cli")} ${import_picocolors37.default.dim(`${ctx.version} ${DOT3} ${ctx.webAppHost}`)}`
64687
64865
  ];
64688
64866
  const height = Math.max(logo.length, text.length);
64689
64867
  const combined = [];
@@ -64696,10 +64874,10 @@ function renderBanner(ctx) {
64696
64874
  return ["", ...combined, ""].join(`
64697
64875
  `);
64698
64876
  }
64699
- var import_picocolors36, CHECK2 = "✔", CIRCLE = "○", DOT3 = "·", LOGO_ROWS, LOGO_MARK, LOGO_WIDTH, LOGO_TONES, COLUMN_GAP = " ";
64877
+ var import_picocolors37, CHECK2 = "✔", CIRCLE = "○", DOT3 = "·", LOGO_ROWS, LOGO_MARK, LOGO_WIDTH, LOGO_TONES, COLUMN_GAP = " ";
64700
64878
  var init_banner = __esm(() => {
64701
64879
  init_styles();
64702
- import_picocolors36 = __toESM(require_picocolors(), 1);
64880
+ import_picocolors37 = __toESM(require_picocolors(), 1);
64703
64881
  LOGO_ROWS = [
64704
64882
  [["sage", "▄▄▄▄▄▄"]],
64705
64883
  [["sage", "██ ▀▄"]],
@@ -64710,8 +64888,8 @@ var init_banner = __esm(() => {
64710
64888
  LOGO_MARK = LOGO_ROWS.map((row) => row.map(([, art]) => art).join(""));
64711
64889
  LOGO_WIDTH = Math.max(...LOGO_MARK.map((row) => row.length));
64712
64890
  LOGO_TONES = {
64713
- sage: { fg: "\x1B[38;2;180;187;145m", fallback: import_picocolors36.default.green },
64714
- moss: { fg: "\x1B[38;2;119;133;97m", fallback: (art) => import_picocolors36.default.dim(import_picocolors36.default.green(art)) }
64891
+ sage: { fg: "\x1B[38;2;180;187;145m", fallback: import_picocolors37.default.green },
64892
+ moss: { fg: "\x1B[38;2;119;133;97m", fallback: (art) => import_picocolors37.default.dim(import_picocolors37.default.green(art)) }
64715
64893
  };
64716
64894
  });
64717
64895
 
@@ -64823,16 +65001,16 @@ function renderPagesFrame(model, width, selected) {
64823
65001
  const body = [];
64824
65002
  let scrollHint = "";
64825
65003
  if (model.phase === "loading") {
64826
- body.push(import_picocolors37.default.dim("Loading pages…"));
65004
+ body.push(import_picocolors38.default.dim("Loading pages…"));
64827
65005
  } else if (model.phase === "error") {
64828
- body.push(import_picocolors37.default.yellow(clip3(model.message, width)));
65006
+ body.push(import_picocolors38.default.yellow(clip3(model.message, width)));
64829
65007
  } else if (model.pages.length === 0) {
64830
- body.push(import_picocolors37.default.dim("No pages in this Library yet."));
65008
+ body.push(import_picocolors38.default.dim("No pages in this Library yet."));
64831
65009
  } else {
64832
65010
  const rows = pageRows2(model.pages, width - 2);
64833
65011
  const { start, above } = windowSelection(rows.length, selected);
64834
65012
  const visible = rows.slice(start, start + PAGES_VIEW_LINES);
64835
- body.push(...visible.map((row, i) => start + i === selected ? `${brand("▸")} ${import_picocolors37.default.bold(row)}` : ` ${import_picocolors37.default.dim(row)}`));
65013
+ body.push(...visible.map((row, i) => start + i === selected ? `${brand("▸")} ${import_picocolors38.default.bold(row)}` : ` ${import_picocolors38.default.dim(row)}`));
64836
65014
  const below = Math.max(0, Math.max(model.total, rows.length) - start - PAGES_VIEW_LINES);
64837
65015
  const parts = [];
64838
65016
  if (above > 0)
@@ -64841,14 +65019,14 @@ function renderPagesFrame(model, width, selected) {
64841
65019
  parts.push(`↓ ${below} more`);
64842
65020
  scrollHint = parts.join(" · ");
64843
65021
  }
64844
- const count = model.phase === "ready" ? ` ${import_picocolors37.default.dim(model.pages.length < model.total ? `(${model.pages.length} of ${model.total})` : `(${model.pages.length})`)}` : "";
65022
+ const count = model.phase === "ready" ? ` ${import_picocolors38.default.dim(model.pages.length < model.total ? `(${model.pages.length} of ${model.total})` : `(${model.pages.length})`)}` : "";
64845
65023
  const lines = [
64846
65024
  `${breadcrumb(["home", "pages"], width)}${count}`,
64847
65025
  "",
64848
65026
  ...body,
64849
- ...scrollHint ? [import_picocolors37.default.dim(scrollHint)] : [],
65027
+ ...scrollHint ? [import_picocolors38.default.dim(scrollHint)] : [],
64850
65028
  "",
64851
- import_picocolors37.default.dim("↑↓ move · enter open · esc back")
65029
+ import_picocolors38.default.dim("↑↓ move · enter open · esc back")
64852
65030
  ];
64853
65031
  return lines.join(`
64854
65032
  `);
@@ -64857,9 +65035,9 @@ function renderPageFrame(page, content, width, scroll) {
64857
65035
  const body = [];
64858
65036
  let scrollHint = "";
64859
65037
  if (content.phase === "loading") {
64860
- body.push(import_picocolors37.default.dim("Loading page…"));
65038
+ body.push(import_picocolors38.default.dim("Loading page…"));
64861
65039
  } else if (content.phase === "error") {
64862
- body.push(import_picocolors37.default.yellow(clip3(content.message, width)));
65040
+ body.push(import_picocolors38.default.yellow(clip3(content.message, width)));
64863
65041
  } else {
64864
65042
  const rows = wrapBody(content.body, width);
64865
65043
  const { visible, above, below } = windowReport(rows, scroll, PAGE_BODY_LINES);
@@ -64874,12 +65052,12 @@ function renderPageFrame(page, content, width, scroll) {
64874
65052
  const meta = `${page.type} · ${page.published ? "published" : "draft"} · updated ${page.updated_at.slice(0, 10)}`;
64875
65053
  const lines = [
64876
65054
  breadcrumb(["home", "pages", page.title], width),
64877
- import_picocolors37.default.dim(clip3(meta, width)),
65055
+ import_picocolors38.default.dim(clip3(meta, width)),
64878
65056
  "",
64879
65057
  ...body,
64880
- ...scrollHint ? [import_picocolors37.default.dim(scrollHint)] : [],
65058
+ ...scrollHint ? [import_picocolors38.default.dim(scrollHint)] : [],
64881
65059
  "",
64882
- import_picocolors37.default.dim("↑↓ scroll · esc back to pages")
65060
+ import_picocolors38.default.dim("↑↓ scroll · esc back to pages")
64883
65061
  ];
64884
65062
  return lines.join(`
64885
65063
  `);
@@ -65028,7 +65206,7 @@ ${CLEAR_BELOW3}`);
65028
65206
  input.on("data", onData);
65029
65207
  });
65030
65208
  }
65031
- var import_picocolors37, ESC9, HIDE_CURSOR5, SHOW_CURSOR5, CTRL_C6, KEY_UP6, KEY_DOWN6, CURSOR_HOME3, CLEAR_BELOW3, CLEAR_EOL3, PAGES_VIEW_LINES = 12, PAGE_BODY_LINES = 14, PAGES_FETCH_BATCH = 100;
65209
+ var import_picocolors38, ESC9, HIDE_CURSOR5, SHOW_CURSOR5, CTRL_C6, KEY_UP6, KEY_DOWN6, CURSOR_HOME3, CLEAR_BELOW3, CLEAR_EOL3, PAGES_VIEW_LINES = 12, PAGE_BODY_LINES = 14, PAGES_FETCH_BATCH = 100;
65032
65210
  var init_pages_view = __esm(() => {
65033
65211
  init_trpc();
65034
65212
  init_config();
@@ -65038,7 +65216,7 @@ var init_pages_view = __esm(() => {
65038
65216
  init_analytics_view();
65039
65217
  init_layout();
65040
65218
  init_menu();
65041
- import_picocolors37 = __toESM(require_picocolors(), 1);
65219
+ import_picocolors38 = __toESM(require_picocolors(), 1);
65042
65220
  ESC9 = String.fromCharCode(27);
65043
65221
  HIDE_CURSOR5 = `${ESC9}[?25l`;
65044
65222
  SHOW_CURSOR5 = `${ESC9}[?25h`;
@@ -65152,7 +65330,7 @@ function isSetUp(cfg) {
65152
65330
  }
65153
65331
  function setupHint(cfg) {
65154
65332
  const started = Boolean(cfg.active_account?.target);
65155
- return import_picocolors38.default.yellow(started ? `incomplete · missing ${missingSetupSteps(cfg).join(" + ")}` : "not set up yet");
65333
+ return import_picocolors39.default.yellow(started ? `incomplete · missing ${missingSetupSteps(cfg).join(" + ")}` : "not set up yet");
65156
65334
  }
65157
65335
  function isStudying() {
65158
65336
  try {
@@ -65398,7 +65576,7 @@ function handleLogout(cfg) {
65398
65576
  saveConfig(cfg);
65399
65577
  log2.success("Credentials cleared.");
65400
65578
  }
65401
- var import_picocolors38, ESC10, CLEAR_SCREEN;
65579
+ var import_picocolors39, ESC10, CLEAR_SCREEN;
65402
65580
  var init_tui = __esm(() => {
65403
65581
  init_client();
65404
65582
  init_config();
@@ -65421,7 +65599,7 @@ var init_tui = __esm(() => {
65421
65599
  init_menu();
65422
65600
  init_pages_view();
65423
65601
  init_prompts();
65424
- import_picocolors38 = __toESM(require_picocolors(), 1);
65602
+ import_picocolors39 = __toESM(require_picocolors(), 1);
65425
65603
  ESC10 = String.fromCharCode(27);
65426
65604
  CLEAR_SCREEN = `${ESC10}[2J${ESC10}[H`;
65427
65605
  });
@@ -66025,18 +66203,15 @@ var init_flow3 = __esm(() => {
66025
66203
  });
66026
66204
 
66027
66205
  // src/cli/cli.ts
66028
- import {
66029
- closeSync as closeSync6,
66030
- constants as constants3,
66031
- fstatSync as fstatSync2,
66032
- openSync as openSync6,
66033
- readFileSync as readFileSync27,
66034
- readSync as readSync5,
66035
- unlinkSync as unlinkSync6
66036
- } from "node:fs";
66206
+ import { readFileSync as readFileSync28, unlinkSync as unlinkSync6 } from "node:fs";
66037
66207
  function shouldRunBackgroundChecks(actionName) {
66038
66208
  return actionName !== "upgrade";
66039
66209
  }
66210
+ function shouldRunMcpRefreshCheck(actionCommand) {
66211
+ if (actionCommand.name() === "setup")
66212
+ return false;
66213
+ return !(actionCommand.name() === "refresh" && actionCommand.parent?.name() === "mcp");
66214
+ }
66040
66215
  function commandTelemetryName(actionCommand) {
66041
66216
  const segments = [];
66042
66217
  let current = actionCommand;
@@ -66053,7 +66228,7 @@ function shouldTrackCommand(command) {
66053
66228
  }
66054
66229
  function commandTelemetryContext() {
66055
66230
  try {
66056
- const cfg = loadConfigForTelemetry();
66231
+ const cfg = loadConfigNonBlocking();
66057
66232
  if (!cfg)
66058
66233
  return { mode: "cloud", isAuthenticated: false };
66059
66234
  const authenticated = isAuthenticated(cfg);
@@ -66073,29 +66248,6 @@ function commandTelemetryContext() {
66073
66248
  return { mode: "cloud", isAuthenticated: false };
66074
66249
  }
66075
66250
  }
66076
- function loadConfigForTelemetry() {
66077
- let fd;
66078
- try {
66079
- const nonblocking = typeof constants3.O_NONBLOCK === "number" ? constants3.O_NONBLOCK : 0;
66080
- fd = openSync6(getConfigPath(), constants3.O_RDONLY | nonblocking);
66081
- const file = fstatSync2(fd);
66082
- if (!file.isFile() || file.size > MAX_TELEMETRY_CONFIG_BYTES)
66083
- return;
66084
- const content = Buffer.alloc(MAX_TELEMETRY_CONFIG_BYTES + 1);
66085
- const bytesRead = readSync5(fd, content, 0, content.byteLength, 0);
66086
- if (bytesRead > MAX_TELEMETRY_CONFIG_BYTES)
66087
- return;
66088
- return parseConfig(JSON.parse(content.subarray(0, bytesRead).toString("utf8")));
66089
- } catch {
66090
- return;
66091
- } finally {
66092
- if (fd !== undefined) {
66093
- try {
66094
- closeSync6(fd);
66095
- } catch {}
66096
- }
66097
- }
66098
- }
66099
66251
  function startTelemetry(telemetry, command, context) {
66100
66252
  if (!telemetry)
66101
66253
  return false;
@@ -66154,12 +66306,15 @@ function createProgram(options = {}) {
66154
66306
  const opts = thisCommand.optsWithGlobals();
66155
66307
  logger.init({ debug: opts.debug });
66156
66308
  if (shouldRunBackgroundChecks(actionCommand.name())) {
66309
+ const launchesTUI = actionCommand.parent === null;
66157
66310
  if (!process.env.CI) {
66158
- const launchesTUI = actionCommand.parent === null;
66159
66311
  await checkForUpdates({ notify: !launchesTUI });
66160
66312
  }
66161
66313
  checkForSkillUpdates();
66162
66314
  checkForReadyTasks();
66315
+ if (shouldRunMcpRefreshCheck(actionCommand)) {
66316
+ checkForMcpRefresh({ notify: !launchesTUI });
66317
+ }
66163
66318
  }
66164
66319
  const command = commandTelemetryName(actionCommand);
66165
66320
  if (options.telemetry && shouldTrackCommand(command)) {
@@ -66386,6 +66541,31 @@ Start ${provider.name()} in any project to use the Dosu MCP.`);
66386
66541
  Start ${provider.name()} in this project directory to use the Dosu MCP.`);
66387
66542
  }
66388
66543
  });
66544
+ mcp.command("refresh").description("Rewrite the Dosu MCP entry in every configured AI tool from the current setup").action(() => {
66545
+ const cfg = loadConfig();
66546
+ if (!canRefreshMcp(cfg)) {
66547
+ throw new CliUsageError("Dosu is not set up yet. Run 'dosu setup' first");
66548
+ }
66549
+ const configured = configuredProviders();
66550
+ if (configured.length === 0) {
66551
+ console.log("No AI tools with Dosu configured. Run 'dosu setup' to add Dosu to a tool.");
66552
+ return;
66553
+ }
66554
+ console.log(`Refreshing Dosu MCP config for configured AI tools...
66555
+ `);
66556
+ const result = refreshConfiguredProviders(cfg);
66557
+ writeMcpRefreshCache({ version: VERSION });
66558
+ for (const provider of result.updated) {
66559
+ console.log(` ✓ ${provider.name()}`);
66560
+ }
66561
+ for (const { provider, error } of result.failed) {
66562
+ console.log(` ✗ ${provider.name()}: ${error.message}`);
66563
+ }
66564
+ console.log(`
66565
+ Restart your AI agents so they pick up the change.`);
66566
+ if (result.failed.length > 0)
66567
+ process.exitCode = 1;
66568
+ });
66389
66569
  mcp.command("list").description("List available AI tools").action(() => {
66390
66570
  console.log(`Available AI tools:
66391
66571
  `);
@@ -66466,7 +66646,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
66466
66646
  }
66467
66647
  if (opts.follow) {
66468
66648
  try {
66469
- const lines = readFileSync27(logPath, "utf-8").split(`
66649
+ const lines = readFileSync28(logPath, "utf-8").split(`
66470
66650
  `);
66471
66651
  if (lines.at(-1) === "")
66472
66652
  lines.pop();
@@ -66482,7 +66662,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
66482
66662
  if (opts.tail !== undefined) {
66483
66663
  const n = typeof opts.tail === "string" ? parseInt(opts.tail, 10) || 50 : 50;
66484
66664
  try {
66485
- const content = readFileSync27(logPath, "utf-8");
66665
+ const content = readFileSync28(logPath, "utf-8");
66486
66666
  const lines = content.split(`
66487
66667
  `);
66488
66668
  console.log(lines.slice(-n).join(`
@@ -66534,7 +66714,7 @@ function processCommandTelemetry() {
66534
66714
  return;
66535
66715
  }
66536
66716
  }
66537
- var TELEMETRY_FLUSH_TIMEOUT_MS = 750, MAX_TELEMETRY_CONFIG_BYTES, CliUsageError, FOLLOW_POLL_MS = 500, DEFAULT_TAIL_LINES = 50;
66717
+ var TELEMETRY_FLUSH_TIMEOUT_MS = 750, CliUsageError, FOLLOW_POLL_MS = 500, DEFAULT_TAIL_LINES = 50;
66538
66718
  var init_cli = __esm(() => {
66539
66719
  init_commander();
66540
66720
  init_client();
@@ -66560,13 +66740,14 @@ var init_cli = __esm(() => {
66560
66740
  init_follow();
66561
66741
  init_logger();
66562
66742
  init_providers();
66743
+ init_refresh();
66563
66744
  init_styles();
66564
66745
  init_settings();
66565
66746
  init_telemetry();
66747
+ init_mcp_refresh_check();
66566
66748
  init_pending_tasks_check();
66567
66749
  init_skill_update_check();
66568
66750
  init_update_check();
66569
- MAX_TELEMETRY_CONFIG_BYTES = 64 * 1024;
66570
66751
  CliUsageError = class CliUsageError extends Error {
66571
66752
  exitCode = 1;
66572
66753
  constructor(message) {
@@ -66607,4 +66788,4 @@ main().catch((err) => {
66607
66788
  process.exit(1);
66608
66789
  });
66609
66790
 
66610
- //# debugId=93db7db7-907f-4d14-6475-6e2164756e21
66791
+ //# debugId=6712b11a-8117-30cb-6475-6e2164756e21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dosu/cli",
3
- "version": "0.56.1",
3
+ "version": "0.57.0",
4
4
  "type": "module",
5
5
  "description": "Dosu CLI - Manage MCP servers for AI tools",
6
6
  "license": "MIT",