@dosu/cli 0.67.1 → 0.67.2

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 +1 -1
  2. package/bin/dosu.js +445 -337
  3. package/package.json +1 -1
package/bin/dosu.js CHANGED
@@ -961,6 +961,10 @@ function mcpHeaders(apiKey) {
961
961
  }
962
962
  return { "X-Dosu-API-Key": apiKey };
963
963
  }
964
+ function npxRemoteEntry(url, apiKey, npx, path) {
965
+ const remote = mcpRemoteServer(url, apiKey);
966
+ return { command: npx, args: remote.args, env: { PATH: path, ...remote.env } };
967
+ }
964
968
  function mcpRemoteServer(url, apiKey) {
965
969
  const env = {};
966
970
  const headerArgs = Object.entries(mcpHeaders(apiKey)).flatMap(([key, value]) => {
@@ -1081,6 +1085,12 @@ function isJSONKeyConfigured(configPath, topLevelKey) {
1081
1085
  return false;
1082
1086
  return "dosu" in section;
1083
1087
  }
1088
+ function readJSONServer(configPath, topLevelKey) {
1089
+ const section = loadJSONConfig(configPath)[topLevelKey];
1090
+ if (typeof section !== "object" || section === null)
1091
+ return;
1092
+ return section.dosu;
1093
+ }
1084
1094
  function installJSONServer(configPath, topKey, server) {
1085
1095
  const jsonCfg = readJSONConfig(configPath);
1086
1096
  let section = jsonCfg[topKey];
@@ -1118,7 +1128,7 @@ function isNpxInvocation(channel = INSTALL_CHANNEL, env = process.env) {
1118
1128
  function getVersionString() {
1119
1129
  return `v${VERSION}`;
1120
1130
  }
1121
- var VERSION = "0.67.1", INSTALL_CHANNEL = "npm";
1131
+ var VERSION = "0.67.2", INSTALL_CHANNEL = "npm";
1122
1132
 
1123
1133
  // src/debug/logger.ts
1124
1134
  import {
@@ -14323,7 +14333,7 @@ var init_conflicts = __esm(() => {
14323
14333
  });
14324
14334
 
14325
14335
  // src/telemetry/debug-id.ts
14326
- var BUNDLE_DEBUG_ID_PLACEHOLDER = "152ccbaa-13cc-83fc-6475-6e2164756e21";
14336
+ var BUNDLE_DEBUG_ID_PLACEHOLDER = "fa693edb-14f4-abcb-6475-6e2164756e21";
14327
14337
 
14328
14338
  // src/telemetry/telemetry.ts
14329
14339
  import { randomUUID as randomUUID2 } from "node:crypto";
@@ -62482,6 +62492,65 @@ var init_topics = __esm(() => {
62482
62492
  import_picocolors31 = __toESM(require_picocolors(), 1);
62483
62493
  });
62484
62494
 
62495
+ // src/mcp/shape.ts
62496
+ function shapeEndpoint(cfg) {
62497
+ const url = cfg?.mode === MODE_OSS ? mcpEndpoint(cfg) : mcpURL(ANY);
62498
+ return ANY + url.slice(getBackendURL().length);
62499
+ }
62500
+ function escapeRegExp(text) {
62501
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
62502
+ }
62503
+ function hasShape(expected, actual) {
62504
+ if (typeof expected === "string") {
62505
+ if (typeof actual !== "string")
62506
+ return false;
62507
+ if (!expected.includes(ANY))
62508
+ return expected === actual;
62509
+ const pattern = expected.split(ANY).map(escapeRegExp).join("[\\s\\S]*");
62510
+ return new RegExp(`^${pattern}$`).test(actual);
62511
+ }
62512
+ if (Array.isArray(expected)) {
62513
+ return Array.isArray(actual) && actual.length === expected.length && expected.every((item, i) => hasShape(item, actual[i]));
62514
+ }
62515
+ if (typeof expected === "object" && expected !== null) {
62516
+ if (typeof actual !== "object" || actual === null || Array.isArray(actual))
62517
+ return false;
62518
+ const expectedKeys = Object.keys(expected);
62519
+ const actualRecord = actual;
62520
+ return expectedKeys.length === Object.keys(actualRecord).length && expectedKeys.every((key) => (key in actualRecord) && hasShape(expected[key], actualRecord[key]));
62521
+ }
62522
+ return expected === actual;
62523
+ }
62524
+ function withoutUserChoices(entry) {
62525
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry))
62526
+ return entry;
62527
+ return Object.fromEntries(Object.entries(entry).filter(([key]) => !USER_CHOICE_KEYS.has(key)));
62528
+ }
62529
+ function entryHasShape(expected, actual) {
62530
+ return hasShape(withoutUserChoices(expected), withoutUserChoices(actual));
62531
+ }
62532
+ var ANY = "\x00any\x00", USER_CHOICE_KEYS;
62533
+ var init_shape = __esm(() => {
62534
+ init_config();
62535
+ init_config_helpers();
62536
+ USER_CHOICE_KEYS = new Set([
62537
+ "disabled",
62538
+ "enabled",
62539
+ "autoApprove",
62540
+ "disabledTools",
62541
+ "timeout",
62542
+ "trust",
62543
+ "includeTools",
62544
+ "excludeTools",
62545
+ "enabled_tools",
62546
+ "disabled_tools",
62547
+ "default_tools_approval_mode",
62548
+ "required",
62549
+ "startup_timeout_sec",
62550
+ "tool_timeout_sec"
62551
+ ]);
62552
+ });
62553
+
62485
62554
  // src/mcp/providers/base.ts
62486
62555
  function createJSONProvider(opts) {
62487
62556
  const buildServer = opts.buildServer ?? defaultBuildServer;
@@ -62494,6 +62563,7 @@ function createJSONProvider(opts) {
62494
62563
  isInstalled: () => isInstalled(opts.paths),
62495
62564
  globalConfigPath: () => expandHome(opts.globalPath),
62496
62565
  isConfigured: () => isJSONKeyConfigured(expandHome(opts.globalPath), opts.topKey),
62566
+ isCurrent: (cfg) => entryHasShape(buildServer({ url: shapeEndpoint(cfg), headers: mcpHeaders(ANY) }), readJSONServer(expandHome(opts.globalPath), opts.topKey)),
62497
62567
  install(cfg, global2) {
62498
62568
  const endpoint = {
62499
62569
  url: mcpEndpoint(cfg),
@@ -62507,7 +62577,7 @@ function createJSONProvider(opts) {
62507
62577
  } else {
62508
62578
  throw new Error(`${opts.providerName} does not support local installation`);
62509
62579
  }
62510
- installJSONServer(configPath, opts.topKey, buildServer(endpoint, cfg));
62580
+ installJSONServer(configPath, opts.topKey, buildServer(endpoint));
62511
62581
  },
62512
62582
  remove(global2) {
62513
62583
  let configPath;
@@ -62530,6 +62600,7 @@ var defaultBuildServer = ({ url, headers }) => ({
62530
62600
  var init_base = __esm(() => {
62531
62601
  init_config_helpers();
62532
62602
  init_detect();
62603
+ init_shape();
62533
62604
  });
62534
62605
 
62535
62606
  // src/mcp/providers/antigravity.ts
@@ -62581,17 +62652,13 @@ var ClaudeDesktopProvider = () => ({
62581
62652
  isInstalled: () => isInstalled([join26(appSupportDir(), "Claude")]),
62582
62653
  globalConfigPath: () => configPath(),
62583
62654
  isConfigured: () => isJSONKeyConfigured(configPath(), "mcpServers"),
62655
+ isCurrent: (cfg) => entryHasShape(npxRemoteEntry(shapeEndpoint(cfg), ANY, ANY, npxPathEnv(join26(ANY, "npx"))), readJSONServer(configPath(), "mcpServers")),
62584
62656
  install(cfg, global2) {
62585
62657
  if (!global2)
62586
62658
  throw new Error("Claude Desktop does not support local installation");
62587
62659
  const url = mcpEndpoint(cfg);
62588
62660
  const npx = findNpx();
62589
- const remote = mcpRemoteServer(url, cfg.active_account?.target?.api_key);
62590
- installJSONServer(configPath(), "mcpServers", {
62591
- command: npx,
62592
- args: remote.args,
62593
- env: { PATH: npxPathEnv(npx), ...remote.env }
62594
- });
62661
+ installJSONServer(configPath(), "mcpServers", npxRemoteEntry(url, cfg.active_account?.target?.api_key, npx, npxPathEnv(npx)));
62595
62662
  },
62596
62663
  remove(global2) {
62597
62664
  if (!global2)
@@ -62602,6 +62669,7 @@ var ClaudeDesktopProvider = () => ({
62602
62669
  var init_claude_desktop = __esm(() => {
62603
62670
  init_config_helpers();
62604
62671
  init_detect();
62672
+ init_shape();
62605
62673
  });
62606
62674
 
62607
62675
  // src/mcp/providers/cline.ts
@@ -62673,25 +62741,70 @@ function writeTOML(path, content) {
62673
62741
  function tomlString(value) {
62674
62742
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
62675
62743
  }
62744
+ function tomlValue(value) {
62745
+ return Array.isArray(value) ? `[${value.map(tomlString).join(", ")}]` : tomlString(value);
62746
+ }
62747
+ function dosuEntry(url, apiKey, npx, path) {
62748
+ return npxRemoteEntry(url, apiKey, npx, path);
62749
+ }
62750
+ function renderDosuEntry(entry) {
62751
+ const tables = [["mcp_servers.dosu", {}]];
62752
+ for (const [key, value] of Object.entries(entry)) {
62753
+ if (typeof value === "string" || Array.isArray(value))
62754
+ tables[0][1][key] = value;
62755
+ else
62756
+ tables.push([`mcp_servers.dosu.${key}`, value]);
62757
+ }
62758
+ return tables.map(([name, keys]) => {
62759
+ const lines = Object.entries(keys).map(([key, value]) => `${key} = ${tomlValue(value)}`);
62760
+ return `
62761
+ [${name}]
62762
+ ${lines.join(`
62763
+ `)}
62764
+ `;
62765
+ }).join("");
62766
+ }
62767
+ function isToolApproval(name) {
62768
+ return name.startsWith("mcp_servers.dosu.tools.");
62769
+ }
62770
+ function readDosuEntry(content) {
62771
+ let entry;
62772
+ let table;
62773
+ for (const line of content.split(`
62774
+ `)) {
62775
+ const name = sectionName(line);
62776
+ if (name !== null) {
62777
+ table = undefined;
62778
+ if (!isDosuSection(name) || isToolApproval(name))
62779
+ continue;
62780
+ entry ??= {};
62781
+ if (name === "mcp_servers.dosu") {
62782
+ table = entry;
62783
+ } else {
62784
+ table = {};
62785
+ entry[name.slice("mcp_servers.dosu.".length)] = table;
62786
+ }
62787
+ continue;
62788
+ }
62789
+ const text = line.trim();
62790
+ if (!table || !text || text.startsWith("#"))
62791
+ continue;
62792
+ const eq = text.indexOf("=");
62793
+ const key = eq === -1 ? text : text.slice(0, eq).trim();
62794
+ const raw = eq === -1 ? "" : text.slice(eq + 1).trim();
62795
+ try {
62796
+ table[key] = JSON.parse(raw);
62797
+ } catch {
62798
+ table[key] = raw;
62799
+ }
62800
+ }
62801
+ return entry;
62802
+ }
62676
62803
  function installDosuToTOML(path, cfg) {
62677
- let content = readTOML(path);
62678
- content = removeDosuFromTOML(content);
62804
+ const content = removeDosuFromTOML(readTOML(path));
62679
62805
  const npx = findNpx();
62680
- const remote = mcpRemoteServer(mcpEndpoint(cfg), cfg.active_account?.target?.api_key);
62681
- const env = { PATH: npxPathEnv(npx), ...remote.env };
62682
- const envEntries = Object.entries(env).map(([key, value]) => `${key} = ${tomlString(value)}`).join(`
62683
- `);
62684
- const args = remote.args.map(tomlString).join(", ");
62685
- const section = `
62686
- [mcp_servers.dosu]
62687
- command = ${tomlString(npx)}
62688
- args = [${args}]
62689
- ` + `
62690
- [mcp_servers.dosu.env]
62691
- ${envEntries}
62692
- `;
62693
- content += section;
62694
- writeTOML(path, content);
62806
+ const entry = dosuEntry(mcpEndpoint(cfg), cfg.active_account?.target?.api_key, npx, npxPathEnv(npx));
62807
+ writeTOML(path, content + renderDosuEntry(entry));
62695
62808
  }
62696
62809
  function sectionName(line) {
62697
62810
  const header = line.trim();
@@ -62769,6 +62882,7 @@ var REMOTE_HTTP_KEY, CodexProvider = () => ({
62769
62882
  const content = readTOML(join29(codexHome3(), "config.toml"));
62770
62883
  return content.includes("[mcp_servers.dosu]");
62771
62884
  },
62885
+ isCurrent: (cfg) => entryHasShape(dosuEntry(shapeEndpoint(cfg), ANY, ANY, npxPathEnv(join29(ANY, "npx"))), readDosuEntry(readTOML(join29(codexHome3(), "config.toml")))),
62772
62886
  install(cfg, global2) {
62773
62887
  if (cfg.mode !== MODE_OSS && !cfg.active_account?.target?.deployment_id)
62774
62888
  throw new Error("deployment ID is required");
@@ -62793,11 +62907,15 @@ var init_codex = __esm(() => {
62793
62907
  init_config();
62794
62908
  init_config_helpers();
62795
62909
  init_detect();
62910
+ init_shape();
62796
62911
  REMOTE_HTTP_KEY = /^(url|type|bearer_token_env_var)\s*=/;
62797
62912
  });
62798
62913
 
62799
62914
  // src/mcp/providers/copilot.ts
62800
62915
  import { join as join30 } from "node:path";
62916
+ function globalServer(url, apiKey) {
62917
+ return { type: "http", url, tools: ["*"], headers: mcpHeaders(apiKey) };
62918
+ }
62801
62919
  function globalPath() {
62802
62920
  if (process.env.XDG_CONFIG_HOME) {
62803
62921
  return join30(process.env.XDG_CONFIG_HOME, "mcp-config.json");
@@ -62813,16 +62931,11 @@ var CopilotProvider = () => ({
62813
62931
  isInstalled: () => isInstalled([expandHome("~/.copilot")]),
62814
62932
  globalConfigPath: () => globalPath(),
62815
62933
  isConfigured: () => isJSONKeyConfigured(globalPath(), "mcpServers"),
62934
+ isCurrent: (cfg) => entryHasShape(globalServer(shapeEndpoint(cfg), ANY), readJSONServer(globalPath(), "mcpServers")),
62816
62935
  install(cfg, global2) {
62817
62936
  const url = mcpEndpoint(cfg);
62818
62937
  if (global2) {
62819
- const server = {
62820
- type: "http",
62821
- url,
62822
- tools: ["*"],
62823
- headers: mcpHeaders(cfg.active_account.target.api_key)
62824
- };
62825
- installJSONServer(globalPath(), "mcpServers", server);
62938
+ installJSONServer(globalPath(), "mcpServers", globalServer(url, cfg.active_account?.target?.api_key));
62826
62939
  } else {
62827
62940
  const configPath = join30(process.cwd(), ".vscode", "mcp.json");
62828
62941
  const server = {
@@ -62845,6 +62958,7 @@ var CopilotProvider = () => ({
62845
62958
  var init_copilot = __esm(() => {
62846
62959
  init_config_helpers();
62847
62960
  init_detect();
62961
+ init_shape();
62848
62962
  });
62849
62963
 
62850
62964
  // src/mcp/providers/cursor.ts
@@ -62937,6 +63051,9 @@ var init_manual = __esm(() => {
62937
63051
  // src/mcp/providers/mcporter.ts
62938
63052
  import { existsSync as existsSync23 } from "node:fs";
62939
63053
  import { join as join34 } from "node:path";
63054
+ function server(url, apiKey) {
63055
+ return { type: "http", url, headers: mcpHeaders(apiKey) };
63056
+ }
62940
63057
  function resolveGlobalConfigPath() {
62941
63058
  const jsonPath = expandHome("~/.mcporter/mcporter.json");
62942
63059
  if (existsSync23(jsonPath))
@@ -62955,14 +63072,10 @@ var MCPorterProvider = () => ({
62955
63072
  isInstalled: () => isInstalled(["~/.mcporter"]),
62956
63073
  globalConfigPath: () => resolveGlobalConfigPath(),
62957
63074
  isConfigured: () => isJSONKeyConfigured(resolveGlobalConfigPath(), "mcpServers"),
63075
+ isCurrent: (cfg) => entryHasShape(server(shapeEndpoint(cfg), ANY), readJSONServer(resolveGlobalConfigPath(), "mcpServers")),
62958
63076
  install(cfg, global2) {
62959
63077
  const configPath = global2 ? resolveGlobalConfigPath() : join34(process.cwd(), "config", "mcporter.json");
62960
- const server = {
62961
- type: "http",
62962
- url: mcpEndpoint(cfg),
62963
- headers: mcpHeaders(cfg.active_account.target.api_key)
62964
- };
62965
- installJSONServer(configPath, "mcpServers", server);
63078
+ installJSONServer(configPath, "mcpServers", server(mcpEndpoint(cfg), cfg.active_account?.target?.api_key));
62966
63079
  },
62967
63080
  remove(global2) {
62968
63081
  const configPath = global2 ? resolveGlobalConfigPath() : join34(process.cwd(), "config", "mcporter.json");
@@ -62972,6 +63085,7 @@ var MCPorterProvider = () => ({
62972
63085
  var init_mcporter = __esm(() => {
62973
63086
  init_config_helpers();
62974
63087
  init_detect();
63088
+ init_shape();
62975
63089
  });
62976
63090
 
62977
63091
  // src/mcp/providers/opencode.ts
@@ -63085,6 +63199,7 @@ var ZedProvider = (legacyPath = legacyDarwinSettingsPath()) => {
63085
63199
  return provider;
63086
63200
  return {
63087
63201
  ...provider,
63202
+ isCurrent: (cfg) => provider.isCurrent(cfg) && readJSONServer(legacyPath, "context_servers") === undefined,
63088
63203
  install(cfg, global2) {
63089
63204
  provider.install(cfg, global2);
63090
63205
  if (global2)
@@ -63163,9 +63278,21 @@ function configuredProviders() {
63163
63278
  }
63164
63279
  });
63165
63280
  }
63281
+ function staleProviders(cfg) {
63282
+ return configuredProviders().filter((provider) => {
63283
+ try {
63284
+ return !provider.isCurrent(cfg);
63285
+ } catch {
63286
+ return true;
63287
+ }
63288
+ });
63289
+ }
63166
63290
  function refreshConfiguredProviders(cfg) {
63291
+ return refreshProviders(cfg, configuredProviders());
63292
+ }
63293
+ function refreshProviders(cfg, providers) {
63167
63294
  const result = { updated: [], failed: [] };
63168
- for (const provider of configuredProviders()) {
63295
+ for (const provider of providers) {
63169
63296
  try {
63170
63297
  provider.install(cfg, true);
63171
63298
  logger.info("mcp", `Refreshed ${provider.name()} MCP entry`);
@@ -63377,199 +63504,18 @@ var init_auto_update = __esm(() => {
63377
63504
  VERSION_PATTERN = /^[0-9A-Za-z.+-]{1,64}$/;
63378
63505
  });
63379
63506
 
63380
- // src/version/update-check.ts
63507
+ // src/version/mcp-refresh-check.ts
63381
63508
  import { existsSync as existsSync25, mkdirSync as mkdirSync15, readFileSync as readFileSync24, writeFileSync as writeFileSync16 } from "node:fs";
63382
63509
  import { join as join40 } from "node:path";
63383
- function isValidVersion2(value) {
63384
- return typeof value === "string" && SEMVER_PATTERN.test(value);
63385
- }
63386
- function stripPrerelease(version) {
63387
- return version.replace(/[-+].*$/, "");
63388
- }
63389
- function isNewerVersion(latest, current) {
63390
- const a = stripPrerelease(latest).split(".").map(Number);
63391
- const b = stripPrerelease(current).split(".").map(Number);
63392
- const len = Math.max(a.length, b.length);
63393
- for (let i = 0;i < len; i++) {
63394
- const av = a[i] ?? 0;
63395
- const bv = b[i] ?? 0;
63396
- if (av > bv)
63397
- return true;
63398
- if (av < bv)
63399
- return false;
63400
- }
63401
- return false;
63402
- }
63403
63510
  function getCachePath2() {
63404
63511
  return join40(getConfigDir(), CACHE_FILENAME3);
63405
63512
  }
63406
- function readCache() {
63513
+ function readMcpRefreshCache() {
63407
63514
  try {
63408
63515
  const path = getCachePath2();
63409
63516
  if (!existsSync25(path))
63410
63517
  return null;
63411
63518
  const data = JSON.parse(readFileSync24(path, "utf-8"));
63412
- if (typeof data.lastCheck === "number" && isValidVersion2(data.latestVersion)) {
63413
- return data;
63414
- }
63415
- return null;
63416
- } catch {
63417
- return null;
63418
- }
63419
- }
63420
- function writeCache(cache) {
63421
- try {
63422
- const dir = getConfigDir();
63423
- if (!existsSync25(dir)) {
63424
- mkdirSync15(dir, { recursive: true, mode: 448 });
63425
- }
63426
- writeFileSync16(getCachePath2(), JSON.stringify(cache), { mode: 384 });
63427
- } catch {}
63428
- }
63429
- async function fetchLatestVersion() {
63430
- const controller = new AbortController;
63431
- const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS2);
63432
- try {
63433
- const resp = await fetch(REGISTRY_URL, { signal: controller.signal });
63434
- if (!resp.ok)
63435
- return null;
63436
- const data = await resp.json();
63437
- const latest = data.latest;
63438
- return isValidVersion2(latest) ? latest : null;
63439
- } catch {
63440
- return null;
63441
- } finally {
63442
- clearTimeout(timeout);
63443
- }
63444
- }
63445
- function buildUpdateHint(channel, npx = false) {
63446
- if (channel === "npm" && npx) {
63447
- return 'Use "npx -y @dosu/cli@latest" for the next Dosu command';
63448
- }
63449
- return 'Run "dosu upgrade"';
63450
- }
63451
- function styleHint(hint) {
63452
- const match = hint.match(/^(.*)("[^"]+")(.*)$/);
63453
- if (!match)
63454
- return import_picocolors32.default.dim(hint);
63455
- const [, before, command, after] = match;
63456
- return [before && import_picocolors32.default.dim(before), import_picocolors32.default.cyan(command), after && import_picocolors32.default.dim(after)].filter(Boolean).join("");
63457
- }
63458
- function framedNotice(content, width) {
63459
- const innerWidth = Math.max(...content.map(visibleWidth2)) + BOX_PADDING * 2;
63460
- const framed = (line) => {
63461
- const pad = innerWidth - visibleWidth2(line);
63462
- const left = Math.floor(pad / 2);
63463
- return `${import_picocolors32.default.yellow("│")}${" ".repeat(left)}${line}${" ".repeat(pad - left)}${import_picocolors32.default.yellow("│")}`;
63464
- };
63465
- const box = [
63466
- import_picocolors32.default.yellow(`╭${"─".repeat(innerWidth)}╮`),
63467
- framed(""),
63468
- ...content.map(framed),
63469
- framed(""),
63470
- import_picocolors32.default.yellow(`╰${"─".repeat(innerWidth)}╯`)
63471
- ];
63472
- return `
63473
- ${centerBlock(box, width).join(`
63474
- `)}
63475
- `;
63476
- }
63477
- function versionArrow(current, latest) {
63478
- return `${import_picocolors32.default.dim(current)} ${import_picocolors32.default.dim("→")} ${import_picocolors32.default.bold(brand(latest))}`;
63479
- }
63480
- function buildAutoUpdateNotice(current, latest, interactive, width = process.stderr.columns ?? 80) {
63481
- if (interactive) {
63482
- return framedNotice([
63483
- `Updating Dosu: ${versionArrow(current, latest)}`,
63484
- import_picocolors32.default.dim("Installing in the background; your next command uses it")
63485
- ], width);
63486
- }
63487
- return `
63488
- [dosu:update] Installing Dosu ${latest} in the background (running ${current}). ` + `The next Dosu command uses it; no action needed.
63489
- `;
63490
- }
63491
- function buildUpdateNotice(current, latest, channel, interactive, npx = false, width = process.stderr.columns ?? 80) {
63492
- const hint = buildUpdateHint(channel, npx);
63493
- if (interactive) {
63494
- return framedNotice([`Update available: ${versionArrow(current, latest)}`, styleHint(hint)], width);
63495
- }
63496
- const agentAction = hint[0].toLowerCase() + hint.slice(1);
63497
- const verifyCommand = npx ? "npx -y @dosu/cli@latest --version" : "dosu --version";
63498
- return `
63499
- [dosu:update] Update available: ${current} → ${latest}
63500
- ` + `Tell the user Dosu CLI is outdated. After they approve, ${agentAction}, ` + `then verify with "${verifyCommand}".
63501
- `;
63502
- }
63503
- function getAvailableUpdate() {
63504
- const cache = readCache();
63505
- return cache && isNewerVersion(cache.latestVersion, VERSION) ? cache.latestVersion : null;
63506
- }
63507
- function handleNewerVersion(latest, notify) {
63508
- const autoUpdate = startAutoUpdate(latest);
63509
- if (!notify)
63510
- return;
63511
- const interactive = process.stderr.isTTY === true;
63512
- console.error(autoUpdate === "unavailable" ? buildUpdateNotice(VERSION, latest, INSTALL_CHANNEL, interactive, isNpxInvocation()) : buildAutoUpdateNotice(VERSION, latest, interactive));
63513
- }
63514
- async function checkForUpdates(options = {}) {
63515
- const notify = options.notify ?? true;
63516
- try {
63517
- const cache = readCache();
63518
- const isStale = !cache || Date.now() - cache.lastCheck > CHECK_INTERVAL_MS2;
63519
- if (!isStale) {
63520
- if (isNewerVersion(cache.latestVersion, VERSION)) {
63521
- handleNewerVersion(cache.latestVersion, notify);
63522
- }
63523
- return;
63524
- }
63525
- const latest = await fetchLatestVersion();
63526
- const latestKnownVersion = latest ?? cache?.latestVersion ?? VERSION;
63527
- writeCache({
63528
- lastCheck: Date.now(),
63529
- latestVersion: latestKnownVersion
63530
- });
63531
- if (latest) {
63532
- logger.debug("update-check", `Cached latest version: ${latest}`);
63533
- }
63534
- if (isNewerVersion(latestKnownVersion, VERSION)) {
63535
- handleNewerVersion(latestKnownVersion, notify);
63536
- }
63537
- } catch (err) {
63538
- logger.error("update-check", `Update check failed: ${err}`);
63539
- }
63540
- }
63541
- var import_picocolors32, CACHE_FILENAME3 = "update-check.json", CHECK_INTERVAL_MS2, FETCH_TIMEOUT_MS2 = 1000, REGISTRY_URL = "https://registry.npmjs.org/-/package/@dosu/cli/dist-tags", SEMVER_PATTERN, BOX_PADDING = 3;
63542
- var init_update_check = __esm(() => {
63543
- init_config();
63544
- init_logger();
63545
- init_styles();
63546
- init_layout();
63547
- init_auto_update();
63548
- import_picocolors32 = __toESM(require_picocolors(), 1);
63549
- CHECK_INTERVAL_MS2 = 6 * 60 * 60 * 1000;
63550
- 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-]+)*)?$/;
63551
- });
63552
-
63553
- // src/version/mcp-refresh-check.ts
63554
- import { existsSync as existsSync26, mkdirSync as mkdirSync16, readFileSync as readFileSync25, writeFileSync as writeFileSync17 } from "node:fs";
63555
- import { join as join41 } from "node:path";
63556
- function needsMcpRefresh(previous, current) {
63557
- if (previous === null)
63558
- return true;
63559
- if (previous === current)
63560
- return false;
63561
- const [older, newer] = isNewerVersion(current, previous) ? [previous, current] : [current, previous];
63562
- return MCP_FORMAT_CHANGES.some((change) => isNewerVersion(change, older) && !isNewerVersion(change, newer));
63563
- }
63564
- function getCachePath3() {
63565
- return join41(getConfigDir(), CACHE_FILENAME4);
63566
- }
63567
- function readMcpRefreshCache() {
63568
- try {
63569
- const path = getCachePath3();
63570
- if (!existsSync26(path))
63571
- return null;
63572
- const data = JSON.parse(readFileSync25(path, "utf-8"));
63573
63519
  if (typeof data?.version === "string")
63574
63520
  return { version: data.version };
63575
63521
  return null;
@@ -63580,10 +63526,10 @@ function readMcpRefreshCache() {
63580
63526
  function writeMcpRefreshCache(cache) {
63581
63527
  try {
63582
63528
  const dir = getConfigDir();
63583
- if (!existsSync26(dir)) {
63584
- mkdirSync16(dir, { recursive: true, mode: 448 });
63529
+ if (!existsSync25(dir)) {
63530
+ mkdirSync15(dir, { recursive: true, mode: 448 });
63585
63531
  }
63586
- writeFileSync17(getCachePath3(), JSON.stringify(cache), { mode: 384 });
63532
+ writeFileSync16(getCachePath2(), JSON.stringify(cache), { mode: 384 });
63587
63533
  } catch {}
63588
63534
  }
63589
63535
  function canRefreshMcp(cfg) {
@@ -63594,26 +63540,21 @@ function canRefreshMcp(cfg) {
63594
63540
  }
63595
63541
  function displayNotice(names) {
63596
63542
  console.error(`
63597
- ${import_picocolors33.default.green(`✓ Dosu ${VERSION}: refreshed MCP config for ${names.join(", ")}`)}
63598
- ` + `${import_picocolors33.default.dim(` Run ${import_picocolors33.default.cyan('"dosu setup"')} to also update hooks and rules, then restart your AI agents.`)}
63543
+ ${import_picocolors32.default.green(`✓ Dosu ${VERSION}: refreshed MCP config for ${names.join(", ")}`)}
63544
+ ` + `${import_picocolors32.default.dim(` Run ${import_picocolors32.default.cyan('"dosu setup"')} to also update hooks and rules, then restart your AI agents.`)}
63599
63545
  `);
63600
63546
  }
63601
63547
  function checkForMcpRefresh(options = {}) {
63602
63548
  const notify = options.notify ?? true;
63603
63549
  try {
63604
- const previous = readMcpRefreshCache()?.version ?? null;
63605
- if (previous === VERSION)
63606
- return;
63607
- if (!needsMcpRefresh(previous, VERSION)) {
63608
- writeMcpRefreshCache({ version: VERSION });
63550
+ if (readMcpRefreshCache()?.version === VERSION)
63609
63551
  return;
63610
- }
63611
63552
  const cfg = loadConfigNonBlocking();
63612
63553
  if (!cfg || !canRefreshMcp(cfg)) {
63613
63554
  logger.debug("mcp-refresh", "Skipping MCP refresh: no API key or deployment configured");
63614
63555
  return;
63615
63556
  }
63616
- const result = refreshConfiguredProviders(cfg);
63557
+ const result = refreshProviders(cfg, staleProviders(cfg));
63617
63558
  writeMcpRefreshCache({ version: VERSION });
63618
63559
  logger.info("mcp-refresh", `Version ${VERSION}: refreshed ${result.updated.length}, failed ${result.failed.length}`);
63619
63560
  if (notify && result.updated.length > 0) {
@@ -63623,24 +63564,19 @@ function checkForMcpRefresh(options = {}) {
63623
63564
  logger.error("mcp-refresh", `MCP refresh check failed: ${err}`);
63624
63565
  }
63625
63566
  }
63626
- var import_picocolors33, CACHE_FILENAME4 = "mcp-refresh.json", MCP_FORMAT_CHANGES;
63567
+ var import_picocolors32, CACHE_FILENAME3 = "mcp-refresh.json";
63627
63568
  var init_mcp_refresh_check = __esm(() => {
63628
63569
  init_config();
63629
63570
  init_logger();
63630
63571
  init_refresh();
63631
- init_update_check();
63632
- import_picocolors33 = __toESM(require_picocolors(), 1);
63633
- MCP_FORMAT_CHANGES = [
63634
- "0.53.0",
63635
- "0.62.0"
63636
- ];
63572
+ import_picocolors32 = __toESM(require_picocolors(), 1);
63637
63573
  });
63638
63574
 
63639
63575
  // src/version/skill-update-check.ts
63640
63576
  function displayNotice2() {
63641
63577
  console.error(`
63642
- ${import_picocolors34.default.green(`✓ Dosu ${VERSION}: refreshed the bundled agent skills`)}
63643
- ` + `${import_picocolors34.default.dim(" Restart your AI agents to pick up the new skill content.")}
63578
+ ${import_picocolors33.default.green(`✓ Dosu ${VERSION}: refreshed the bundled agent skills`)}
63579
+ ` + `${import_picocolors33.default.dim(" Restart your AI agents to pick up the new skill content.")}
63644
63580
  `);
63645
63581
  }
63646
63582
  function checkForSkillUpdates(options = {}) {
@@ -63661,11 +63597,11 @@ function checkForSkillUpdates(options = {}) {
63661
63597
  logger.error("skill-update-check", `Skill update check failed: ${err}`);
63662
63598
  }
63663
63599
  }
63664
- var import_picocolors34;
63600
+ var import_picocolors33;
63665
63601
  var init_skill_update_check = __esm(() => {
63666
63602
  init_skill();
63667
63603
  init_logger();
63668
- import_picocolors34 = __toESM(require_picocolors(), 1);
63604
+ import_picocolors33 = __toESM(require_picocolors(), 1);
63669
63605
  });
63670
63606
 
63671
63607
  // src/auth/errors.ts
@@ -63731,15 +63667,15 @@ function createLogFollower(path, emit) {
63731
63667
  var init_follow = () => {};
63732
63668
 
63733
63669
  // src/sessions/session-title.ts
63734
- import { existsSync as existsSync27, mkdirSync as mkdirSync17, readFileSync as readFileSync26, renameSync as renameSync7, statSync as statSync10, writeFileSync as writeFileSync18 } from "node:fs";
63670
+ import { existsSync as existsSync26, mkdirSync as mkdirSync16, readFileSync as readFileSync25, renameSync as renameSync7, statSync as statSync10, writeFileSync as writeFileSync17 } from "node:fs";
63735
63671
  import { homedir as homedir11 } from "node:os";
63736
- import { join as join42 } from "node:path";
63672
+ import { join as join41 } from "node:path";
63737
63673
  function cachePath2(configDir) {
63738
- return join42(configDir, CACHE_FILENAME5);
63674
+ return join41(configDir, CACHE_FILENAME4);
63739
63675
  }
63740
63676
  function loadCacheFile2(configDir) {
63741
63677
  try {
63742
- const raw = JSON.parse(readFileSync26(cachePath2(configDir), "utf-8"));
63678
+ const raw = JSON.parse(readFileSync25(cachePath2(configDir), "utf-8"));
63743
63679
  if (raw.schema_version !== CACHE_SCHEMA_VERSION2 || typeof raw.entries !== "object")
63744
63680
  return {};
63745
63681
  return raw.entries;
@@ -63749,12 +63685,12 @@ function loadCacheFile2(configDir) {
63749
63685
  }
63750
63686
  function saveCacheFile2(configDir, entries) {
63751
63687
  try {
63752
- if (!existsSync27(configDir))
63753
- mkdirSync17(configDir, { recursive: true, mode: 448 });
63688
+ if (!existsSync26(configDir))
63689
+ mkdirSync16(configDir, { recursive: true, mode: 448 });
63754
63690
  const path = cachePath2(configDir);
63755
63691
  const tmp = `${path}.${process.pid}.tmp`;
63756
63692
  const file = { schema_version: CACHE_SCHEMA_VERSION2, entries };
63757
- writeFileSync18(tmp, JSON.stringify(file), { mode: 384 });
63693
+ writeFileSync17(tmp, JSON.stringify(file), { mode: 384 });
63758
63694
  renameSync7(tmp, path);
63759
63695
  } catch {}
63760
63696
  }
@@ -63787,15 +63723,15 @@ function claudeSummaryFromHead(text) {
63787
63723
  }
63788
63724
  return null;
63789
63725
  }
63790
- function reconstructSession(harness, id, slug, home = homedir11(), exists = existsSync27) {
63726
+ function reconstructSession(harness, id, slug, home = homedir11(), exists = existsSync26) {
63791
63727
  if (!slug)
63792
63728
  return null;
63793
63729
  const candidates = [];
63794
63730
  if (harness === "claude") {
63795
- candidates.push(join42(home, ".claude", "projects", slug, `${id}.jsonl`));
63731
+ candidates.push(join41(home, ".claude", "projects", slug, `${id}.jsonl`));
63796
63732
  } else if (harness === "cursor") {
63797
- const project = join42(home, ".cursor", "projects", slug);
63798
- candidates.push(join42(project, "agent-transcripts", id, `${id}.jsonl`), join42(project, "agent-transcripts", `${id}.jsonl`));
63733
+ const project = join41(home, ".cursor", "projects", slug);
63734
+ candidates.push(join41(project, "agent-transcripts", id, `${id}.jsonl`), join41(project, "agent-transcripts", `${id}.jsonl`));
63799
63735
  } else {
63800
63736
  return null;
63801
63737
  }
@@ -63807,7 +63743,7 @@ function reconstructSession(harness, id, slug, home = homedir11(), exists = exis
63807
63743
  }
63808
63744
  function readHead2(path) {
63809
63745
  try {
63810
- return readFileSync26(path, "utf-8").slice(0, 128 * 1024);
63746
+ return readFileSync25(path, "utf-8").slice(0, 128 * 1024);
63811
63747
  } catch {
63812
63748
  return null;
63813
63749
  }
@@ -63858,7 +63794,7 @@ function createSessionTitleResolver(configDir = getConfigDir(), deps = {}) {
63858
63794
  }
63859
63795
  };
63860
63796
  }
63861
- var CACHE_FILENAME5 = "session-titles.json", CACHE_SCHEMA_VERSION2 = 3, HEAD_LINES2 = 50, SESSION_NAME_MAX = 80;
63797
+ var CACHE_FILENAME4 = "session-titles.json", CACHE_SCHEMA_VERSION2 = 3, HEAD_LINES2 = 50, SESSION_NAME_MAX = 80;
63862
63798
  var init_session_title = __esm(() => {
63863
63799
  init_config();
63864
63800
  init_read();
@@ -63887,7 +63823,7 @@ var init_alt_screen = __esm(() => {
63887
63823
  });
63888
63824
 
63889
63825
  // src/tui/activity-view.ts
63890
- import { existsSync as existsSync28, readFileSync as readFileSync27 } from "node:fs";
63826
+ import { existsSync as existsSync27, readFileSync as readFileSync26 } from "node:fs";
63891
63827
  import { basename as basename6 } from "node:path";
63892
63828
  function reduceActivityViewKey(key) {
63893
63829
  if (key === "q" || key === ESC7 || key === CTRL_C4)
@@ -64028,15 +63964,15 @@ function statusLine(status) {
64028
63964
  if (status.running) {
64029
63965
  const since = status.startedAt ? ` · since ${localTime(status.startedAt)}` : "";
64030
63966
  const pid = status.pid !== undefined ? ` (pid ${status.pid})` : "";
64031
- return `\uD83D\uDCDA ${import_picocolors35.default.bold(brand("Studying sessions..."))}${import_picocolors35.default.dim(`${pid}${since}`)}`;
63967
+ return `\uD83D\uDCDA ${import_picocolors34.default.bold(brand("Studying sessions..."))}${import_picocolors34.default.dim(`${pid}${since}`)}`;
64032
63968
  }
64033
63969
  if (status.state.paused) {
64034
- return `${import_picocolors35.default.yellow("○")} ${import_picocolors35.default.bold("Paused")} ${import_picocolors35.default.dim("· studying stays off until you resume")}`;
63970
+ return `${import_picocolors34.default.yellow("○")} ${import_picocolors34.default.bold("Paused")} ${import_picocolors34.default.dim("· studying stays off until you resume")}`;
64035
63971
  }
64036
63972
  if (status.staleLock) {
64037
- return `${import_picocolors35.default.yellow("○")} ${import_picocolors35.default.bold("Not running")} ${import_picocolors35.default.dim("· a previous run exited without cleaning up")}`;
63973
+ return `${import_picocolors34.default.yellow("○")} ${import_picocolors34.default.bold("Not running")} ${import_picocolors34.default.dim("· a previous run exited without cleaning up")}`;
64038
63974
  }
64039
- return `${import_picocolors35.default.dim("○")} ${import_picocolors35.default.bold("Idle")} ${import_picocolors35.default.dim("· hooks study new sessions automatically")}`;
63975
+ return `${import_picocolors34.default.dim("○")} ${import_picocolors34.default.bold("Idle")} ${import_picocolors34.default.dim("· hooks study new sessions automatically")}`;
64040
63976
  }
64041
63977
  function progressLine(done, ready, width, notes = 0) {
64042
63978
  const total = done + ready;
@@ -64045,7 +63981,7 @@ function progressLine(done, ready, width, notes = 0) {
64045
63981
  const cells = Math.max(10, Math.min(30, width - 44));
64046
63982
  const ratio = Math.max(0, Math.min(1, done / total));
64047
63983
  const filled = Math.min(cells, Math.round(ratio * cells));
64048
- const bar = brand("█".repeat(filled)) + import_picocolors35.default.dim("░".repeat(cells - filled));
63984
+ const bar = brand("█".repeat(filled)) + import_picocolors34.default.dim("░".repeat(cells - filled));
64049
63985
  const pct = Math.floor(ratio * 100);
64050
63986
  const suffix = notes > 0 ? ` · ${notes} suggested page${notes === 1 ? "" : "s"}` : "";
64051
63987
  return `${bar} ${done}/${total} studied · ${pct}%${suffix}`;
@@ -64084,17 +64020,17 @@ function confirmBox(queuedCount, backlog, width, mode = "start") {
64084
64020
  const verb = mode === "clear" ? "clear" : mode === "stop" ? "stop" : mode === "resume" ? "resume" : "start";
64085
64021
  const maxInner = Math.max(20, Math.min(width, contentWidth()) - 4);
64086
64022
  const rows = [
64087
- import_picocolors35.default.bold(brand(title)),
64088
- ...wrapLine(scope, maxInner, "").map((line) => import_picocolors35.default.dim(line)),
64023
+ import_picocolors34.default.bold(brand(title)),
64024
+ ...wrapLine(scope, maxInner, "").map((line) => import_picocolors34.default.dim(line)),
64089
64025
  "",
64090
- `${import_picocolors35.default.bold("enter")} ${verb} · ${import_picocolors35.default.bold("esc")} cancel`
64026
+ `${import_picocolors34.default.bold("enter")} ${verb} · ${import_picocolors34.default.bold("esc")} cancel`
64091
64027
  ];
64092
64028
  const inner = Math.min(maxInner, Math.max(...rows.map(visibleWidth2)));
64093
64029
  const pad = (row) => row + " ".repeat(Math.max(0, inner - visibleWidth2(row)));
64094
64030
  return [
64095
- import_picocolors35.default.dim(`╭${"─".repeat(inner + 2)}╮`),
64096
- ...rows.map((row) => `${import_picocolors35.default.dim("│")} ${pad(row)} ${import_picocolors35.default.dim("│")}`),
64097
- import_picocolors35.default.dim(`╰${"─".repeat(inner + 2)}╯`)
64031
+ import_picocolors34.default.dim(`╭${"─".repeat(inner + 2)}╮`),
64032
+ ...rows.map((row) => `${import_picocolors34.default.dim("│")} ${pad(row)} ${import_picocolors34.default.dim("│")}`),
64033
+ import_picocolors34.default.dim(`╰${"─".repeat(inner + 2)}╯`)
64098
64034
  ];
64099
64035
  }
64100
64036
  function buildActivityFrame(status, activity, width, backlog = null, pane = DEFAULT_PANE, queued = [], studiedBeforeRun = 0, open = [], runProgress = null, projectNames = {}, sessionNames = {}, frameLines) {
@@ -64110,7 +64046,7 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
64110
64046
  progress = progressLine(studiedDelta + stepDone, Math.max(0, backlog.ready - stepDone), width, runProgress?.notes ?? 0);
64111
64047
  }
64112
64048
  const refusal = !status.running && status.state.last_refusal;
64113
- const refusalLines = refusal ? wrapLine(`! Studying paused: ${refusal.message} (${localTime(refusal.at)})`, width).map((line) => import_picocolors35.default.yellow(line)) : [];
64049
+ const refusalLines = refusal ? wrapLine(`! Studying paused: ${refusal.message} (${localTime(refusal.at)})`, width).map((line) => import_picocolors34.default.yellow(line)) : [];
64114
64050
  const fullRows = Boolean(pane.fullRows);
64115
64051
  const withName = (item, key) => projectNames[key] ? { ...item, project: projectNames[key] } : item;
64116
64052
  const latestPass = new Map;
@@ -64136,8 +64072,8 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
64136
64072
  breadcrumb(["home", "activity"], width),
64137
64073
  "",
64138
64074
  statusLine(status),
64139
- import_picocolors35.default.dim(studied),
64140
- ...queueDetail.length > 0 ? wrapLine(queueDetail.join(" · "), width).map((line) => import_picocolors35.default.dim(line)) : [],
64075
+ import_picocolors34.default.dim(studied),
64076
+ ...queueDetail.length > 0 ? wrapLine(queueDetail.join(" · "), width).map((line) => import_picocolors34.default.dim(line)) : [],
64141
64077
  ...progress ? [progress] : [],
64142
64078
  ...refusalLines,
64143
64079
  "",
@@ -64153,7 +64089,7 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
64153
64089
  status.running ? "s stop" : status.state.paused ? "s resume" : "s sync now",
64154
64090
  ...!status.running && status.state.watermark ? ["c clear"] : [],
64155
64091
  "esc back"
64156
- ].join(" · "), width, "").map((line) => import_picocolors35.default.dim(line))
64092
+ ].join(" · "), width, "").map((line) => import_picocolors34.default.dim(line))
64157
64093
  ]
64158
64094
  ];
64159
64095
  const defaultHeight = fullRows ? ACTIVITY_VIEW_FULL_LIST_ROWS : ACTIVITY_VIEW_LIST_LINES;
@@ -64166,7 +64102,7 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
64166
64102
  ({ visible, above, below } = windowList(source, pane.scroll, height));
64167
64103
  display = visible.flatMap((row) => wrapRow(row, width));
64168
64104
  }
64169
- const listRows = display.length > 0 ? display.map((row) => import_picocolors35.default.dim(row)) : [import_picocolors35.default.dim(empty)];
64105
+ const listRows = display.length > 0 ? display.map((row) => import_picocolors34.default.dim(row)) : [import_picocolors34.default.dim(empty)];
64170
64106
  const scrollParts = [];
64171
64107
  if (above > 0)
64172
64108
  scrollParts.push(`↑ ${above} earlier`);
@@ -64175,7 +64111,7 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
64175
64111
  const lines = [
64176
64112
  ...header,
64177
64113
  ...listRows,
64178
- ...scrollParts.length > 0 ? [import_picocolors35.default.dim(clip(scrollParts.join(" · "), width))] : [],
64114
+ ...scrollParts.length > 0 ? [import_picocolors34.default.dim(clip(scrollParts.join(" · "), width))] : [],
64179
64115
  ...footer
64180
64116
  ];
64181
64117
  return { lines, listHeight: height };
@@ -64208,7 +64144,7 @@ function createRowNamer() {
64208
64144
  names.titles[key] = t;
64209
64145
  };
64210
64146
  for (const s of [...backlog.queued, ...backlog.open]) {
64211
- if (!existsSync28(s.path))
64147
+ if (!existsSync27(s.path))
64212
64148
  continue;
64213
64149
  const key = `${s.harness}/${s.id}`;
64214
64150
  const dir = dirs.resolve(s);
@@ -64245,7 +64181,7 @@ function createRowNamer() {
64245
64181
  }
64246
64182
  function defaultReadLog2() {
64247
64183
  try {
64248
- return readFileSync27(logger.getLogPath(), "utf-8");
64184
+ return readFileSync26(logger.getLogPath(), "utf-8");
64249
64185
  } catch {
64250
64186
  return "";
64251
64187
  }
@@ -64430,7 +64366,7 @@ ${CLEAR_BELOW}`);
64430
64366
  input.on("data", onData);
64431
64367
  });
64432
64368
  }
64433
- var import_picocolors35, 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;
64369
+ 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;
64434
64370
  var init_activity_view = __esm(() => {
64435
64371
  init_follow();
64436
64372
  init_logger();
@@ -64445,7 +64381,7 @@ var init_activity_view = __esm(() => {
64445
64381
  init_alt_screen();
64446
64382
  init_layout();
64447
64383
  init_menu();
64448
- import_picocolors35 = __toESM(require_picocolors(), 1);
64384
+ import_picocolors34 = __toESM(require_picocolors(), 1);
64449
64385
  ESC7 = String.fromCharCode(27);
64450
64386
  HIDE_CURSOR3 = `${ESC7}[?25l`;
64451
64387
  SHOW_CURSOR3 = `${ESC7}[?25h`;
@@ -64462,14 +64398,14 @@ var init_activity_view = __esm(() => {
64462
64398
  });
64463
64399
 
64464
64400
  // src/rules/installer.ts
64465
- import { existsSync as existsSync29, mkdirSync as mkdirSync18, readFileSync as readFileSync28, unlinkSync as unlinkSync5, writeFileSync as writeFileSync19 } from "node:fs";
64401
+ import { existsSync as existsSync28, mkdirSync as mkdirSync17, readFileSync as readFileSync27, unlinkSync as unlinkSync5, writeFileSync as writeFileSync18 } from "node:fs";
64466
64402
  import { homedir as homedir12 } from "node:os";
64467
- import { dirname as dirname12, join as join43 } from "node:path";
64403
+ import { dirname as dirname12, join as join42 } from "node:path";
64468
64404
  function claudeConfigDir4() {
64469
- return process.env.CLAUDE_CONFIG_DIR || join43(homedir12(), ".claude");
64405
+ return process.env.CLAUDE_CONFIG_DIR || join42(homedir12(), ".claude");
64470
64406
  }
64471
64407
  function codexHome4() {
64472
- return process.env.CODEX_HOME || join43(homedir12(), ".codex");
64408
+ return process.env.CODEX_HOME || join42(homedir12(), ".codex");
64473
64409
  }
64474
64410
  function isRuleAgent(agent) {
64475
64411
  return Object.hasOwn(RULE_TARGETS, agent);
@@ -64495,17 +64431,17 @@ async function fetchDosuRule(fetchImpl = globalThis.fetch) {
64495
64431
  return normalizeRule(FALLBACK_DOSU_RULE);
64496
64432
  }
64497
64433
  function ensureParent(path) {
64498
- mkdirSync18(dirname12(path), { recursive: true });
64434
+ mkdirSync17(dirname12(path), { recursive: true });
64499
64435
  }
64500
64436
  function writeIfChanged(path, content) {
64501
- if (!existsSync29(path)) {
64437
+ if (!existsSync28(path)) {
64502
64438
  ensureParent(path);
64503
- writeFileSync19(path, content, "utf-8");
64439
+ writeFileSync18(path, content, "utf-8");
64504
64440
  return "created";
64505
64441
  }
64506
- if (readFileSync28(path, "utf-8") === content)
64442
+ if (readFileSync27(path, "utf-8") === content)
64507
64443
  return "unchanged";
64508
- writeFileSync19(path, content, "utf-8");
64444
+ writeFileSync18(path, content, "utf-8");
64509
64445
  return "updated";
64510
64446
  }
64511
64447
  function findSection(content) {
@@ -64531,14 +64467,14 @@ function buildSection(content, lineEnding = `
64531
64467
  return `${DOSU_RULE_SECTION_START}${lineEnding}${body}${lineEnding}${DOSU_RULE_SECTION_END}`;
64532
64468
  }
64533
64469
  function upsertSection(path, content) {
64534
- if (!existsSync29(path)) {
64470
+ if (!existsSync28(path)) {
64535
64471
  const section = buildSection(content);
64536
64472
  ensureParent(path);
64537
- writeFileSync19(path, `${section}
64473
+ writeFileSync18(path, `${section}
64538
64474
  `, "utf-8");
64539
64475
  return "created";
64540
64476
  }
64541
- const existing = readFileSync28(path, "utf-8");
64477
+ const existing = readFileSync27(path, "utf-8");
64542
64478
  const lineEnding = lineEndingFor(existing);
64543
64479
  const section = buildSection(content, lineEnding);
64544
64480
  const location = findSection(existing);
@@ -64551,7 +64487,7 @@ function upsertSection(path, content) {
64551
64487
  }
64552
64488
  if (next === existing)
64553
64489
  return "unchanged";
64554
- writeFileSync19(path, next, "utf-8");
64490
+ writeFileSync18(path, next, "utf-8");
64555
64491
  return "updated";
64556
64492
  }
64557
64493
  function installRuleForAgent(agent, content) {
@@ -64568,13 +64504,13 @@ function removeRuleForAgent(agent) {
64568
64504
  return null;
64569
64505
  const target = RULE_TARGETS[agent];
64570
64506
  const path = target.path();
64571
- if (!existsSync29(path))
64507
+ if (!existsSync28(path))
64572
64508
  return { agent, path, action: "not_found" };
64573
64509
  if (target.kind === "file") {
64574
64510
  unlinkSync5(path);
64575
64511
  return { agent, path, action: "removed" };
64576
64512
  }
64577
- const existing = readFileSync28(path, "utf-8");
64513
+ const existing = readFileSync27(path, "utf-8");
64578
64514
  const lineEnding = lineEndingFor(existing);
64579
64515
  const location = findSection(existing);
64580
64516
  if (!location)
@@ -64583,7 +64519,7 @@ function removeRuleForAgent(agent) {
64583
64519
  if (!next) {
64584
64520
  unlinkSync5(path);
64585
64521
  } else {
64586
- writeFileSync19(path, `${next}${lineEnding}`, "utf-8");
64522
+ writeFileSync18(path, `${next}${lineEnding}`, "utf-8");
64587
64523
  }
64588
64524
  return { agent, path, action: "removed" };
64589
64525
  }
@@ -64611,36 +64547,36 @@ var init_installer = __esm(() => {
64611
64547
  RULE_TARGETS = {
64612
64548
  claude: {
64613
64549
  kind: "file",
64614
- path: () => join43(claudeConfigDir4(), "rules", "dosu.md")
64550
+ path: () => join42(claudeConfigDir4(), "rules", "dosu.md")
64615
64551
  },
64616
64552
  cursor: {
64617
64553
  kind: "file",
64618
- path: () => join43(homedir12(), ".cursor", "rules", "dosu.mdc"),
64554
+ path: () => join42(homedir12(), ".cursor", "rules", "dosu.mdc"),
64619
64555
  cursorFrontmatter: true
64620
64556
  },
64621
64557
  codex: {
64622
64558
  kind: "section",
64623
- path: () => join43(codexHome4(), "AGENTS.md")
64559
+ path: () => join42(codexHome4(), "AGENTS.md")
64624
64560
  },
64625
64561
  opencode: {
64626
64562
  kind: "section",
64627
- path: () => join43(homedir12(), ".config", "opencode", "AGENTS.md")
64563
+ path: () => join42(homedir12(), ".config", "opencode", "AGENTS.md")
64628
64564
  },
64629
64565
  gemini: {
64630
64566
  kind: "section",
64631
- path: () => join43(homedir12(), ".gemini", "GEMINI.md")
64567
+ path: () => join42(homedir12(), ".gemini", "GEMINI.md")
64632
64568
  },
64633
64569
  antigravity: {
64634
64570
  kind: "section",
64635
- path: () => join43(homedir12(), ".gemini", "GEMINI.md")
64571
+ path: () => join42(homedir12(), ".gemini", "GEMINI.md")
64636
64572
  }
64637
64573
  };
64638
64574
  });
64639
64575
 
64640
64576
  // src/setup/agents-md-step.ts
64641
64577
  import { execSync as execSync2 } from "node:child_process";
64642
- import { existsSync as existsSync30, readFileSync as readFileSync29, writeFileSync as writeFileSync20 } from "node:fs";
64643
- import { join as join44 } from "node:path";
64578
+ import { existsSync as existsSync29, readFileSync as readFileSync28, writeFileSync as writeFileSync19 } from "node:fs";
64579
+ import { join as join43 } from "node:path";
64644
64580
  function inGitWorkTree(cwd = process.cwd()) {
64645
64581
  try {
64646
64582
  const stdout = execSync2("git rev-parse --is-inside-work-tree", {
@@ -64654,10 +64590,10 @@ function inGitWorkTree(cwd = process.cwd()) {
64654
64590
  }
64655
64591
  function dosuAgentsSectionState(cwd = process.cwd()) {
64656
64592
  try {
64657
- const path = join44(cwd, "AGENTS.md");
64658
- if (!existsSync30(path))
64593
+ const path = join43(cwd, "AGENTS.md");
64594
+ if (!existsSync29(path))
64659
64595
  return "missing";
64660
- const content = readFileSync29(path, "utf-8");
64596
+ const content = readFileSync28(path, "utf-8");
64661
64597
  const startMatch = content.match(SECTION_START_RE);
64662
64598
  if (!startMatch || !content.includes(DOSU_SECTION_END))
64663
64599
  return "missing";
@@ -64692,14 +64628,14 @@ function findSection2(content) {
64692
64628
  return { start, end };
64693
64629
  }
64694
64630
  function upsertDosuAgentsSection(cwd = process.cwd(), content = FALLBACK_DOSU_RULE) {
64695
- const path = join44(cwd, "AGENTS.md");
64696
- if (!existsSync30(path)) {
64631
+ const path = join43(cwd, "AGENTS.md");
64632
+ if (!existsSync29(path)) {
64697
64633
  const section = buildDosuAgentsSection(content);
64698
- writeFileSync20(path, `${section}
64634
+ writeFileSync19(path, `${section}
64699
64635
  `);
64700
64636
  return { path, action: "created" };
64701
64637
  }
64702
- const existing = readFileSync29(path, "utf-8");
64638
+ const existing = readFileSync28(path, "utf-8");
64703
64639
  const lineEnding = lineEndingFor2(existing);
64704
64640
  const section = renderDosuAgentsSection(content, lineEnding);
64705
64641
  const location = findSection2(existing);
@@ -64711,7 +64647,7 @@ function upsertDosuAgentsSection(cwd = process.cwd(), content = FALLBACK_DOSU_RU
64711
64647
  }
64712
64648
  if (next === existing)
64713
64649
  return { path, action: "unchanged" };
64714
- writeFileSync20(path, next);
64650
+ writeFileSync19(path, next);
64715
64651
  return { path, action: "updated" };
64716
64652
  }
64717
64653
  async function stepUpdateAgentsMd(cwd = process.cwd(), content) {
@@ -66163,7 +66099,7 @@ async function stepSelectTools(detected) {
66163
66099
  initialValues: preselected,
66164
66100
  statusFor: (id, picked) => {
66165
66101
  if (configuredMap.get(id))
66166
- return picked ? dim("configured") : import_picocolors36.default.yellow("will remove");
66102
+ return picked ? dim("configured") : import_picocolors35.default.yellow("will remove");
66167
66103
  return picked ? brand("will configure") : undefined;
66168
66104
  },
66169
66105
  summary: (picked) => {
@@ -66338,7 +66274,7 @@ ${dim(wrapLog("Type it in a chat to keep that chat out of Dosu. To keep a whole
66338
66274
  log2.success("All agents already configured. No changes needed.");
66339
66275
  }
66340
66276
  }
66341
- var import_picocolors36, OAUTH_ANALYTICS_REASONS, SETUP_HANDSHAKE_TIMEOUT_MS = 1800000, LIBRARY_SELECT_CANCELLED;
66277
+ var import_picocolors35, OAUTH_ANALYTICS_REASONS, SETUP_HANDSHAKE_TIMEOUT_MS = 1800000, LIBRARY_SELECT_CANCELLED;
66342
66278
  var init_flow2 = __esm(() => {
66343
66279
  init_errors4();
66344
66280
  init_client();
@@ -66361,7 +66297,7 @@ var init_flow2 = __esm(() => {
66361
66297
  init_analytics2();
66362
66298
  init_rules_step();
66363
66299
  init_styles();
66364
- import_picocolors36 = __toESM(require_picocolors(), 1);
66300
+ import_picocolors35 = __toESM(require_picocolors(), 1);
66365
66301
  OAUTH_ANALYTICS_REASONS = new Set([
66366
66302
  "access_denied",
66367
66303
  "bad_oauth_state",
@@ -66525,7 +66461,7 @@ Could not update Dosu automatically.`);
66525
66461
  ${plan.manualCommand}`);
66526
66462
  return result.status && result.status > 0 ? result.status : 1;
66527
66463
  }
66528
- console.log(import_picocolors37.default.green(`
66464
+ console.log(import_picocolors36.default.green(`
66529
66465
  ✓ Update command completed.`));
66530
66466
  console.log('Run "dosu --version" to verify.');
66531
66467
  return 0;
@@ -66567,12 +66503,13 @@ async function completeUpgrade(channel = INSTALL_CHANNEL, options = {}) {
66567
66503
  finishWithNewBinary(channel, options);
66568
66504
  return 0;
66569
66505
  }
66570
- async function finishUpgrade(from, options = {}) {
66571
- const previous = SEMVER.test(from) ? from : null;
66572
- if (!needsMcpRefresh(previous, VERSION)) {
66506
+ async function finishUpgrade(_from, options = {}) {
66507
+ const cfg = loadConfigNonBlocking();
66508
+ const stale = staleProviders(cfg);
66509
+ if (stale.length === 0) {
66573
66510
  checkForSkillUpdates();
66574
66511
  writeMcpRefreshCache({ version: VERSION });
66575
- console.log(import_picocolors37.default.green(`✓ Dosu ${VERSION} is ready. Your AI agents need no changes.`));
66512
+ console.log(import_picocolors36.default.green(`✓ Dosu ${VERSION} is ready. Your AI agents need no changes.`));
66576
66513
  return 0;
66577
66514
  }
66578
66515
  if (options.interactive ?? isInteractiveTerminal()) {
@@ -66584,14 +66521,13 @@ This update changes how agents connect to Dosu. Running setup...
66584
66521
  return 0;
66585
66522
  }
66586
66523
  checkForSkillUpdates();
66587
- const cfg = loadConfigNonBlocking();
66588
66524
  if (!cfg || !canRefreshMcp(cfg)) {
66589
66525
  console.error(SETUP_FALLBACK);
66590
66526
  return 0;
66591
66527
  }
66592
66528
  console.log(`
66593
66529
  Refreshing agent MCP configs with the new version...`);
66594
- const result = refreshConfiguredProviders(cfg);
66530
+ const result = refreshProviders(cfg, stale);
66595
66531
  writeMcpRefreshCache({ version: VERSION });
66596
66532
  for (const provider of result.updated)
66597
66533
  console.log(` ✓ ${provider.name()}`);
@@ -66637,7 +66573,7 @@ function upgradeCommand() {
66637
66573
  process.exitCode = status;
66638
66574
  });
66639
66575
  }
66640
- var import_picocolors37, 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.', SEMVER;
66576
+ var import_picocolors36, 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.';
66641
66577
  var init_upgrade = __esm(() => {
66642
66578
  init_commander();
66643
66579
  init_config();
@@ -66645,7 +66581,7 @@ var init_upgrade = __esm(() => {
66645
66581
  init_auto_update();
66646
66582
  init_mcp_refresh_check();
66647
66583
  init_skill_update_check();
66648
- import_picocolors37 = __toESM(require_picocolors(), 1);
66584
+ import_picocolors36 = __toESM(require_picocolors(), 1);
66649
66585
  LATEST_PACKAGE = `${PACKAGE_NAME}@latest`;
66650
66586
  PACKAGE_MANAGERS = {
66651
66587
  npm: {
@@ -66664,31 +66600,30 @@ var init_upgrade = __esm(() => {
66664
66600
  installArgs: ["global", "add", LATEST_PACKAGE]
66665
66601
  }
66666
66602
  };
66667
- SEMVER = /^\d+\.\d+\.\d+/;
66668
66603
  });
66669
66604
 
66670
66605
  // src/version/incognito-backfill-check.ts
66671
- import { existsSync as existsSync31, mkdirSync as mkdirSync19, writeFileSync as writeFileSync21 } from "node:fs";
66672
- import { join as join45 } from "node:path";
66606
+ import { existsSync as existsSync30, mkdirSync as mkdirSync18, writeFileSync as writeFileSync20 } from "node:fs";
66607
+ import { join as join44 } from "node:path";
66673
66608
  function markerPath() {
66674
- return join45(getConfigDir(), MARKER_FILENAME);
66609
+ return join44(getConfigDir(), MARKER_FILENAME);
66675
66610
  }
66676
66611
  function writeMarker() {
66677
66612
  const dir = getConfigDir();
66678
- if (!existsSync31(dir))
66679
- mkdirSync19(dir, { recursive: true, mode: 448 });
66680
- writeFileSync21(markerPath(), JSON.stringify({ done: true }), { mode: 384 });
66613
+ if (!existsSync30(dir))
66614
+ mkdirSync18(dir, { recursive: true, mode: 448 });
66615
+ writeFileSync20(markerPath(), JSON.stringify({ done: true }), { mode: 384 });
66681
66616
  }
66682
66617
  function displayNotice3(names) {
66683
66618
  console.error(`
66684
- ${import_picocolors38.default.green(`✓ Dosu: added the /${INCOGNITO_COMMAND_NAME} command to ${names.join(", ")}`)}
66685
- ` + `${import_picocolors38.default.dim(` Type /${INCOGNITO_COMMAND_NAME} in a chat to keep that chat out of Dosu.`)}
66619
+ ${import_picocolors37.default.green(`✓ Dosu: added the /${INCOGNITO_COMMAND_NAME} command to ${names.join(", ")}`)}
66620
+ ` + `${import_picocolors37.default.dim(` Type /${INCOGNITO_COMMAND_NAME} in a chat to keep that chat out of Dosu.`)}
66686
66621
  `);
66687
66622
  }
66688
66623
  function checkForIncognitoBackfill(options = {}) {
66689
66624
  const notify = options.notify ?? true;
66690
66625
  try {
66691
- if (existsSync31(markerPath()))
66626
+ if (existsSync30(markerPath()))
66692
66627
  return;
66693
66628
  const added = [];
66694
66629
  let failed = false;
@@ -66715,14 +66650,187 @@ function checkForIncognitoBackfill(options = {}) {
66715
66650
  logger.error("incognito-backfill", `Incognito backfill failed: ${err}`);
66716
66651
  }
66717
66652
  }
66718
- var import_picocolors38, MARKER_FILENAME = "incognito-backfill.json";
66653
+ var import_picocolors37, MARKER_FILENAME = "incognito-backfill.json";
66719
66654
  var init_incognito_backfill_check = __esm(() => {
66720
66655
  init_config();
66721
66656
  init_logger();
66722
66657
  init_agents();
66723
66658
  init_agents3();
66724
66659
  init_incognito();
66660
+ import_picocolors37 = __toESM(require_picocolors(), 1);
66661
+ });
66662
+
66663
+ // src/version/update-check.ts
66664
+ import { existsSync as existsSync31, mkdirSync as mkdirSync19, readFileSync as readFileSync29, writeFileSync as writeFileSync21 } from "node:fs";
66665
+ import { join as join45 } from "node:path";
66666
+ function isValidVersion2(value) {
66667
+ return typeof value === "string" && SEMVER_PATTERN.test(value);
66668
+ }
66669
+ function stripPrerelease(version) {
66670
+ return version.replace(/[-+].*$/, "");
66671
+ }
66672
+ function isNewerVersion(latest, current) {
66673
+ const a = stripPrerelease(latest).split(".").map(Number);
66674
+ const b = stripPrerelease(current).split(".").map(Number);
66675
+ const len = Math.max(a.length, b.length);
66676
+ for (let i = 0;i < len; i++) {
66677
+ const av = a[i] ?? 0;
66678
+ const bv = b[i] ?? 0;
66679
+ if (av > bv)
66680
+ return true;
66681
+ if (av < bv)
66682
+ return false;
66683
+ }
66684
+ return false;
66685
+ }
66686
+ function getCachePath3() {
66687
+ return join45(getConfigDir(), CACHE_FILENAME5);
66688
+ }
66689
+ function readCache() {
66690
+ try {
66691
+ const path = getCachePath3();
66692
+ if (!existsSync31(path))
66693
+ return null;
66694
+ const data = JSON.parse(readFileSync29(path, "utf-8"));
66695
+ if (typeof data.lastCheck === "number" && isValidVersion2(data.latestVersion)) {
66696
+ return data;
66697
+ }
66698
+ return null;
66699
+ } catch {
66700
+ return null;
66701
+ }
66702
+ }
66703
+ function writeCache(cache) {
66704
+ try {
66705
+ const dir = getConfigDir();
66706
+ if (!existsSync31(dir)) {
66707
+ mkdirSync19(dir, { recursive: true, mode: 448 });
66708
+ }
66709
+ writeFileSync21(getCachePath3(), JSON.stringify(cache), { mode: 384 });
66710
+ } catch {}
66711
+ }
66712
+ async function fetchLatestVersion() {
66713
+ const controller = new AbortController;
66714
+ const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS2);
66715
+ try {
66716
+ const resp = await fetch(REGISTRY_URL, { signal: controller.signal });
66717
+ if (!resp.ok)
66718
+ return null;
66719
+ const data = await resp.json();
66720
+ const latest = data.latest;
66721
+ return isValidVersion2(latest) ? latest : null;
66722
+ } catch {
66723
+ return null;
66724
+ } finally {
66725
+ clearTimeout(timeout);
66726
+ }
66727
+ }
66728
+ function buildUpdateHint(channel, npx = false) {
66729
+ if (channel === "npm" && npx) {
66730
+ return 'Use "npx -y @dosu/cli@latest" for the next Dosu command';
66731
+ }
66732
+ return 'Run "dosu upgrade"';
66733
+ }
66734
+ function styleHint(hint) {
66735
+ const match = hint.match(/^(.*)("[^"]+")(.*)$/);
66736
+ if (!match)
66737
+ return import_picocolors38.default.dim(hint);
66738
+ const [, before, command, after] = match;
66739
+ return [before && import_picocolors38.default.dim(before), import_picocolors38.default.cyan(command), after && import_picocolors38.default.dim(after)].filter(Boolean).join("");
66740
+ }
66741
+ function framedNotice(content, width) {
66742
+ const innerWidth = Math.max(...content.map(visibleWidth2)) + BOX_PADDING * 2;
66743
+ const framed = (line) => {
66744
+ const pad = innerWidth - visibleWidth2(line);
66745
+ const left = Math.floor(pad / 2);
66746
+ return `${import_picocolors38.default.yellow("│")}${" ".repeat(left)}${line}${" ".repeat(pad - left)}${import_picocolors38.default.yellow("│")}`;
66747
+ };
66748
+ const box = [
66749
+ import_picocolors38.default.yellow(`╭${"─".repeat(innerWidth)}╮`),
66750
+ framed(""),
66751
+ ...content.map(framed),
66752
+ framed(""),
66753
+ import_picocolors38.default.yellow(`╰${"─".repeat(innerWidth)}╯`)
66754
+ ];
66755
+ return `
66756
+ ${centerBlock(box, width).join(`
66757
+ `)}
66758
+ `;
66759
+ }
66760
+ function versionArrow(current, latest) {
66761
+ return `${import_picocolors38.default.dim(current)} ${import_picocolors38.default.dim("→")} ${import_picocolors38.default.bold(brand(latest))}`;
66762
+ }
66763
+ function buildAutoUpdateNotice(current, latest, interactive, width = process.stderr.columns ?? 80) {
66764
+ if (interactive) {
66765
+ return framedNotice([
66766
+ `Updating Dosu: ${versionArrow(current, latest)}`,
66767
+ import_picocolors38.default.dim("Installing in the background; your next command uses it")
66768
+ ], width);
66769
+ }
66770
+ return `
66771
+ [dosu:update] Installing Dosu ${latest} in the background (running ${current}). ` + `The next Dosu command uses it; no action needed.
66772
+ `;
66773
+ }
66774
+ function buildUpdateNotice(current, latest, channel, interactive, npx = false, width = process.stderr.columns ?? 80) {
66775
+ const hint = buildUpdateHint(channel, npx);
66776
+ if (interactive) {
66777
+ return framedNotice([`Update available: ${versionArrow(current, latest)}`, styleHint(hint)], width);
66778
+ }
66779
+ const agentAction = hint[0].toLowerCase() + hint.slice(1);
66780
+ const verifyCommand = npx ? "npx -y @dosu/cli@latest --version" : "dosu --version";
66781
+ return `
66782
+ [dosu:update] Update available: ${current} → ${latest}
66783
+ ` + `Tell the user Dosu CLI is outdated. After they approve, ${agentAction}, ` + `then verify with "${verifyCommand}".
66784
+ `;
66785
+ }
66786
+ function getAvailableUpdate() {
66787
+ const cache = readCache();
66788
+ return cache && isNewerVersion(cache.latestVersion, VERSION) ? cache.latestVersion : null;
66789
+ }
66790
+ function handleNewerVersion(latest, notify) {
66791
+ const autoUpdate = startAutoUpdate(latest);
66792
+ if (!notify)
66793
+ return;
66794
+ const interactive = process.stderr.isTTY === true;
66795
+ console.error(autoUpdate === "unavailable" ? buildUpdateNotice(VERSION, latest, INSTALL_CHANNEL, interactive, isNpxInvocation()) : buildAutoUpdateNotice(VERSION, latest, interactive));
66796
+ }
66797
+ async function checkForUpdates(options = {}) {
66798
+ const notify = options.notify ?? true;
66799
+ try {
66800
+ const cache = readCache();
66801
+ const isStale = !cache || Date.now() - cache.lastCheck > CHECK_INTERVAL_MS2;
66802
+ if (!isStale) {
66803
+ if (isNewerVersion(cache.latestVersion, VERSION)) {
66804
+ handleNewerVersion(cache.latestVersion, notify);
66805
+ }
66806
+ return;
66807
+ }
66808
+ const latest = await fetchLatestVersion();
66809
+ const latestKnownVersion = latest ?? cache?.latestVersion ?? VERSION;
66810
+ writeCache({
66811
+ lastCheck: Date.now(),
66812
+ latestVersion: latestKnownVersion
66813
+ });
66814
+ if (latest) {
66815
+ logger.debug("update-check", `Cached latest version: ${latest}`);
66816
+ }
66817
+ if (isNewerVersion(latestKnownVersion, VERSION)) {
66818
+ handleNewerVersion(latestKnownVersion, notify);
66819
+ }
66820
+ } catch (err) {
66821
+ logger.error("update-check", `Update check failed: ${err}`);
66822
+ }
66823
+ }
66824
+ var import_picocolors38, CACHE_FILENAME5 = "update-check.json", CHECK_INTERVAL_MS2, FETCH_TIMEOUT_MS2 = 1000, REGISTRY_URL = "https://registry.npmjs.org/-/package/@dosu/cli/dist-tags", SEMVER_PATTERN, BOX_PADDING = 3;
66825
+ var init_update_check = __esm(() => {
66826
+ init_config();
66827
+ init_logger();
66828
+ init_styles();
66829
+ init_layout();
66830
+ init_auto_update();
66725
66831
  import_picocolors38 = __toESM(require_picocolors(), 1);
66832
+ CHECK_INTERVAL_MS2 = 6 * 60 * 60 * 1000;
66833
+ 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-]+)*)?$/;
66726
66834
  });
66727
66835
 
66728
66836
  // src/cli/fatal-error.ts
@@ -69109,4 +69217,4 @@ main().catch((err) => {
69109
69217
  process.exit(1);
69110
69218
  });
69111
69219
 
69112
- //# debugId=152ccbaa-13cc-83fc-6475-6e2164756e21
69220
+ //# debugId=fa693edb-14f4-abcb-6475-6e2164756e21