@dosu/cli 0.56.1 → 0.57.1

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 +417 -211
  3. package/package.json +1 -1
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.1", 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);
@@ -8515,6 +8548,9 @@ function layoutMargin(columns = process.stdout.columns ?? 80) {
8515
8548
  function frameTopMargin(rows = process.stdout.rows ?? 24) {
8516
8549
  return Math.max(2, Math.min(6, Math.floor(rows / 8)));
8517
8550
  }
8551
+ function frameMaxLines(rows = process.stdout.rows ?? 24) {
8552
+ return Math.max(1, rows - frameTopMargin(rows) - 2);
8553
+ }
8518
8554
  function breadcrumb(segments, width = contentWidth()) {
8519
8555
  const head = segments.slice(0, -1);
8520
8556
  const leaf = segments[segments.length - 1] ?? "";
@@ -11821,12 +11857,12 @@ var init_integrations = __esm(() => {
11821
11857
 
11822
11858
  // src/sessions/project-dir.ts
11823
11859
  import {
11824
- closeSync as closeSync3,
11860
+ closeSync as closeSync4,
11825
11861
  existsSync as existsSync11,
11826
11862
  mkdirSync as mkdirSync7,
11827
- openSync as openSync3,
11863
+ openSync as openSync4,
11828
11864
  readFileSync as readFileSync11,
11829
- readSync as readSync2,
11865
+ readSync as readSync3,
11830
11866
  renameSync as renameSync3,
11831
11867
  statSync as statSync4,
11832
11868
  writeFileSync as writeFileSync7
@@ -11859,18 +11895,18 @@ function saveCacheFile(configDir, entries) {
11859
11895
  function readHead(path, bytes = HEAD_BYTES) {
11860
11896
  let fd;
11861
11897
  try {
11862
- fd = openSync3(path, "r");
11898
+ fd = openSync4(path, "r");
11863
11899
  } catch {
11864
11900
  return null;
11865
11901
  }
11866
11902
  try {
11867
11903
  const buf = Buffer.alloc(bytes);
11868
- const read = readSync2(fd, buf, 0, bytes, 0);
11904
+ const read = readSync3(fd, buf, 0, bytes, 0);
11869
11905
  return buf.subarray(0, read).toString("utf-8");
11870
11906
  } catch {
11871
11907
  return null;
11872
11908
  } finally {
11873
- closeSync3(fd);
11909
+ closeSync4(fd);
11874
11910
  }
11875
11911
  }
11876
11912
  function cwdFromJsonlHead(text) {
@@ -13680,7 +13716,7 @@ var init_sync = __esm(() => {
13680
13716
  });
13681
13717
 
13682
13718
  // src/telemetry/debug-id.ts
13683
- var BUNDLE_DEBUG_ID_PLACEHOLDER = "93db7db7-907f-4d14-6475-6e2164756e21";
13719
+ var BUNDLE_DEBUG_ID_PLACEHOLDER = "ca9e58ba-ed30-6875-6475-6e2164756e21";
13684
13720
 
13685
13721
  // src/telemetry/telemetry.ts
13686
13722
  import { randomUUID as randomUUID2 } from "node:crypto";
@@ -15205,7 +15241,7 @@ var init_env = __esm(() => {
15205
15241
  });
15206
15242
 
15207
15243
  // src/learner/executable.ts
15208
- import { accessSync, constants, existsSync as existsSync16 } from "node:fs";
15244
+ import { accessSync, constants as constants2, existsSync as existsSync16 } from "node:fs";
15209
15245
  import { createRequire as createRequire3 } from "node:module";
15210
15246
  import { homedir as homedir6 } from "node:os";
15211
15247
  import { delimiter as delimiter2, dirname as dirname6, join as join16 } from "node:path";
@@ -15224,7 +15260,7 @@ function sdkNativeBinaryExists() {
15224
15260
  }
15225
15261
  function isExecutable(path) {
15226
15262
  try {
15227
- accessSync(path, constants.X_OK);
15263
+ accessSync(path, constants2.X_OK);
15228
15264
  return true;
15229
15265
  } catch {
15230
15266
  return false;
@@ -60724,6 +60760,10 @@ function runUpgrade(channel = INSTALL_CHANNEL, options = {}) {
60724
60760
  }
60725
60761
  manager = owningPackageManager(options.entrypoint ?? process.argv[1], platform, options.comSpec, env, cwd);
60726
60762
  if (!manager) {
60763
+ if (env.DOSU_DEV === "true") {
60764
+ console.log("DOSU_DEV=true and this copy is not a global package: skipping the package install.");
60765
+ return 0;
60766
+ }
60727
60767
  printNonGlobalPackageGuidance();
60728
60768
  return 1;
60729
60769
  }
@@ -60756,6 +60796,41 @@ Could not update Dosu automatically.`);
60756
60796
  console.log('Run "dosu --version" to verify.');
60757
60797
  return 0;
60758
60798
  }
60799
+ function postUpgradeArgs(interactive) {
60800
+ return interactive ? ["setup"] : ["mcp", "refresh"];
60801
+ }
60802
+ function newBinaryInvocation(channel, args, entrypoint = process.argv[1], execPath = process.execPath) {
60803
+ if (channel === "npm") {
60804
+ return entrypoint ? { command: execPath, args: [entrypoint, ...args] } : null;
60805
+ }
60806
+ if (channel === "homebrew") {
60807
+ return { command: "dosu", args };
60808
+ }
60809
+ return null;
60810
+ }
60811
+ function isInteractiveTerminal() {
60812
+ return process.stdin.isTTY === true && process.stdout.isTTY === true;
60813
+ }
60814
+ function setupAgentsWithNewBinary(channel, options) {
60815
+ const interactive = options.interactive ?? isInteractiveTerminal();
60816
+ const invocation = newBinaryInvocation(channel, postUpgradeArgs(interactive), options.entrypoint ?? process.argv[1]);
60817
+ if (!invocation) {
60818
+ console.error(SETUP_FALLBACK);
60819
+ return;
60820
+ }
60821
+ console.log(interactive ? `
60822
+ Running setup with the new version to update your AI agents...
60823
+ ` : `
60824
+ Refreshing agent MCP configs with the new version...`);
60825
+ const result = spawnSync(invocation.command, invocation.args, {
60826
+ cwd: options.cwd ?? homedir9(),
60827
+ shell: false,
60828
+ stdio: "inherit"
60829
+ });
60830
+ if (result.error || result.status !== 0) {
60831
+ console.error(SETUP_FALLBACK);
60832
+ }
60833
+ }
60759
60834
  async function completeUpgrade(channel = INSTALL_CHANNEL, options = {}) {
60760
60835
  const status = runUpgrade(channel, options);
60761
60836
  if (status !== 0)
@@ -60771,16 +60846,17 @@ async function completeUpgrade(channel = INSTALL_CHANNEL, options = {}) {
60771
60846
  } catch {
60772
60847
  console.error('Skills could not be refreshed. Run "dosu skill update" to retry.');
60773
60848
  }
60849
+ setupAgentsWithNewBinary(channel, options);
60774
60850
  return 0;
60775
60851
  }
60776
60852
  function upgradeCommand() {
60777
- return new Command("upgrade").description("Update Dosu to the latest version and refresh agent skills").action(async () => {
60853
+ return new Command("upgrade").description("Update Dosu to the latest version, then re-run setup to update your AI agents").action(async () => {
60778
60854
  const status = await completeUpgrade();
60779
60855
  if (status !== 0)
60780
60856
  process.exitCode = status;
60781
60857
  });
60782
60858
  }
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;
60859
+ 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
60860
  var init_upgrade = __esm(() => {
60785
60861
  init_commander();
60786
60862
  init_skill();
@@ -60806,7 +60882,7 @@ var init_upgrade = __esm(() => {
60806
60882
  });
60807
60883
 
60808
60884
  // src/debug/follow.ts
60809
- import { closeSync as closeSync5, openSync as openSync5, readSync as readSync4, statSync as statSync8 } from "node:fs";
60885
+ import { closeSync as closeSync6, openSync as openSync6, readSync as readSync5, statSync as statSync8 } from "node:fs";
60810
60886
  function createLogFollower(path, emit) {
60811
60887
  const sizeOf = () => {
60812
60888
  try {
@@ -60824,15 +60900,15 @@ function createLogFollower(path, emit) {
60824
60900
  if (size === offset)
60825
60901
  return;
60826
60902
  try {
60827
- const fd = openSync5(path, "r");
60903
+ const fd = openSync6(path, "r");
60828
60904
  try {
60829
60905
  const buf = Buffer.alloc(size - offset);
60830
- const read = readSync4(fd, buf, 0, buf.length, offset);
60906
+ const read = readSync5(fd, buf, 0, buf.length, offset);
60831
60907
  offset += read;
60832
60908
  if (read > 0)
60833
60909
  emit(buf.toString("utf-8", 0, read));
60834
60910
  } finally {
60835
- closeSync5(fd);
60911
+ closeSync6(fd);
60836
60912
  }
60837
60913
  } catch {}
60838
60914
  }
@@ -61510,6 +61586,36 @@ var init_providers = __esm(() => {
61510
61586
  init_zed();
61511
61587
  });
61512
61588
 
61589
+ // src/mcp/refresh.ts
61590
+ function configuredProviders() {
61591
+ return allSetupProviders().filter((provider) => {
61592
+ try {
61593
+ return provider.isInstalled() && provider.isConfigured();
61594
+ } catch {
61595
+ return false;
61596
+ }
61597
+ });
61598
+ }
61599
+ function refreshConfiguredProviders(cfg) {
61600
+ const result = { updated: [], failed: [] };
61601
+ for (const provider of configuredProviders()) {
61602
+ try {
61603
+ provider.install(cfg, true);
61604
+ logger.info("mcp", `Refreshed ${provider.name()} MCP entry`);
61605
+ result.updated.push(provider);
61606
+ } catch (err) {
61607
+ const error = err instanceof Error ? err : new Error(String(err));
61608
+ logger.error("mcp", `Refresh failed for ${provider.name()}: ${error.message}`);
61609
+ result.failed.push({ provider, error });
61610
+ }
61611
+ }
61612
+ return result;
61613
+ }
61614
+ var init_refresh = __esm(() => {
61615
+ init_logger();
61616
+ init_providers();
61617
+ });
61618
+
61513
61619
  // src/version/update-check.ts
61514
61620
  import { existsSync as existsSync23, mkdirSync as mkdirSync13, readFileSync as readFileSync22, writeFileSync as writeFileSync14 } from "node:fs";
61515
61621
  import { join as join39 } from "node:path";
@@ -61665,15 +61771,98 @@ var init_update_check = __esm(() => {
61665
61771
  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
61772
  });
61667
61773
 
61774
+ // src/version/mcp-refresh-check.ts
61775
+ import { existsSync as existsSync24, mkdirSync as mkdirSync14, readFileSync as readFileSync23, writeFileSync as writeFileSync15 } from "node:fs";
61776
+ import { join as join40 } from "node:path";
61777
+ function needsMcpRefresh(previous, current) {
61778
+ if (previous === null)
61779
+ return true;
61780
+ if (previous === current)
61781
+ return false;
61782
+ const [older, newer] = isNewerVersion(current, previous) ? [previous, current] : [current, previous];
61783
+ return MCP_FORMAT_CHANGES.some((change) => isNewerVersion(change, older) && !isNewerVersion(change, newer));
61784
+ }
61785
+ function getCachePath4() {
61786
+ return join40(getConfigDir(), CACHE_FILENAME5);
61787
+ }
61788
+ function readMcpRefreshCache() {
61789
+ try {
61790
+ const path = getCachePath4();
61791
+ if (!existsSync24(path))
61792
+ return null;
61793
+ const data = JSON.parse(readFileSync23(path, "utf-8"));
61794
+ if (typeof data?.version === "string")
61795
+ return { version: data.version };
61796
+ return null;
61797
+ } catch {
61798
+ return null;
61799
+ }
61800
+ }
61801
+ function writeMcpRefreshCache(cache) {
61802
+ try {
61803
+ const dir = getConfigDir();
61804
+ if (!existsSync24(dir)) {
61805
+ mkdirSync14(dir, { recursive: true, mode: 448 });
61806
+ }
61807
+ writeFileSync15(getCachePath4(), JSON.stringify(cache), { mode: 384 });
61808
+ } catch {}
61809
+ }
61810
+ function canRefreshMcp(cfg) {
61811
+ const target = cfg.active_account?.target;
61812
+ if (!target?.api_key)
61813
+ return false;
61814
+ return cfg.mode === MODE_OSS || Boolean(target.deployment_id);
61815
+ }
61816
+ function displayNotice3(names) {
61817
+ console.error(`
61818
+ ${import_picocolors33.default.green(`✓ Dosu ${VERSION}: refreshed MCP config for ${names.join(", ")}`)}
61819
+ ` + `${import_picocolors33.default.dim(` Run ${import_picocolors33.default.cyan('"dosu setup"')} to also update skills, hooks, and rules, then restart your AI agents.`)}
61820
+ `);
61821
+ }
61822
+ function checkForMcpRefresh(options = {}) {
61823
+ const notify = options.notify ?? true;
61824
+ try {
61825
+ const previous = readMcpRefreshCache()?.version ?? null;
61826
+ if (previous === VERSION)
61827
+ return;
61828
+ if (!needsMcpRefresh(previous, VERSION)) {
61829
+ writeMcpRefreshCache({ version: VERSION });
61830
+ return;
61831
+ }
61832
+ const cfg = loadConfigNonBlocking();
61833
+ if (!cfg || !canRefreshMcp(cfg)) {
61834
+ logger.debug("mcp-refresh", "Skipping MCP refresh: no API key or deployment configured");
61835
+ return;
61836
+ }
61837
+ const result = refreshConfiguredProviders(cfg);
61838
+ writeMcpRefreshCache({ version: VERSION });
61839
+ logger.info("mcp-refresh", `Version ${VERSION}: refreshed ${result.updated.length}, failed ${result.failed.length}`);
61840
+ if (notify && result.updated.length > 0) {
61841
+ displayNotice3(result.updated.map((provider) => provider.name()));
61842
+ }
61843
+ } catch (err) {
61844
+ logger.error("mcp-refresh", `MCP refresh check failed: ${err}`);
61845
+ }
61846
+ }
61847
+ var import_picocolors33, CACHE_FILENAME5 = "mcp-refresh.json", MCP_FORMAT_CHANGES;
61848
+ var init_mcp_refresh_check = __esm(() => {
61849
+ init_config();
61850
+ init_logger();
61851
+ init_refresh();
61852
+ init_update_check();
61853
+ import_picocolors33 = __toESM(require_picocolors(), 1);
61854
+ MCP_FORMAT_CHANGES = ["0.53.0"];
61855
+ });
61856
+
61668
61857
  // src/rules/installer.ts
61669
- import { existsSync as existsSync24, mkdirSync as mkdirSync14, readFileSync as readFileSync23, unlinkSync as unlinkSync5, writeFileSync as writeFileSync15 } from "node:fs";
61858
+ import { existsSync as existsSync25, mkdirSync as mkdirSync15, readFileSync as readFileSync24, unlinkSync as unlinkSync5, writeFileSync as writeFileSync16 } from "node:fs";
61670
61859
  import { homedir as homedir12 } from "node:os";
61671
- import { dirname as dirname11, join as join40 } from "node:path";
61860
+ import { dirname as dirname11, join as join41 } from "node:path";
61672
61861
  function claudeConfigDir4() {
61673
- return process.env.CLAUDE_CONFIG_DIR || join40(homedir12(), ".claude");
61862
+ return process.env.CLAUDE_CONFIG_DIR || join41(homedir12(), ".claude");
61674
61863
  }
61675
61864
  function codexHome4() {
61676
- return process.env.CODEX_HOME || join40(homedir12(), ".codex");
61865
+ return process.env.CODEX_HOME || join41(homedir12(), ".codex");
61677
61866
  }
61678
61867
  function isRuleAgent(agent) {
61679
61868
  return Object.hasOwn(RULE_TARGETS, agent);
@@ -61699,17 +61888,17 @@ async function fetchDosuRule(fetchImpl = globalThis.fetch) {
61699
61888
  return normalizeRule(FALLBACK_DOSU_RULE);
61700
61889
  }
61701
61890
  function ensureParent(path) {
61702
- mkdirSync14(dirname11(path), { recursive: true });
61891
+ mkdirSync15(dirname11(path), { recursive: true });
61703
61892
  }
61704
61893
  function writeIfChanged(path, content) {
61705
- if (!existsSync24(path)) {
61894
+ if (!existsSync25(path)) {
61706
61895
  ensureParent(path);
61707
- writeFileSync15(path, content, "utf-8");
61896
+ writeFileSync16(path, content, "utf-8");
61708
61897
  return "created";
61709
61898
  }
61710
- if (readFileSync23(path, "utf-8") === content)
61899
+ if (readFileSync24(path, "utf-8") === content)
61711
61900
  return "unchanged";
61712
- writeFileSync15(path, content, "utf-8");
61901
+ writeFileSync16(path, content, "utf-8");
61713
61902
  return "updated";
61714
61903
  }
61715
61904
  function findSection(content) {
@@ -61735,14 +61924,14 @@ function buildSection(content, lineEnding = `
61735
61924
  return `${DOSU_RULE_SECTION_START}${lineEnding}${body}${lineEnding}${DOSU_RULE_SECTION_END}`;
61736
61925
  }
61737
61926
  function upsertSection(path, content) {
61738
- if (!existsSync24(path)) {
61927
+ if (!existsSync25(path)) {
61739
61928
  const section = buildSection(content);
61740
61929
  ensureParent(path);
61741
- writeFileSync15(path, `${section}
61930
+ writeFileSync16(path, `${section}
61742
61931
  `, "utf-8");
61743
61932
  return "created";
61744
61933
  }
61745
- const existing = readFileSync23(path, "utf-8");
61934
+ const existing = readFileSync24(path, "utf-8");
61746
61935
  const lineEnding = lineEndingFor(existing);
61747
61936
  const section = buildSection(content, lineEnding);
61748
61937
  const location = findSection(existing);
@@ -61755,7 +61944,7 @@ function upsertSection(path, content) {
61755
61944
  }
61756
61945
  if (next === existing)
61757
61946
  return "unchanged";
61758
- writeFileSync15(path, next, "utf-8");
61947
+ writeFileSync16(path, next, "utf-8");
61759
61948
  return "updated";
61760
61949
  }
61761
61950
  function installRuleForAgent(agent, content) {
@@ -61772,13 +61961,13 @@ function removeRuleForAgent(agent) {
61772
61961
  return null;
61773
61962
  const target = RULE_TARGETS[agent];
61774
61963
  const path = target.path();
61775
- if (!existsSync24(path))
61964
+ if (!existsSync25(path))
61776
61965
  return { agent, path, action: "not_found" };
61777
61966
  if (target.kind === "file") {
61778
61967
  unlinkSync5(path);
61779
61968
  return { agent, path, action: "removed" };
61780
61969
  }
61781
- const existing = readFileSync23(path, "utf-8");
61970
+ const existing = readFileSync24(path, "utf-8");
61782
61971
  const lineEnding = lineEndingFor(existing);
61783
61972
  const location = findSection(existing);
61784
61973
  if (!location)
@@ -61787,7 +61976,7 @@ function removeRuleForAgent(agent) {
61787
61976
  if (!next) {
61788
61977
  unlinkSync5(path);
61789
61978
  } else {
61790
- writeFileSync15(path, `${next}${lineEnding}`, "utf-8");
61979
+ writeFileSync16(path, `${next}${lineEnding}`, "utf-8");
61791
61980
  }
61792
61981
  return { agent, path, action: "removed" };
61793
61982
  }
@@ -61815,36 +62004,36 @@ var init_installer = __esm(() => {
61815
62004
  RULE_TARGETS = {
61816
62005
  claude: {
61817
62006
  kind: "file",
61818
- path: () => join40(claudeConfigDir4(), "rules", "dosu.md")
62007
+ path: () => join41(claudeConfigDir4(), "rules", "dosu.md")
61819
62008
  },
61820
62009
  cursor: {
61821
62010
  kind: "file",
61822
- path: () => join40(homedir12(), ".cursor", "rules", "dosu.mdc"),
62011
+ path: () => join41(homedir12(), ".cursor", "rules", "dosu.mdc"),
61823
62012
  cursorFrontmatter: true
61824
62013
  },
61825
62014
  codex: {
61826
62015
  kind: "section",
61827
- path: () => join40(codexHome4(), "AGENTS.md")
62016
+ path: () => join41(codexHome4(), "AGENTS.md")
61828
62017
  },
61829
62018
  opencode: {
61830
62019
  kind: "section",
61831
- path: () => join40(homedir12(), ".config", "opencode", "AGENTS.md")
62020
+ path: () => join41(homedir12(), ".config", "opencode", "AGENTS.md")
61832
62021
  },
61833
62022
  gemini: {
61834
62023
  kind: "section",
61835
- path: () => join40(homedir12(), ".gemini", "GEMINI.md")
62024
+ path: () => join41(homedir12(), ".gemini", "GEMINI.md")
61836
62025
  },
61837
62026
  antigravity: {
61838
62027
  kind: "section",
61839
- path: () => join40(homedir12(), ".gemini", "GEMINI.md")
62028
+ path: () => join41(homedir12(), ".gemini", "GEMINI.md")
61840
62029
  }
61841
62030
  };
61842
62031
  });
61843
62032
 
61844
62033
  // src/setup/agents-md-step.ts
61845
62034
  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";
62035
+ import { existsSync as existsSync26, readFileSync as readFileSync25, writeFileSync as writeFileSync17 } from "node:fs";
62036
+ import { join as join42 } from "node:path";
61848
62037
  function inGitWorkTree(cwd = process.cwd()) {
61849
62038
  try {
61850
62039
  const stdout = execSync3("git rev-parse --is-inside-work-tree", {
@@ -61858,10 +62047,10 @@ function inGitWorkTree(cwd = process.cwd()) {
61858
62047
  }
61859
62048
  function dosuAgentsSectionState(cwd = process.cwd()) {
61860
62049
  try {
61861
- const path = join41(cwd, "AGENTS.md");
61862
- if (!existsSync25(path))
62050
+ const path = join42(cwd, "AGENTS.md");
62051
+ if (!existsSync26(path))
61863
62052
  return "missing";
61864
- const content = readFileSync24(path, "utf-8");
62053
+ const content = readFileSync25(path, "utf-8");
61865
62054
  const startMatch = content.match(SECTION_START_RE);
61866
62055
  if (!startMatch || !content.includes(DOSU_SECTION_END))
61867
62056
  return "missing";
@@ -61896,14 +62085,14 @@ function findSection2(content) {
61896
62085
  return { start, end };
61897
62086
  }
61898
62087
  function upsertDosuAgentsSection(cwd = process.cwd(), content = FALLBACK_DOSU_RULE) {
61899
- const path = join41(cwd, "AGENTS.md");
61900
- if (!existsSync25(path)) {
62088
+ const path = join42(cwd, "AGENTS.md");
62089
+ if (!existsSync26(path)) {
61901
62090
  const section = buildDosuAgentsSection(content);
61902
- writeFileSync16(path, `${section}
62091
+ writeFileSync17(path, `${section}
61903
62092
  `);
61904
62093
  return { path, action: "created" };
61905
62094
  }
61906
- const existing = readFileSync24(path, "utf-8");
62095
+ const existing = readFileSync25(path, "utf-8");
61907
62096
  const lineEnding = lineEndingFor2(existing);
61908
62097
  const section = renderDosuAgentsSection(content, lineEnding);
61909
62098
  const location = findSection2(existing);
@@ -61915,7 +62104,7 @@ function upsertDosuAgentsSection(cwd = process.cwd(), content = FALLBACK_DOSU_RU
61915
62104
  }
61916
62105
  if (next === existing)
61917
62106
  return { path, action: "unchanged" };
61918
- writeFileSync16(path, next);
62107
+ writeFileSync17(path, next);
61919
62108
  return { path, action: "updated" };
61920
62109
  }
61921
62110
  async function stepUpdateAgentsMd(cwd = process.cwd(), content) {
@@ -61971,15 +62160,15 @@ var init_errors3 = __esm(() => {
61971
62160
  var MCP_PROVIDER_SLUG2 = "dosu_mcp";
61972
62161
 
61973
62162
  // 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";
62163
+ import { existsSync as existsSync27, mkdirSync as mkdirSync16, readFileSync as readFileSync26, renameSync as renameSync6, statSync as statSync9, writeFileSync as writeFileSync18 } from "node:fs";
61975
62164
  import { homedir as homedir13 } from "node:os";
61976
- import { join as join42 } from "node:path";
62165
+ import { join as join43 } from "node:path";
61977
62166
  function cachePath2(configDir) {
61978
- return join42(configDir, CACHE_FILENAME5);
62167
+ return join43(configDir, CACHE_FILENAME6);
61979
62168
  }
61980
62169
  function loadCacheFile2(configDir) {
61981
62170
  try {
61982
- const raw = JSON.parse(readFileSync25(cachePath2(configDir), "utf-8"));
62171
+ const raw = JSON.parse(readFileSync26(cachePath2(configDir), "utf-8"));
61983
62172
  if (raw.schema_version !== CACHE_SCHEMA_VERSION2 || typeof raw.entries !== "object")
61984
62173
  return {};
61985
62174
  return raw.entries;
@@ -61989,12 +62178,12 @@ function loadCacheFile2(configDir) {
61989
62178
  }
61990
62179
  function saveCacheFile2(configDir, entries) {
61991
62180
  try {
61992
- if (!existsSync26(configDir))
61993
- mkdirSync15(configDir, { recursive: true, mode: 448 });
62181
+ if (!existsSync27(configDir))
62182
+ mkdirSync16(configDir, { recursive: true, mode: 448 });
61994
62183
  const path = cachePath2(configDir);
61995
62184
  const tmp = `${path}.${process.pid}.tmp`;
61996
62185
  const file = { schema_version: CACHE_SCHEMA_VERSION2, entries };
61997
- writeFileSync17(tmp, JSON.stringify(file), { mode: 384 });
62186
+ writeFileSync18(tmp, JSON.stringify(file), { mode: 384 });
61998
62187
  renameSync6(tmp, path);
61999
62188
  } catch {}
62000
62189
  }
@@ -62027,15 +62216,15 @@ function claudeSummaryFromHead(text) {
62027
62216
  }
62028
62217
  return null;
62029
62218
  }
62030
- function reconstructSession(harness, id, slug, home = homedir13(), exists = existsSync26) {
62219
+ function reconstructSession(harness, id, slug, home = homedir13(), exists = existsSync27) {
62031
62220
  if (!slug)
62032
62221
  return null;
62033
62222
  const candidates = [];
62034
62223
  if (harness === "claude") {
62035
- candidates.push(join42(home, ".claude", "projects", slug, `${id}.jsonl`));
62224
+ candidates.push(join43(home, ".claude", "projects", slug, `${id}.jsonl`));
62036
62225
  } 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`));
62226
+ const project = join43(home, ".cursor", "projects", slug);
62227
+ candidates.push(join43(project, "agent-transcripts", id, `${id}.jsonl`), join43(project, "agent-transcripts", `${id}.jsonl`));
62039
62228
  } else {
62040
62229
  return null;
62041
62230
  }
@@ -62047,7 +62236,7 @@ function reconstructSession(harness, id, slug, home = homedir13(), exists = exis
62047
62236
  }
62048
62237
  function readHead2(path) {
62049
62238
  try {
62050
- return readFileSync25(path, "utf-8").slice(0, 128 * 1024);
62239
+ return readFileSync26(path, "utf-8").slice(0, 128 * 1024);
62051
62240
  } catch {
62052
62241
  return null;
62053
62242
  }
@@ -62098,7 +62287,7 @@ function createSessionTitleResolver(configDir = getConfigDir(), deps = {}) {
62098
62287
  }
62099
62288
  };
62100
62289
  }
62101
- var CACHE_FILENAME5 = "session-titles.json", CACHE_SCHEMA_VERSION2 = 3, HEAD_LINES2 = 50, SESSION_NAME_MAX = 80;
62290
+ var CACHE_FILENAME6 = "session-titles.json", CACHE_SCHEMA_VERSION2 = 3, HEAD_LINES2 = 50, SESSION_NAME_MAX = 80;
62102
62291
  var init_session_title = __esm(() => {
62103
62292
  init_config();
62104
62293
  init_read();
@@ -62127,7 +62316,7 @@ var init_alt_screen = __esm(() => {
62127
62316
  });
62128
62317
 
62129
62318
  // src/tui/activity-view.ts
62130
- import { existsSync as existsSync27, readFileSync as readFileSync26 } from "node:fs";
62319
+ import { existsSync as existsSync28, readFileSync as readFileSync27 } from "node:fs";
62131
62320
  import { basename as basename5 } from "node:path";
62132
62321
  function reduceActivityViewKey(key) {
62133
62322
  if (key === "q" || key === ESC7 || key === CTRL_C4)
@@ -62268,15 +62457,15 @@ function statusLine(status) {
62268
62457
  if (status.running) {
62269
62458
  const since = status.startedAt ? ` · since ${localTime(status.startedAt)}` : "";
62270
62459
  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}`)}`;
62460
+ return `\uD83D\uDCDA ${import_picocolors34.default.bold(brand("Studying sessions..."))}${import_picocolors34.default.dim(`${pid}${since}`)}`;
62272
62461
  }
62273
62462
  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")}`;
62463
+ return `${import_picocolors34.default.yellow("○")} ${import_picocolors34.default.bold("Paused")} ${import_picocolors34.default.dim("· studying stays off until you resume")}`;
62275
62464
  }
62276
62465
  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")}`;
62466
+ return `${import_picocolors34.default.yellow("○")} ${import_picocolors34.default.bold("Not running")} ${import_picocolors34.default.dim("· a previous run exited without cleaning up")}`;
62278
62467
  }
62279
- return `${import_picocolors33.default.dim("○")} ${import_picocolors33.default.bold("Idle")} ${import_picocolors33.default.dim("· hooks study new sessions automatically")}`;
62468
+ return `${import_picocolors34.default.dim("○")} ${import_picocolors34.default.bold("Idle")} ${import_picocolors34.default.dim("· hooks study new sessions automatically")}`;
62280
62469
  }
62281
62470
  function progressLine(done, ready, width, notes = 0) {
62282
62471
  const total = done + ready;
@@ -62285,7 +62474,7 @@ function progressLine(done, ready, width, notes = 0) {
62285
62474
  const cells = Math.max(10, Math.min(30, width - 44));
62286
62475
  const ratio = Math.max(0, Math.min(1, done / total));
62287
62476
  const filled = Math.min(cells, Math.round(ratio * cells));
62288
- const bar = brand("█".repeat(filled)) + import_picocolors33.default.dim("░".repeat(cells - filled));
62477
+ const bar = brand("█".repeat(filled)) + import_picocolors34.default.dim("░".repeat(cells - filled));
62289
62478
  const pct = Math.floor(ratio * 100);
62290
62479
  const suffix = notes > 0 ? ` · ${notes} suggested page${notes === 1 ? "" : "s"}` : "";
62291
62480
  return `${bar} ${done}/${total} studied · ${pct}%${suffix}`;
@@ -62324,20 +62513,20 @@ function confirmBox(queuedCount, backlog, width, mode = "start") {
62324
62513
  const verb = mode === "clear" ? "clear" : mode === "stop" ? "stop" : mode === "resume" ? "resume" : "start";
62325
62514
  const maxInner = Math.max(20, Math.min(width, contentWidth()) - 4);
62326
62515
  const rows = [
62327
- import_picocolors33.default.bold(brand(title)),
62328
- ...wrapLine(scope, maxInner, "").map((line) => import_picocolors33.default.dim(line)),
62516
+ import_picocolors34.default.bold(brand(title)),
62517
+ ...wrapLine(scope, maxInner, "").map((line) => import_picocolors34.default.dim(line)),
62329
62518
  "",
62330
- `${import_picocolors33.default.bold("enter")} ${verb} · ${import_picocolors33.default.bold("esc")} cancel`
62519
+ `${import_picocolors34.default.bold("enter")} ${verb} · ${import_picocolors34.default.bold("esc")} cancel`
62331
62520
  ];
62332
62521
  const inner = Math.min(maxInner, Math.max(...rows.map(visibleWidth2)));
62333
62522
  const pad = (row) => row + " ".repeat(Math.max(0, inner - visibleWidth2(row)));
62334
62523
  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)}╯`)
62524
+ import_picocolors34.default.dim(`╭${"─".repeat(inner + 2)}╮`),
62525
+ ...rows.map((row) => `${import_picocolors34.default.dim("│")} ${pad(row)} ${import_picocolors34.default.dim("│")}`),
62526
+ import_picocolors34.default.dim(`╰${"─".repeat(inner + 2)}╯`)
62338
62527
  ];
62339
62528
  }
62340
- function renderActivityFrame(status, activity, width, backlog = null, pane = DEFAULT_PANE, queued = [], studiedBeforeRun = 0, open = [], runProgress = null, projectNames = {}, sessionNames = {}) {
62529
+ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFAULT_PANE, queued = [], studiedBeforeRun = 0, open = [], runProgress = null, projectNames = {}, sessionNames = {}, frameLines) {
62341
62530
  const studied = status.state.watermark ? `Studied sessions up to ${localTime(status.state.watermark)}` : "Nothing studied yet";
62342
62531
  const queueDetail = [];
62343
62532
  if (status.backoffUntil) {
@@ -62350,7 +62539,7 @@ function renderActivityFrame(status, activity, width, backlog = null, pane = DEF
62350
62539
  progress = progressLine(studiedDelta + stepDone, Math.max(0, backlog.ready - stepDone), width, runProgress?.notes ?? 0);
62351
62540
  }
62352
62541
  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)) : [];
62542
+ const refusalLines = refusal ? wrapLine(`! Studying paused: ${refusal.message} (${localTime(refusal.at)})`, width).map((line) => import_picocolors34.default.yellow(line)) : [];
62354
62543
  const fullRows = Boolean(pane.fullRows);
62355
62544
  const withName = (item, key) => projectNames[key] ? { ...item, project: projectNames[key] } : item;
62356
62545
  const latestPass = new Map;
@@ -62372,27 +62561,18 @@ function renderActivityFrame(status, activity, width, backlog = null, pane = DEF
62372
62561
  const source = pane.tab === "activity" ? activity.map((line) => formatActivityLine(line, width, fullRows)) : pane.tab === "queued" ? queuedRows : pane.tab === "open" ? openRows : studiedRows;
62373
62562
  const emptyStudied = status.state.watermark ? "No sessions recorded yet. History starts with the next study run." : "No studied sessions yet.";
62374
62563
  const empty = pane.tab === "activity" ? "No sync activity in the log yet." : pane.tab === "queued" ? "Queue empty. Finished agent sessions appear here." : pane.tab === "open" ? "No open sessions. Live agent sessions sit here until they go quiet." : emptyStudied;
62375
- const height = fullRows ? ACTIVITY_VIEW_FULL_LIST_ROWS : ACTIVITY_VIEW_LIST_LINES;
62376
- const { visible, above, below } = windowList(source, pane.scroll, height);
62377
- 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)];
62379
- const scrollParts = [];
62380
- if (above > 0)
62381
- scrollParts.push(`↑ ${above} earlier`);
62382
- if (below > 0)
62383
- scrollParts.push(`↓ ${below} newer`);
62384
- const lines = [
62564
+ const header = [
62385
62565
  breadcrumb(["home", "activity"], width),
62386
62566
  "",
62387
62567
  statusLine(status),
62388
- import_picocolors33.default.dim(studied),
62389
- ...queueDetail.length > 0 ? wrapLine(queueDetail.join(" · "), width).map((line) => import_picocolors33.default.dim(line)) : [],
62568
+ import_picocolors34.default.dim(studied),
62569
+ ...queueDetail.length > 0 ? wrapLine(queueDetail.join(" · "), width).map((line) => import_picocolors34.default.dim(line)) : [],
62390
62570
  ...progress ? [progress] : [],
62391
62571
  ...refusalLines,
62392
62572
  "",
62393
- ...tabBar(pane.tab, queued.length, open.length, latestPass.size, width),
62394
- ...listRows,
62395
- ...scrollParts.length > 0 ? [import_picocolors33.default.dim(clip(scrollParts.join(" · "), width))] : [],
62573
+ ...tabBar(pane.tab, queued.length, open.length, latestPass.size, width)
62574
+ ];
62575
+ const footer = [
62396
62576
  "",
62397
62577
  ...pane.confirm ? centerBlock(confirmBox(queued.length, backlog, width, pane.confirm), width) : [
62398
62578
  ...wrapLine([
@@ -62402,11 +62582,32 @@ function renderActivityFrame(status, activity, width, backlog = null, pane = DEF
62402
62582
  status.running ? "s stop" : status.state.paused ? "s resume" : "s sync now",
62403
62583
  ...!status.running && status.state.watermark ? ["c clear"] : [],
62404
62584
  "esc back"
62405
- ].join(" · "), width, "").map((line) => import_picocolors33.default.dim(line))
62585
+ ].join(" · "), width, "").map((line) => import_picocolors34.default.dim(line))
62406
62586
  ]
62407
62587
  ];
62408
- return lines.join(`
62409
- `);
62588
+ const defaultHeight = fullRows ? ACTIVITY_VIEW_FULL_LIST_ROWS : ACTIVITY_VIEW_LIST_LINES;
62589
+ const listBudget = frameLines === undefined ? defaultHeight : Math.max(1, Math.min(defaultHeight, frameLines - header.length - footer.length - 1));
62590
+ let height = listBudget;
62591
+ let { visible, above, below } = windowList(source, pane.scroll, height);
62592
+ let display = fullRows ? visible.flatMap((row) => wrapRow(row, width)) : visible;
62593
+ while (fullRows && frameLines !== undefined && display.length > listBudget && height > 1) {
62594
+ height -= 1;
62595
+ ({ visible, above, below } = windowList(source, pane.scroll, height));
62596
+ display = visible.flatMap((row) => wrapRow(row, width));
62597
+ }
62598
+ const listRows = display.length > 0 ? display.map((row) => import_picocolors34.default.dim(row)) : [import_picocolors34.default.dim(empty)];
62599
+ const scrollParts = [];
62600
+ if (above > 0)
62601
+ scrollParts.push(`↑ ${above} earlier`);
62602
+ if (below > 0)
62603
+ scrollParts.push(`↓ ${below} newer`);
62604
+ const lines = [
62605
+ ...header,
62606
+ ...listRows,
62607
+ ...scrollParts.length > 0 ? [import_picocolors34.default.dim(clip(scrollParts.join(" · "), width))] : [],
62608
+ ...footer
62609
+ ];
62610
+ return { lines, listHeight: height };
62410
62611
  }
62411
62612
  function createRowNamer() {
62412
62613
  const dirs = createProjectDirResolver();
@@ -62436,7 +62637,7 @@ function createRowNamer() {
62436
62637
  names.titles[key] = t;
62437
62638
  };
62438
62639
  for (const s of [...backlog.queued, ...backlog.open]) {
62439
- if (!existsSync27(s.path))
62640
+ if (!existsSync28(s.path))
62440
62641
  continue;
62441
62642
  const key = `${s.harness}/${s.id}`;
62442
62643
  const dir = dirs.resolve(s);
@@ -62473,7 +62674,7 @@ function createRowNamer() {
62473
62674
  }
62474
62675
  function defaultReadLog2() {
62475
62676
  try {
62476
- return readFileSync26(logger.getLogPath(), "utf-8");
62677
+ return readFileSync27(logger.getLogPath(), "utf-8");
62477
62678
  } catch {
62478
62679
  return "";
62479
62680
  }
@@ -62518,7 +62719,7 @@ function runActivityView(io = {}) {
62518
62719
  return sessions.open.length;
62519
62720
  return (status.state.mined_sessions ?? []).length;
62520
62721
  };
62521
- const listHeight = () => fullRows ? ACTIVITY_VIEW_FULL_LIST_ROWS : ACTIVITY_VIEW_LIST_LINES;
62722
+ let listHeight = ACTIVITY_VIEW_LIST_LINES;
62522
62723
  let lastFrame = null;
62523
62724
  let studiedBeforeRun = null;
62524
62725
  const draw = () => {
@@ -62543,7 +62744,11 @@ function runActivityView(io = {}) {
62543
62744
  confirmSync = null;
62544
62745
  const width = activityWidth(output.columns ?? 80);
62545
62746
  const rowNames = rowNamesFor(status, sessions);
62546
- const frame = renderActivityFrame(status, activity, width, backlog, { tab, scroll, confirm: confirmSync ?? undefined, fullRows }, sessions.queued, studiedBeforeRun ?? 0, sessions.open, runProgress, rowNames.projects, rowNames.titles);
62747
+ scroll = Math.min(scroll, Math.max(0, activeListLength() - listHeight));
62748
+ const built = buildActivityFrame(status, activity, width, backlog, { tab, scroll, confirm: confirmSync ?? undefined, fullRows }, sessions.queued, studiedBeforeRun ?? 0, sessions.open, runProgress, rowNames.projects, rowNames.titles, frameMaxLines(output.rows ?? 24));
62749
+ listHeight = built.listHeight;
62750
+ const frame = built.lines.join(`
62751
+ `);
62547
62752
  if (frame === lastFrame)
62548
62753
  return;
62549
62754
  lastFrame = frame;
@@ -62638,7 +62843,7 @@ ${CLEAR_BELOW}`);
62638
62843
  scroll = 0;
62639
62844
  draw();
62640
62845
  } else if (action === "up") {
62641
- const maxScroll = Math.max(0, activeListLength() - listHeight());
62846
+ const maxScroll = Math.max(0, activeListLength() - listHeight);
62642
62847
  if (scroll < maxScroll) {
62643
62848
  scroll += 1;
62644
62849
  draw();
@@ -62654,7 +62859,7 @@ ${CLEAR_BELOW}`);
62654
62859
  input.on("data", onData);
62655
62860
  });
62656
62861
  }
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;
62862
+ 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
62863
  var init_activity_view = __esm(() => {
62659
62864
  init_follow();
62660
62865
  init_logger();
@@ -62669,7 +62874,7 @@ var init_activity_view = __esm(() => {
62669
62874
  init_alt_screen();
62670
62875
  init_layout();
62671
62876
  init_menu();
62672
- import_picocolors33 = __toESM(require_picocolors(), 1);
62877
+ import_picocolors34 = __toESM(require_picocolors(), 1);
62673
62878
  ESC7 = String.fromCharCode(27);
62674
62879
  HIDE_CURSOR3 = `${ESC7}[?25l`;
62675
62880
  SHOW_CURSOR3 = `${ESC7}[?25h`;
@@ -63450,6 +63655,7 @@ async function runSetupFlow(opts = {}) {
63450
63655
  }));
63451
63656
  return;
63452
63657
  }
63658
+ writeMcpRefreshCache({ version: VERSION });
63453
63659
  const configuredProviders = configured.filter((result) => (result.action === "install" || result.action === "skip") && !result.error);
63454
63660
  const mcpCompleted = configuredProviders.length > 0;
63455
63661
  if (mcpCompleted) {
@@ -63894,25 +64100,14 @@ async function runSwitchTarget(scope = "org") {
63894
64100
  return;
63895
64101
  updateTarget(cfg, { api_key: apiKey });
63896
64102
  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
- }
64103
+ const refreshed = refreshConfiguredProviders(cfg);
64104
+ for (const provider of refreshed.updated) {
64105
+ log2.success(`${provider.name()} ${dim("· updated")}`);
64106
+ }
64107
+ for (const { provider, error } of refreshed.failed) {
64108
+ log2.error(`Failed to update ${provider.name()}: ${error.message}`);
63914
64109
  }
63915
- if (configured.length > 0) {
64110
+ if (refreshed.updated.length + refreshed.failed.length > 0) {
63916
64111
  log2.info("Restart your AI agents so they pick up the new MCP target.");
63917
64112
  }
63918
64113
  }
@@ -64162,7 +64357,7 @@ async function stepSelectTools(detected) {
64162
64357
  initialValues: preselected,
64163
64358
  statusFor: (id, picked) => {
64164
64359
  if (configuredMap.get(id))
64165
- return picked ? dim("configured") : import_picocolors34.default.yellow("will remove");
64360
+ return picked ? dim("configured") : import_picocolors35.default.yellow("will remove");
64166
64361
  return picked ? brand("will configure") : undefined;
64167
64362
  },
64168
64363
  summary: (picked) => {
@@ -64337,7 +64532,7 @@ ${dim(wrapLog("Run it inside a session to keep that session out of studying. Rem
64337
64532
  log2.success("All agents already configured. No changes needed.");
64338
64533
  }
64339
64534
  }
64340
- var import_picocolors34, OAUTH_ANALYTICS_REASONS, SETUP_HANDSHAKE_TIMEOUT_MS = 1800000, LIBRARY_SELECT_CANCELLED;
64535
+ var import_picocolors35, OAUTH_ANALYTICS_REASONS, SETUP_HANDSHAKE_TIMEOUT_MS = 1800000, LIBRARY_SELECT_CANCELLED;
64341
64536
  var init_flow2 = __esm(() => {
64342
64537
  init_dist();
64343
64538
  init_errors3();
@@ -64348,6 +64543,7 @@ var init_flow2 = __esm(() => {
64348
64543
  init_agents();
64349
64544
  init_agents3();
64350
64545
  init_providers();
64546
+ init_refresh();
64351
64547
  init_agents4();
64352
64548
  init_detach();
64353
64549
  init_sync();
@@ -64355,12 +64551,13 @@ var init_flow2 = __esm(() => {
64355
64551
  init_activity_view();
64356
64552
  init_layout();
64357
64553
  init_prompts();
64554
+ init_mcp_refresh_check();
64358
64555
  init_agents_md_step();
64359
64556
  init_analytics2();
64360
64557
  init_github_step();
64361
64558
  init_rules_step();
64362
64559
  init_styles();
64363
- import_picocolors34 = __toESM(require_picocolors(), 1);
64560
+ import_picocolors35 = __toESM(require_picocolors(), 1);
64364
64561
  OAUTH_ANALYTICS_REASONS = new Set([
64365
64562
  "access_denied",
64366
64563
  "bad_oauth_state",
@@ -64375,6 +64572,10 @@ var init_flow2 = __esm(() => {
64375
64572
  });
64376
64573
 
64377
64574
  // src/tui/analytics-view.ts
64575
+ function analyticsListHeight(rows) {
64576
+ const room = frameMaxLines(rows) - ANALYTICS_VIEW_CHROME_LINES;
64577
+ return Math.max(1, Math.min(ANALYTICS_VIEW_LINES, room));
64578
+ }
64378
64579
  function reduceAnalyticsViewKey(key) {
64379
64580
  if (key === "q" || key === ESC8 || key === CTRL_C5)
64380
64581
  return "back";
@@ -64484,11 +64685,11 @@ function analyticsTabRows(tab, state, pageStats) {
64484
64685
  return projectRows(state);
64485
64686
  return pageRows(pageStats);
64486
64687
  }
64487
- function renderAnalyticsFrame(state, tab, scroll, pageStats = null, pagesPending = false, width = contentWidth()) {
64688
+ function renderAnalyticsFrame(state, tab, scroll, pageStats = null, pagesPending = false, width = contentWidth(), height = ANALYTICS_VIEW_LINES) {
64488
64689
  const rows = analyticsTabRows(tab, state, pageStats);
64489
- const { visible, above, below } = windowReport(rows, scroll);
64690
+ const { visible, above, below } = windowReport(rows, scroll, height);
64490
64691
  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)];
64692
+ const listRows = visible.length > 0 ? visible.map((row) => import_picocolors36.default.dim(row)) : [import_picocolors36.default.dim(empty)];
64492
64693
  const scrollParts = [];
64493
64694
  if (above > 0)
64494
64695
  scrollParts.push(`↑ ${above} earlier`);
@@ -64504,9 +64705,9 @@ function renderAnalyticsFrame(state, tab, scroll, pageStats = null, pagesPending
64504
64705
  ], tab, width, { spread: false }),
64505
64706
  "",
64506
64707
  ...listRows,
64507
- ...scrollParts.length > 0 ? [import_picocolors35.default.dim(scrollParts.join(" · "))] : [],
64708
+ ...scrollParts.length > 0 ? [import_picocolors36.default.dim(scrollParts.join(" · "))] : [],
64508
64709
  "",
64509
- import_picocolors35.default.dim("tab switch · ↑↓ scroll · esc back")
64710
+ import_picocolors36.default.dim("tab switch · ↑↓ scroll · esc back")
64510
64711
  ];
64511
64712
  return lines.join(`
64512
64713
  `);
@@ -64526,10 +64727,13 @@ function runAnalyticsView(io = {}) {
64526
64727
  let pagesPending = true;
64527
64728
  let closed = false;
64528
64729
  let lastFrame = null;
64730
+ const listHeight = () => analyticsListHeight(output.rows ?? 24);
64731
+ const maxScroll = () => Math.max(0, analyticsTabRows(tab, status.state, pageStats).length - listHeight());
64529
64732
  const draw = () => {
64530
64733
  status = getStatus();
64734
+ scroll = Math.min(scroll, maxScroll());
64531
64735
  const width = Math.min(contentWidth(output.columns ?? 80), (output.columns ?? 80) - 1);
64532
- const frame = renderAnalyticsFrame(status.state, tab, scroll, pageStats, pagesPending, width);
64736
+ const frame = renderAnalyticsFrame(status.state, tab, scroll, pageStats, pagesPending, width, listHeight());
64533
64737
  if (frame === lastFrame)
64534
64738
  return;
64535
64739
  lastFrame = frame;
@@ -64590,8 +64794,7 @@ ${CLEAR_BELOW2}`);
64590
64794
  draw();
64591
64795
  }
64592
64796
  } else if (action === "down") {
64593
- const maxScroll = Math.max(0, analyticsTabRows(tab, status.state, pageStats).length - ANALYTICS_VIEW_LINES);
64594
- if (scroll < maxScroll) {
64797
+ if (scroll < maxScroll()) {
64595
64798
  scroll += 1;
64596
64799
  draw();
64597
64800
  }
@@ -64601,7 +64804,7 @@ ${CLEAR_BELOW2}`);
64601
64804
  input.on("data", onData);
64602
64805
  });
64603
64806
  }
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);
64807
+ 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_CHROME_LINES = 8, ANALYTICS_VIEW_TABS, TOP_PAGES_LIMIT = 5, TOP_CITED_DAYS = 30, label = (text) => text.padEnd(26);
64605
64808
  var init_analytics_view = __esm(() => {
64606
64809
  init_trpc();
64607
64810
  init_config();
@@ -64609,7 +64812,7 @@ var init_analytics_view = __esm(() => {
64609
64812
  init_alt_screen();
64610
64813
  init_layout();
64611
64814
  init_menu();
64612
- import_picocolors35 = __toESM(require_picocolors(), 1);
64815
+ import_picocolors36 = __toESM(require_picocolors(), 1);
64613
64816
  ESC8 = String.fromCharCode(27);
64614
64817
  HIDE_CURSOR4 = `${ESC8}[?25l`;
64615
64818
  SHOW_CURSOR4 = `${ESC8}[?25h`;
@@ -64627,7 +64830,7 @@ var init_analytics_view = __esm(() => {
64627
64830
  // src/tui/banner.ts
64628
64831
  function paintLogoRow(row) {
64629
64832
  return row.map(([tone, art]) => {
64630
- if (!import_picocolors36.default.isColorSupported)
64833
+ if (!import_picocolors37.default.isColorSupported)
64631
64834
  return art;
64632
64835
  const { fg, fallback } = LOGO_TONES[tone];
64633
64836
  return hasTruecolor() ? `${fg}${art}\x1B[39m` : fallback(art);
@@ -64635,14 +64838,14 @@ function paintLogoRow(row) {
64635
64838
  }
64636
64839
  function checklistRows(ctx) {
64637
64840
  const on = brand(CHECK2);
64638
- const off = import_picocolors36.default.dim(CIRCLE);
64841
+ const off = import_picocolors37.default.dim(CIRCLE);
64639
64842
  const rows = [["workspace", ctx.directory]];
64640
64843
  rows.push([
64641
64844
  "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`)}`
64845
+ 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
64846
  ]);
64644
64847
  const missing = new Set(ctx.setupMissing ?? []);
64645
- const warnRow = `${off} ${import_picocolors36.default.yellow("not configured")} ${import_picocolors36.default.dim(`${DOT3} run Setup`)}`;
64848
+ const warnRow = `${off} ${import_picocolors37.default.yellow("not configured")} ${import_picocolors37.default.dim(`${DOT3} run Setup`)}`;
64646
64849
  if (missing.has("MCP"))
64647
64850
  rows.push(["mcp", warnRow]);
64648
64851
  else if (ctx.deploymentName)
@@ -64654,7 +64857,7 @@ function checklistRows(ctx) {
64654
64857
  if (ctx.repoAgentsMd) {
64655
64858
  rows.push([
64656
64859
  "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`)}`
64860
+ 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
64861
  ]);
64659
64862
  }
64660
64863
  if (missing.has("agents"))
@@ -64666,24 +64869,24 @@ function checklistRows(ctx) {
64666
64869
  if (ctx.studying) {
64667
64870
  rows.push([
64668
64871
  "sync",
64669
- `\uD83D\uDCDA ${brand("studying sessions...")} ${import_picocolors36.default.dim(`${DOT3} see activity`)}`
64872
+ `\uD83D\uDCDA ${brand("studying sessions...")} ${import_picocolors37.default.dim(`${DOT3} see activity`)}`
64670
64873
  ]);
64671
64874
  }
64672
64875
  if (ctx.update) {
64673
64876
  rows.push([
64674
64877
  "update",
64675
- `${import_picocolors36.default.yellow(`↑ ${ctx.update.version} available`)} ${import_picocolors36.default.dim(`${DOT3} ${ctx.update.hint}`)}`
64878
+ `${import_picocolors37.default.yellow(`↑ ${ctx.update.version} available`)} ${import_picocolors37.default.dim(`${DOT3} ${ctx.update.hint}`)}`
64676
64879
  ]);
64677
64880
  }
64678
64881
  const labelWidth = Math.max(...rows.map(([label]) => label.length));
64679
- return rows.map(([label, value]) => `${import_picocolors36.default.dim(label.padEnd(labelWidth))} ${value}`);
64882
+ return rows.map(([label, value]) => `${import_picocolors37.default.dim(label.padEnd(labelWidth))} ${value}`);
64680
64883
  }
64681
64884
  function renderBanner(ctx) {
64682
64885
  const logo = LOGO_ROWS.map(paintLogoRow);
64683
64886
  const text = [
64684
64887
  ...checklistRows(ctx),
64685
64888
  "",
64686
- `${brandBadge("dosu-cli")} ${import_picocolors36.default.dim(`${ctx.version} ${DOT3} ${ctx.webAppHost}`)}`
64889
+ `${brandBadge("dosu-cli")} ${import_picocolors37.default.dim(`${ctx.version} ${DOT3} ${ctx.webAppHost}`)}`
64687
64890
  ];
64688
64891
  const height = Math.max(logo.length, text.length);
64689
64892
  const combined = [];
@@ -64696,10 +64899,10 @@ function renderBanner(ctx) {
64696
64899
  return ["", ...combined, ""].join(`
64697
64900
  `);
64698
64901
  }
64699
- var import_picocolors36, CHECK2 = "✔", CIRCLE = "○", DOT3 = "·", LOGO_ROWS, LOGO_MARK, LOGO_WIDTH, LOGO_TONES, COLUMN_GAP = " ";
64902
+ var import_picocolors37, CHECK2 = "✔", CIRCLE = "○", DOT3 = "·", LOGO_ROWS, LOGO_MARK, LOGO_WIDTH, LOGO_TONES, COLUMN_GAP = " ";
64700
64903
  var init_banner = __esm(() => {
64701
64904
  init_styles();
64702
- import_picocolors36 = __toESM(require_picocolors(), 1);
64905
+ import_picocolors37 = __toESM(require_picocolors(), 1);
64703
64906
  LOGO_ROWS = [
64704
64907
  [["sage", "▄▄▄▄▄▄"]],
64705
64908
  [["sage", "██ ▀▄"]],
@@ -64710,8 +64913,8 @@ var init_banner = __esm(() => {
64710
64913
  LOGO_MARK = LOGO_ROWS.map((row) => row.map(([, art]) => art).join(""));
64711
64914
  LOGO_WIDTH = Math.max(...LOGO_MARK.map((row) => row.length));
64712
64915
  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)) }
64916
+ sage: { fg: "\x1B[38;2;180;187;145m", fallback: import_picocolors37.default.green },
64917
+ moss: { fg: "\x1B[38;2;119;133;97m", fallback: (art) => import_picocolors37.default.dim(import_picocolors37.default.green(art)) }
64715
64918
  };
64716
64919
  });
64717
64920
 
@@ -64823,16 +65026,16 @@ function renderPagesFrame(model, width, selected) {
64823
65026
  const body = [];
64824
65027
  let scrollHint = "";
64825
65028
  if (model.phase === "loading") {
64826
- body.push(import_picocolors37.default.dim("Loading pages…"));
65029
+ body.push(import_picocolors38.default.dim("Loading pages…"));
64827
65030
  } else if (model.phase === "error") {
64828
- body.push(import_picocolors37.default.yellow(clip3(model.message, width)));
65031
+ body.push(import_picocolors38.default.yellow(clip3(model.message, width)));
64829
65032
  } else if (model.pages.length === 0) {
64830
- body.push(import_picocolors37.default.dim("No pages in this Library yet."));
65033
+ body.push(import_picocolors38.default.dim("No pages in this Library yet."));
64831
65034
  } else {
64832
65035
  const rows = pageRows2(model.pages, width - 2);
64833
65036
  const { start, above } = windowSelection(rows.length, selected);
64834
65037
  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)}`));
65038
+ body.push(...visible.map((row, i) => start + i === selected ? `${brand("▸")} ${import_picocolors38.default.bold(row)}` : ` ${import_picocolors38.default.dim(row)}`));
64836
65039
  const below = Math.max(0, Math.max(model.total, rows.length) - start - PAGES_VIEW_LINES);
64837
65040
  const parts = [];
64838
65041
  if (above > 0)
@@ -64841,14 +65044,14 @@ function renderPagesFrame(model, width, selected) {
64841
65044
  parts.push(`↓ ${below} more`);
64842
65045
  scrollHint = parts.join(" · ");
64843
65046
  }
64844
- const count = model.phase === "ready" ? ` ${import_picocolors37.default.dim(model.pages.length < model.total ? `(${model.pages.length} of ${model.total})` : `(${model.pages.length})`)}` : "";
65047
+ const count = model.phase === "ready" ? ` ${import_picocolors38.default.dim(model.pages.length < model.total ? `(${model.pages.length} of ${model.total})` : `(${model.pages.length})`)}` : "";
64845
65048
  const lines = [
64846
65049
  `${breadcrumb(["home", "pages"], width)}${count}`,
64847
65050
  "",
64848
65051
  ...body,
64849
- ...scrollHint ? [import_picocolors37.default.dim(scrollHint)] : [],
65052
+ ...scrollHint ? [import_picocolors38.default.dim(scrollHint)] : [],
64850
65053
  "",
64851
- import_picocolors37.default.dim("↑↓ move · enter open · esc back")
65054
+ import_picocolors38.default.dim("↑↓ move · enter open · esc back")
64852
65055
  ];
64853
65056
  return lines.join(`
64854
65057
  `);
@@ -64857,9 +65060,9 @@ function renderPageFrame(page, content, width, scroll) {
64857
65060
  const body = [];
64858
65061
  let scrollHint = "";
64859
65062
  if (content.phase === "loading") {
64860
- body.push(import_picocolors37.default.dim("Loading page…"));
65063
+ body.push(import_picocolors38.default.dim("Loading page…"));
64861
65064
  } else if (content.phase === "error") {
64862
- body.push(import_picocolors37.default.yellow(clip3(content.message, width)));
65065
+ body.push(import_picocolors38.default.yellow(clip3(content.message, width)));
64863
65066
  } else {
64864
65067
  const rows = wrapBody(content.body, width);
64865
65068
  const { visible, above, below } = windowReport(rows, scroll, PAGE_BODY_LINES);
@@ -64874,12 +65077,12 @@ function renderPageFrame(page, content, width, scroll) {
64874
65077
  const meta = `${page.type} · ${page.published ? "published" : "draft"} · updated ${page.updated_at.slice(0, 10)}`;
64875
65078
  const lines = [
64876
65079
  breadcrumb(["home", "pages", page.title], width),
64877
- import_picocolors37.default.dim(clip3(meta, width)),
65080
+ import_picocolors38.default.dim(clip3(meta, width)),
64878
65081
  "",
64879
65082
  ...body,
64880
- ...scrollHint ? [import_picocolors37.default.dim(scrollHint)] : [],
65083
+ ...scrollHint ? [import_picocolors38.default.dim(scrollHint)] : [],
64881
65084
  "",
64882
- import_picocolors37.default.dim("↑↓ scroll · esc back to pages")
65085
+ import_picocolors38.default.dim("↑↓ scroll · esc back to pages")
64883
65086
  ];
64884
65087
  return lines.join(`
64885
65088
  `);
@@ -65028,7 +65231,7 @@ ${CLEAR_BELOW3}`);
65028
65231
  input.on("data", onData);
65029
65232
  });
65030
65233
  }
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;
65234
+ 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
65235
  var init_pages_view = __esm(() => {
65033
65236
  init_trpc();
65034
65237
  init_config();
@@ -65038,7 +65241,7 @@ var init_pages_view = __esm(() => {
65038
65241
  init_analytics_view();
65039
65242
  init_layout();
65040
65243
  init_menu();
65041
- import_picocolors37 = __toESM(require_picocolors(), 1);
65244
+ import_picocolors38 = __toESM(require_picocolors(), 1);
65042
65245
  ESC9 = String.fromCharCode(27);
65043
65246
  HIDE_CURSOR5 = `${ESC9}[?25l`;
65044
65247
  SHOW_CURSOR5 = `${ESC9}[?25h`;
@@ -65152,7 +65355,7 @@ function isSetUp(cfg) {
65152
65355
  }
65153
65356
  function setupHint(cfg) {
65154
65357
  const started = Boolean(cfg.active_account?.target);
65155
- return import_picocolors38.default.yellow(started ? `incomplete · missing ${missingSetupSteps(cfg).join(" + ")}` : "not set up yet");
65358
+ return import_picocolors39.default.yellow(started ? `incomplete · missing ${missingSetupSteps(cfg).join(" + ")}` : "not set up yet");
65156
65359
  }
65157
65360
  function isStudying() {
65158
65361
  try {
@@ -65398,7 +65601,7 @@ function handleLogout(cfg) {
65398
65601
  saveConfig(cfg);
65399
65602
  log2.success("Credentials cleared.");
65400
65603
  }
65401
- var import_picocolors38, ESC10, CLEAR_SCREEN;
65604
+ var import_picocolors39, ESC10, CLEAR_SCREEN;
65402
65605
  var init_tui = __esm(() => {
65403
65606
  init_client();
65404
65607
  init_config();
@@ -65421,7 +65624,7 @@ var init_tui = __esm(() => {
65421
65624
  init_menu();
65422
65625
  init_pages_view();
65423
65626
  init_prompts();
65424
- import_picocolors38 = __toESM(require_picocolors(), 1);
65627
+ import_picocolors39 = __toESM(require_picocolors(), 1);
65425
65628
  ESC10 = String.fromCharCode(27);
65426
65629
  CLEAR_SCREEN = `${ESC10}[2J${ESC10}[H`;
65427
65630
  });
@@ -66025,18 +66228,15 @@ var init_flow3 = __esm(() => {
66025
66228
  });
66026
66229
 
66027
66230
  // 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";
66231
+ import { readFileSync as readFileSync28, unlinkSync as unlinkSync6 } from "node:fs";
66037
66232
  function shouldRunBackgroundChecks(actionName) {
66038
66233
  return actionName !== "upgrade";
66039
66234
  }
66235
+ function shouldRunMcpRefreshCheck(actionCommand) {
66236
+ if (actionCommand.name() === "setup")
66237
+ return false;
66238
+ return !(actionCommand.name() === "refresh" && actionCommand.parent?.name() === "mcp");
66239
+ }
66040
66240
  function commandTelemetryName(actionCommand) {
66041
66241
  const segments = [];
66042
66242
  let current = actionCommand;
@@ -66053,7 +66253,7 @@ function shouldTrackCommand(command) {
66053
66253
  }
66054
66254
  function commandTelemetryContext() {
66055
66255
  try {
66056
- const cfg = loadConfigForTelemetry();
66256
+ const cfg = loadConfigNonBlocking();
66057
66257
  if (!cfg)
66058
66258
  return { mode: "cloud", isAuthenticated: false };
66059
66259
  const authenticated = isAuthenticated(cfg);
@@ -66073,29 +66273,6 @@ function commandTelemetryContext() {
66073
66273
  return { mode: "cloud", isAuthenticated: false };
66074
66274
  }
66075
66275
  }
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
66276
  function startTelemetry(telemetry, command, context) {
66100
66277
  if (!telemetry)
66101
66278
  return false;
@@ -66154,12 +66331,15 @@ function createProgram(options = {}) {
66154
66331
  const opts = thisCommand.optsWithGlobals();
66155
66332
  logger.init({ debug: opts.debug });
66156
66333
  if (shouldRunBackgroundChecks(actionCommand.name())) {
66334
+ const launchesTUI = actionCommand.parent === null;
66157
66335
  if (!process.env.CI) {
66158
- const launchesTUI = actionCommand.parent === null;
66159
66336
  await checkForUpdates({ notify: !launchesTUI });
66160
66337
  }
66161
66338
  checkForSkillUpdates();
66162
66339
  checkForReadyTasks();
66340
+ if (shouldRunMcpRefreshCheck(actionCommand)) {
66341
+ checkForMcpRefresh({ notify: !launchesTUI });
66342
+ }
66163
66343
  }
66164
66344
  const command = commandTelemetryName(actionCommand);
66165
66345
  if (options.telemetry && shouldTrackCommand(command)) {
@@ -66386,6 +66566,31 @@ Start ${provider.name()} in any project to use the Dosu MCP.`);
66386
66566
  Start ${provider.name()} in this project directory to use the Dosu MCP.`);
66387
66567
  }
66388
66568
  });
66569
+ mcp.command("refresh").description("Rewrite the Dosu MCP entry in every configured AI tool from the current setup").action(() => {
66570
+ const cfg = loadConfig();
66571
+ if (!canRefreshMcp(cfg)) {
66572
+ throw new CliUsageError("Dosu is not set up yet. Run 'dosu setup' first");
66573
+ }
66574
+ const configured = configuredProviders();
66575
+ if (configured.length === 0) {
66576
+ console.log("No AI tools with Dosu configured. Run 'dosu setup' to add Dosu to a tool.");
66577
+ return;
66578
+ }
66579
+ console.log(`Refreshing Dosu MCP config for configured AI tools...
66580
+ `);
66581
+ const result = refreshConfiguredProviders(cfg);
66582
+ writeMcpRefreshCache({ version: VERSION });
66583
+ for (const provider of result.updated) {
66584
+ console.log(` ✓ ${provider.name()}`);
66585
+ }
66586
+ for (const { provider, error } of result.failed) {
66587
+ console.log(` ✗ ${provider.name()}: ${error.message}`);
66588
+ }
66589
+ console.log(`
66590
+ Restart your AI agents so they pick up the change.`);
66591
+ if (result.failed.length > 0)
66592
+ process.exitCode = 1;
66593
+ });
66389
66594
  mcp.command("list").description("List available AI tools").action(() => {
66390
66595
  console.log(`Available AI tools:
66391
66596
  `);
@@ -66466,7 +66671,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
66466
66671
  }
66467
66672
  if (opts.follow) {
66468
66673
  try {
66469
- const lines = readFileSync27(logPath, "utf-8").split(`
66674
+ const lines = readFileSync28(logPath, "utf-8").split(`
66470
66675
  `);
66471
66676
  if (lines.at(-1) === "")
66472
66677
  lines.pop();
@@ -66482,7 +66687,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
66482
66687
  if (opts.tail !== undefined) {
66483
66688
  const n = typeof opts.tail === "string" ? parseInt(opts.tail, 10) || 50 : 50;
66484
66689
  try {
66485
- const content = readFileSync27(logPath, "utf-8");
66690
+ const content = readFileSync28(logPath, "utf-8");
66486
66691
  const lines = content.split(`
66487
66692
  `);
66488
66693
  console.log(lines.slice(-n).join(`
@@ -66534,7 +66739,7 @@ function processCommandTelemetry() {
66534
66739
  return;
66535
66740
  }
66536
66741
  }
66537
- var TELEMETRY_FLUSH_TIMEOUT_MS = 750, MAX_TELEMETRY_CONFIG_BYTES, CliUsageError, FOLLOW_POLL_MS = 500, DEFAULT_TAIL_LINES = 50;
66742
+ var TELEMETRY_FLUSH_TIMEOUT_MS = 750, CliUsageError, FOLLOW_POLL_MS = 500, DEFAULT_TAIL_LINES = 50;
66538
66743
  var init_cli = __esm(() => {
66539
66744
  init_commander();
66540
66745
  init_client();
@@ -66560,13 +66765,14 @@ var init_cli = __esm(() => {
66560
66765
  init_follow();
66561
66766
  init_logger();
66562
66767
  init_providers();
66768
+ init_refresh();
66563
66769
  init_styles();
66564
66770
  init_settings();
66565
66771
  init_telemetry();
66772
+ init_mcp_refresh_check();
66566
66773
  init_pending_tasks_check();
66567
66774
  init_skill_update_check();
66568
66775
  init_update_check();
66569
- MAX_TELEMETRY_CONFIG_BYTES = 64 * 1024;
66570
66776
  CliUsageError = class CliUsageError extends Error {
66571
66777
  exitCode = 1;
66572
66778
  constructor(message) {
@@ -66607,4 +66813,4 @@ main().catch((err) => {
66607
66813
  process.exit(1);
66608
66814
  });
66609
66815
 
66610
- //# debugId=93db7db7-907f-4d14-6475-6e2164756e21
66816
+ //# debugId=ca9e58ba-ed30-6875-6475-6e2164756e21