@dosu/cli 0.67.0 → 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 +776 -526
  3. package/package.json +1 -1
package/bin/dosu.js CHANGED
@@ -47,6 +47,111 @@ var __esm = (fn, res, err) => () => {
47
47
  };
48
48
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
49
49
 
50
+ // node_modules/picocolors/picocolors.js
51
+ var require_picocolors = __commonJS(function(exports, module) {
52
+ var p = process || {};
53
+ var argv = p.argv || [];
54
+ var env = p.env || {};
55
+ var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
56
+ var formatter = (open, close, replace = open) => (input) => {
57
+ let string = "" + input, index = string.indexOf(close, open.length);
58
+ return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
59
+ };
60
+ var replaceClose = (string, close, replace, index) => {
61
+ let result = "", cursor = 0;
62
+ do {
63
+ result += string.substring(cursor, index) + replace;
64
+ cursor = index + close.length;
65
+ index = string.indexOf(close, cursor);
66
+ } while (~index);
67
+ return result + string.substring(cursor);
68
+ };
69
+ var createColors = (enabled = isColorSupported) => {
70
+ let f = enabled ? formatter : () => String;
71
+ return {
72
+ isColorSupported: enabled,
73
+ reset: f("\x1B[0m", "\x1B[0m"),
74
+ bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
75
+ dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
76
+ italic: f("\x1B[3m", "\x1B[23m"),
77
+ underline: f("\x1B[4m", "\x1B[24m"),
78
+ inverse: f("\x1B[7m", "\x1B[27m"),
79
+ hidden: f("\x1B[8m", "\x1B[28m"),
80
+ strikethrough: f("\x1B[9m", "\x1B[29m"),
81
+ black: f("\x1B[30m", "\x1B[39m"),
82
+ red: f("\x1B[31m", "\x1B[39m"),
83
+ green: f("\x1B[32m", "\x1B[39m"),
84
+ yellow: f("\x1B[33m", "\x1B[39m"),
85
+ blue: f("\x1B[34m", "\x1B[39m"),
86
+ magenta: f("\x1B[35m", "\x1B[39m"),
87
+ cyan: f("\x1B[36m", "\x1B[39m"),
88
+ white: f("\x1B[37m", "\x1B[39m"),
89
+ gray: f("\x1B[90m", "\x1B[39m"),
90
+ bgBlack: f("\x1B[40m", "\x1B[49m"),
91
+ bgRed: f("\x1B[41m", "\x1B[49m"),
92
+ bgGreen: f("\x1B[42m", "\x1B[49m"),
93
+ bgYellow: f("\x1B[43m", "\x1B[49m"),
94
+ bgBlue: f("\x1B[44m", "\x1B[49m"),
95
+ bgMagenta: f("\x1B[45m", "\x1B[49m"),
96
+ bgCyan: f("\x1B[46m", "\x1B[49m"),
97
+ bgWhite: f("\x1B[47m", "\x1B[49m"),
98
+ blackBright: f("\x1B[90m", "\x1B[39m"),
99
+ redBright: f("\x1B[91m", "\x1B[39m"),
100
+ greenBright: f("\x1B[92m", "\x1B[39m"),
101
+ yellowBright: f("\x1B[93m", "\x1B[39m"),
102
+ blueBright: f("\x1B[94m", "\x1B[39m"),
103
+ magentaBright: f("\x1B[95m", "\x1B[39m"),
104
+ cyanBright: f("\x1B[96m", "\x1B[39m"),
105
+ whiteBright: f("\x1B[97m", "\x1B[39m"),
106
+ bgBlackBright: f("\x1B[100m", "\x1B[49m"),
107
+ bgRedBright: f("\x1B[101m", "\x1B[49m"),
108
+ bgGreenBright: f("\x1B[102m", "\x1B[49m"),
109
+ bgYellowBright: f("\x1B[103m", "\x1B[49m"),
110
+ bgBlueBright: f("\x1B[104m", "\x1B[49m"),
111
+ bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
112
+ bgCyanBright: f("\x1B[106m", "\x1B[49m"),
113
+ bgWhiteBright: f("\x1B[107m", "\x1B[49m")
114
+ };
115
+ };
116
+ module.exports = createColors();
117
+ module.exports.createColors = createColors;
118
+ });
119
+
120
+ // src/cli/command-error.ts
121
+ function isCommandError(error) {
122
+ return error instanceof CommandError;
123
+ }
124
+ var COMMAND_ERROR_CODES, CommandError;
125
+ var init_command_error = __esm(() => {
126
+ COMMAND_ERROR_CODES = [
127
+ "NOT_LOGGED_IN",
128
+ "NO_ORG_SELECTED",
129
+ "NO_API_KEY",
130
+ "NO_LIBRARY_SELECTED",
131
+ "NO_DEPLOYMENT_SELECTED",
132
+ "ORG_UNAVAILABLE",
133
+ "LIBRARY_UNAVAILABLE",
134
+ "DEPLOYMENT_UNAVAILABLE",
135
+ "SCOPE_MISMATCH",
136
+ "DEPLOYMENT_NOT_FOUND",
137
+ "DEPLOYMENT_AMBIGUOUS",
138
+ "NOT_MCP_DEPLOYMENT",
139
+ "REVIEW_ITEM_NOT_FOUND",
140
+ "INVALID_ARGUMENT"
141
+ ];
142
+ CommandError = class CommandError extends Error {
143
+ code;
144
+ details;
145
+ exitCode = 1;
146
+ constructor(code, message, details = []) {
147
+ super(message);
148
+ this.code = code;
149
+ this.details = details;
150
+ this.name = "CommandError";
151
+ }
152
+ };
153
+ });
154
+
50
155
  // src/statusline/argv.ts
51
156
  function agentFromArgv(argv) {
52
157
  for (let i = 0;i < argv.length; i++) {
@@ -856,6 +961,10 @@ function mcpHeaders(apiKey) {
856
961
  }
857
962
  return { "X-Dosu-API-Key": apiKey };
858
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
+ }
859
968
  function mcpRemoteServer(url, apiKey) {
860
969
  const env = {};
861
970
  const headerArgs = Object.entries(mcpHeaders(apiKey)).flatMap(([key, value]) => {
@@ -976,6 +1085,12 @@ function isJSONKeyConfigured(configPath, topLevelKey) {
976
1085
  return false;
977
1086
  return "dosu" in section;
978
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
+ }
979
1094
  function installJSONServer(configPath, topKey, server) {
980
1095
  const jsonCfg = readJSONConfig(configPath);
981
1096
  let section = jsonCfg[topKey];
@@ -1006,76 +1121,6 @@ var init_config_helpers = __esm(() => {
1006
1121
  writeFileAtomic = import_write_file_atomic.default;
1007
1122
  });
1008
1123
 
1009
- // node_modules/picocolors/picocolors.js
1010
- var require_picocolors = __commonJS(function(exports, module) {
1011
- var p = process || {};
1012
- var argv = p.argv || [];
1013
- var env = p.env || {};
1014
- var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
1015
- var formatter = (open, close, replace = open) => (input) => {
1016
- let string = "" + input, index = string.indexOf(close, open.length);
1017
- return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
1018
- };
1019
- var replaceClose = (string, close, replace, index) => {
1020
- let result = "", cursor = 0;
1021
- do {
1022
- result += string.substring(cursor, index) + replace;
1023
- cursor = index + close.length;
1024
- index = string.indexOf(close, cursor);
1025
- } while (~index);
1026
- return result + string.substring(cursor);
1027
- };
1028
- var createColors = (enabled = isColorSupported) => {
1029
- let f = enabled ? formatter : () => String;
1030
- return {
1031
- isColorSupported: enabled,
1032
- reset: f("\x1B[0m", "\x1B[0m"),
1033
- bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
1034
- dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
1035
- italic: f("\x1B[3m", "\x1B[23m"),
1036
- underline: f("\x1B[4m", "\x1B[24m"),
1037
- inverse: f("\x1B[7m", "\x1B[27m"),
1038
- hidden: f("\x1B[8m", "\x1B[28m"),
1039
- strikethrough: f("\x1B[9m", "\x1B[29m"),
1040
- black: f("\x1B[30m", "\x1B[39m"),
1041
- red: f("\x1B[31m", "\x1B[39m"),
1042
- green: f("\x1B[32m", "\x1B[39m"),
1043
- yellow: f("\x1B[33m", "\x1B[39m"),
1044
- blue: f("\x1B[34m", "\x1B[39m"),
1045
- magenta: f("\x1B[35m", "\x1B[39m"),
1046
- cyan: f("\x1B[36m", "\x1B[39m"),
1047
- white: f("\x1B[37m", "\x1B[39m"),
1048
- gray: f("\x1B[90m", "\x1B[39m"),
1049
- bgBlack: f("\x1B[40m", "\x1B[49m"),
1050
- bgRed: f("\x1B[41m", "\x1B[49m"),
1051
- bgGreen: f("\x1B[42m", "\x1B[49m"),
1052
- bgYellow: f("\x1B[43m", "\x1B[49m"),
1053
- bgBlue: f("\x1B[44m", "\x1B[49m"),
1054
- bgMagenta: f("\x1B[45m", "\x1B[49m"),
1055
- bgCyan: f("\x1B[46m", "\x1B[49m"),
1056
- bgWhite: f("\x1B[47m", "\x1B[49m"),
1057
- blackBright: f("\x1B[90m", "\x1B[39m"),
1058
- redBright: f("\x1B[91m", "\x1B[39m"),
1059
- greenBright: f("\x1B[92m", "\x1B[39m"),
1060
- yellowBright: f("\x1B[93m", "\x1B[39m"),
1061
- blueBright: f("\x1B[94m", "\x1B[39m"),
1062
- magentaBright: f("\x1B[95m", "\x1B[39m"),
1063
- cyanBright: f("\x1B[96m", "\x1B[39m"),
1064
- whiteBright: f("\x1B[97m", "\x1B[39m"),
1065
- bgBlackBright: f("\x1B[100m", "\x1B[49m"),
1066
- bgRedBright: f("\x1B[101m", "\x1B[49m"),
1067
- bgGreenBright: f("\x1B[102m", "\x1B[49m"),
1068
- bgYellowBright: f("\x1B[103m", "\x1B[49m"),
1069
- bgBlueBright: f("\x1B[104m", "\x1B[49m"),
1070
- bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
1071
- bgCyanBright: f("\x1B[106m", "\x1B[49m"),
1072
- bgWhiteBright: f("\x1B[107m", "\x1B[49m")
1073
- };
1074
- };
1075
- module.exports = createColors();
1076
- module.exports.createColors = createColors;
1077
- });
1078
-
1079
1124
  // src/version/version.ts
1080
1125
  function isNpxInvocation(channel = INSTALL_CHANNEL, env = process.env) {
1081
1126
  return channel === "npm" && (env.npm_lifecycle_event === "npx" || env.npm_command === "exec");
@@ -1083,7 +1128,7 @@ function isNpxInvocation(channel = INSTALL_CHANNEL, env = process.env) {
1083
1128
  function getVersionString() {
1084
1129
  return `v${VERSION}`;
1085
1130
  }
1086
- var VERSION = "0.67.0", INSTALL_CHANNEL = "npm";
1131
+ var VERSION = "0.67.2", INSTALL_CHANNEL = "npm";
1087
1132
 
1088
1133
  // src/debug/logger.ts
1089
1134
  import {
@@ -1173,18 +1218,18 @@ function writeEntry(level, mod, message) {
1173
1218
  } catch {}
1174
1219
  if (debugToConsole) {
1175
1220
  const colorize = {
1176
- DEBUG: import_picocolors.default.dim,
1177
- INFO: import_picocolors.default.cyan,
1178
- WARN: import_picocolors.default.yellow,
1179
- ERROR: import_picocolors.default.red
1221
+ DEBUG: import_picocolors2.default.dim,
1222
+ INFO: import_picocolors2.default.cyan,
1223
+ WARN: import_picocolors2.default.yellow,
1224
+ ERROR: import_picocolors2.default.red
1180
1225
  }[level];
1181
1226
  console.error(colorize(line.trimEnd()));
1182
1227
  }
1183
1228
  }
1184
- var import_picocolors, MAX_LOG_SIZE = 1048576, TRUNCATE_KEEP = 524288, LOG_FILENAME = "debug.log", REDACTED = "[REDACTED]", SECRET_QUERY_KEYS, initialized = false, debugToConsole = false, logFilePath = null, ANSI_PATTERN, logger;
1229
+ var import_picocolors2, MAX_LOG_SIZE = 1048576, TRUNCATE_KEEP = 524288, LOG_FILENAME = "debug.log", REDACTED = "[REDACTED]", SECRET_QUERY_KEYS, initialized = false, debugToConsole = false, logFilePath = null, ANSI_PATTERN, logger;
1185
1230
  var init_logger = __esm(() => {
1186
1231
  init_config();
1187
- import_picocolors = __toESM(require_picocolors(), 1);
1232
+ import_picocolors2 = __toESM(require_picocolors(), 1);
1188
1233
  SECRET_QUERY_KEYS = [
1189
1234
  "access_token",
1190
1235
  "refresh_token",
@@ -7187,8 +7232,7 @@ var init_arguments = __esm(() => {
7187
7232
  function requireLoginConfig() {
7188
7233
  const cfg = loadConfig();
7189
7234
  if (!cfg.active_account?.session.access_token) {
7190
- console.error(import_picocolors2.default.red("Not logged in. Run 'dosu login' first."));
7191
- process.exit(1);
7235
+ throw new CommandError("NOT_LOGGED_IN", "Not logged in. Run 'dosu login' first.");
7192
7236
  }
7193
7237
  return cfg;
7194
7238
  }
@@ -7196,22 +7240,20 @@ function requireOrgConfig() {
7196
7240
  const cfg = requireLoginConfig();
7197
7241
  const orgId = cfg.active_account?.target?.org_id;
7198
7242
  if (!orgId) {
7199
- console.error(import_picocolors2.default.red("Missing org config. Run 'dosu setup' to reconfigure."));
7200
- process.exit(1);
7243
+ throw new CommandError("NO_ORG_SELECTED", "Missing org config. Run 'dosu setup' to reconfigure.");
7201
7244
  }
7202
7245
  return { cfg, orgId };
7203
7246
  }
7204
7247
  function requireAPIKey(cfg) {
7205
- if (!cfg.active_account?.target?.api_key) {
7206
- console.error(import_picocolors2.default.red("API key not configured. Run 'dosu setup' first."));
7207
- process.exit(1);
7248
+ const apiKey = cfg.active_account?.target?.api_key;
7249
+ if (!apiKey) {
7250
+ throw new CommandError("NO_API_KEY", "API key not configured. Run 'dosu setup' first.");
7208
7251
  }
7209
- return cfg.active_account?.target?.api_key;
7252
+ return apiKey;
7210
7253
  }
7211
- var import_picocolors2;
7212
7254
  var init_auth = __esm(() => {
7255
+ init_command_error();
7213
7256
  init_config();
7214
- import_picocolors2 = __toESM(require_picocolors(), 1);
7215
7257
  });
7216
7258
 
7217
7259
  // node_modules/fast-string-truncated-width/dist/utils.js
@@ -11430,8 +11472,16 @@ function requireConfig4() {
11430
11472
  return requireLoginConfig();
11431
11473
  }
11432
11474
  async function listAccessibleDeployments(client, activeOrgId) {
11433
- if (activeOrgId)
11434
- return client.workspaces.listForOrg.query(activeOrgId);
11475
+ if (activeOrgId) {
11476
+ const deployments = await client.workspaces.listForOrg.query(activeOrgId);
11477
+ if (deployments.length > 0)
11478
+ return deployments;
11479
+ const orgs = await client.organization.getOrganizations.query({});
11480
+ if (!orgs.some((org) => org.org_id === activeOrgId)) {
11481
+ throw new CommandError("ORG_UNAVAILABLE", "The saved organization is not available to this account.", ["Run 'dosu setup' to choose an organization and MCP deployment."]);
11482
+ }
11483
+ return deployments;
11484
+ }
11435
11485
  const orgs = await client.organization.getOrganizations.query({});
11436
11486
  const deployments = await Promise.all(orgs.map((org) => client.workspaces.listForOrg.query(org.org_id)));
11437
11487
  return deployments.flat();
@@ -11445,15 +11495,11 @@ async function resolveDeploymentId(client, activeOrgId, id) {
11445
11495
  if (matches.length === 1)
11446
11496
  return matches[0].deployment_id;
11447
11497
  if (matches.length === 0) {
11448
- console.error(import_picocolors15.default.red(`Deployment not found: ${id}`));
11449
- console.error(import_picocolors15.default.dim("Expected a full deployment ID (UUID). Run 'dosu deployments list --json' to see full IDs."));
11450
- process.exit(1);
11451
- }
11452
- console.error(import_picocolors15.default.red(`Ambiguous deployment ID prefix: ${id} matches ${matches.length}:`));
11453
- for (const d of matches) {
11454
- console.error(import_picocolors15.default.dim(` ${d.deployment_id} ${d.name}`));
11498
+ throw new CommandError("DEPLOYMENT_NOT_FOUND", `Deployment not found: ${id}`, [
11499
+ "Expected a full deployment ID (UUID). Run 'dosu deployments list --json' to see full IDs."
11500
+ ]);
11455
11501
  }
11456
- process.exit(1);
11502
+ throw new CommandError("DEPLOYMENT_AMBIGUOUS", `Ambiguous deployment ID prefix: ${id} matches ${matches.length}:`, matches.map((d) => ` ${d.deployment_id} ${d.name}`));
11457
11503
  }
11458
11504
  function deploymentsCommand() {
11459
11505
  const cmd = new Command("deployments").description("Select the MCP deployment (and its Library) this CLI uses; not for Agents");
@@ -11484,14 +11530,12 @@ ${import_picocolors15.default.dim(`Current: ${cfg.active_account?.target?.deploy
11484
11530
  cmd.command("info").description("Show the selected MCP deployment").option("--json", "Output as JSON").action(async (opts) => {
11485
11531
  const cfg = requireConfig4();
11486
11532
  if (!cfg.active_account?.target?.deployment_id) {
11487
- console.error(import_picocolors15.default.red("No deployment selected. Run 'dosu setup' or 'dosu deployments switch'."));
11488
- process.exit(1);
11533
+ throw new CommandError("NO_DEPLOYMENT_SELECTED", "No deployment selected. Run 'dosu setup' or 'dosu deployments switch'.");
11489
11534
  }
11490
11535
  const client = createTypedClient(cfg);
11491
11536
  const deployment = await client.workspaces.get.query(cfg.active_account?.target?.deployment_id);
11492
11537
  if (!deployment) {
11493
- console.error(import_picocolors15.default.red(`Deployment not found: ${cfg.active_account?.target?.deployment_id}`));
11494
- process.exit(1);
11538
+ throw new CommandError("DEPLOYMENT_UNAVAILABLE", `Deployment not found: ${cfg.active_account?.target?.deployment_id}`);
11495
11539
  }
11496
11540
  if (opts.json) {
11497
11541
  printResult(deployment, opts);
@@ -11514,12 +11558,10 @@ ${import_picocolors15.default.dim(`Current: ${cfg.active_account?.target?.deploy
11514
11558
  const id = await resolveDeploymentId(client, cfg.active_account?.target?.org_id, rawId);
11515
11559
  const deployment = await client.workspaces.get.query(id);
11516
11560
  if (!deployment) {
11517
- console.error(import_picocolors15.default.red(`Deployment not found: ${id}`));
11518
- process.exit(1);
11561
+ throw new CommandError("DEPLOYMENT_NOT_FOUND", `Deployment not found: ${id}`);
11519
11562
  }
11520
11563
  if (deployment.provider_slug !== MCP_PROVIDER_SLUG) {
11521
- console.error(import_picocolors15.default.red(`Not a Dosu MCP deployment: ${id}`));
11522
- process.exit(1);
11564
+ throw new CommandError("NOT_MCP_DEPLOYMENT", `Not a Dosu MCP deployment: ${id}`);
11523
11565
  }
11524
11566
  const apiKey = await new Client(cfg).createAPIKey(deployment.deployment_id, "dosu-cli");
11525
11567
  updateTarget(cfg, {
@@ -11542,6 +11584,7 @@ ${import_picocolors15.default.dim(`Current: ${cfg.active_account?.target?.deploy
11542
11584
  var import_picocolors15, MCP_PROVIDER_SLUG = "dosu_mcp";
11543
11585
  var init_deployments = __esm(() => {
11544
11586
  init_commander();
11587
+ init_command_error();
11545
11588
  init_client();
11546
11589
  init_trpc();
11547
11590
  init_config();
@@ -14290,7 +14333,7 @@ var init_conflicts = __esm(() => {
14290
14333
  });
14291
14334
 
14292
14335
  // src/telemetry/debug-id.ts
14293
- var BUNDLE_DEBUG_ID_PLACEHOLDER = "5d3e9365-4283-2b42-6475-6e2164756e21";
14336
+ var BUNDLE_DEBUG_ID_PLACEHOLDER = "fa693edb-14f4-abcb-6475-6e2164756e21";
14294
14337
 
14295
14338
  // src/telemetry/telemetry.ts
14296
14339
  import { randomUUID as randomUUID2 } from "node:crypto";
@@ -15089,10 +15132,11 @@ function createCommandTelemetry(settings, dependencies = {}) {
15089
15132
  };
15090
15133
  }
15091
15134
  function shouldSendToSentry(error) {
15092
- return !["SESSION_EXPIRED", "SESSION_PERSISTENCE_ERROR"].includes(error.code ?? "");
15135
+ return !EXPECTED_STATE_ERROR_CODES.has(error.code ?? "");
15093
15136
  }
15094
- var REQUEST_TIMEOUT_MS = 500, MAX_RESPONSE_BYTES, MAX_STACK_FRAMES = 20, UNKNOWN_COMMAND = "unknown", FALLBACK_INSTALL_ID = "00000000-0000-4000-8000-000000000000", BUNDLE_CODE_FILE = "app:///bin/dosu.js", CLI_PACKAGE_ROOT, SAFE_ERROR_TYPES, SAFE_ERROR_CODES, SYNC_TRIGGERS, SYNC_STATUSES, LEARNER_OUTCOMES, GATEWAY_REASONS, CLAUDE_CODE_SOURCES, SETTINGS_CONFLICT_KEYS, SETTINGS_CONFLICT_FAMILIES, MAX_CONFLICT_KEYS_LENGTH = 200, CLAUDE_CODE_VERSION_PATTERN, LEARNER_MODEL_PATTERN, BACKFILL_OFFERS, SENTRY_FACET_TAGS, pendingFacets;
15137
+ var REQUEST_TIMEOUT_MS = 500, MAX_RESPONSE_BYTES, MAX_STACK_FRAMES = 20, UNKNOWN_COMMAND = "unknown", FALLBACK_INSTALL_ID = "00000000-0000-4000-8000-000000000000", BUNDLE_CODE_FILE = "app:///bin/dosu.js", CLI_PACKAGE_ROOT, SAFE_ERROR_TYPES, SAFE_ERROR_CODES, EXPECTED_STATE_ERROR_CODES, SYNC_TRIGGERS, SYNC_STATUSES, LEARNER_OUTCOMES, GATEWAY_REASONS, CLAUDE_CODE_SOURCES, SETTINGS_CONFLICT_KEYS, SETTINGS_CONFLICT_FAMILIES, MAX_CONFLICT_KEYS_LENGTH = 200, CLAUDE_CODE_VERSION_PATTERN, LEARNER_MODEL_PATTERN, BACKFILL_OFFERS, SENTRY_FACET_TAGS, pendingFacets;
15095
15138
  var init_telemetry = __esm(() => {
15139
+ init_command_error();
15096
15140
  init_conflicts();
15097
15141
  MAX_RESPONSE_BYTES = 1024 * 1024;
15098
15142
  CLI_PACKAGE_ROOT = detectCliPackageRoot();
@@ -15173,7 +15217,13 @@ var init_telemetry = __esm(() => {
15173
15217
  "commander.missingMandatoryOptionValue",
15174
15218
  "commander.optionMissingArgument",
15175
15219
  "commander.unknownCommand",
15176
- "commander.unknownOption"
15220
+ "commander.unknownOption",
15221
+ ...COMMAND_ERROR_CODES
15222
+ ]);
15223
+ EXPECTED_STATE_ERROR_CODES = new Set([
15224
+ "SESSION_EXPIRED",
15225
+ "SESSION_PERSISTENCE_ERROR",
15226
+ ...COMMAND_ERROR_CODES
15177
15227
  ]);
15178
15228
  SYNC_TRIGGERS = new Set(["hook", "manual", "bootstrap"]);
15179
15229
  SYNC_STATUSES = new Set([
@@ -60317,25 +60367,27 @@ function bareMessageId(id) {
60317
60367
  function truncateId(id) {
60318
60368
  return (isDraftId(id) ? bareMessageId(id) : id).slice(0, 8);
60319
60369
  }
60370
+ function reviewItemNotFound(id) {
60371
+ return new CommandError("REVIEW_ITEM_NOT_FOUND", `No review item found for '${id}'. Run 'dosu review list' to see pending items.`);
60372
+ }
60320
60373
  async function requireChange(client, id) {
60321
60374
  try {
60322
60375
  return await client.review.getChange.query({ id });
60323
60376
  } catch (err) {
60324
- if (isNotFound(err) || isInvalidId(err)) {
60325
- console.error(import_picocolors27.default.red(`No review item found for '${id}'. Run 'dosu review list' to see pending items.`));
60326
- process.exit(1);
60327
- }
60377
+ if (isNotFound(err) || isInvalidId(err))
60378
+ throw reviewItemNotFound(id);
60328
60379
  throw err;
60329
60380
  }
60330
60381
  }
60331
60382
  async function requireDraft(client, id) {
60332
60383
  const draft = await client.messages.getMessage.query(bareMessageId(id));
60333
- if (!draft) {
60334
- console.error(import_picocolors27.default.red(`No review item found for '${id}'. Run 'dosu review list' to see pending items.`));
60335
- process.exit(1);
60336
- }
60384
+ if (!draft)
60385
+ throw reviewItemNotFound(id);
60337
60386
  return draft;
60338
60387
  }
60388
+ function invalidArgument(message) {
60389
+ return new CommandError("INVALID_ARGUMENT", message);
60390
+ }
60339
60391
  function humanizeSource(origin, version) {
60340
60392
  switch (origin) {
60341
60393
  case "manual_update":
@@ -60387,10 +60439,8 @@ function printDraftPreview(draft) {
60387
60439
  console.log();
60388
60440
  console.log(draft.body ?? "");
60389
60441
  }
60390
- function failScope(message, hint = SELECT_LIBRARY_HINT) {
60391
- console.error(import_picocolors27.default.red(message));
60392
- console.error(import_picocolors27.default.dim(hint));
60393
- process.exit(1);
60442
+ function failScope(code, message, hint = SELECT_LIBRARY_HINT) {
60443
+ throw new CommandError(code, message, [hint]);
60394
60444
  }
60395
60445
  async function lookupLibrary(client, spaceId) {
60396
60446
  try {
@@ -60409,13 +60459,13 @@ async function resolveReviewScope(client, spaceId, deploymentId) {
60409
60459
  deploymentId ? client.workspaces.get.query(deploymentId) : Promise.resolve(undefined)
60410
60460
  ]);
60411
60461
  if (deploymentId && !deployment) {
60412
- failScope(`The selected MCP deployment (${deploymentId}) is unavailable: it was deleted, or the ` + "signed-in account cannot access it.");
60462
+ failScope("DEPLOYMENT_UNAVAILABLE", `The selected MCP deployment (${deploymentId}) is unavailable: it was deleted, or the ` + "signed-in account cannot access it.");
60413
60463
  }
60414
60464
  if (deployment && deployment.space_id !== spaceId) {
60415
- failScope(`The saved Library (${spaceId}) does not match the Library of the selected MCP ` + `deployment (${deployment.space_id}).`, `Run 'dosu deployments switch ${deployment.deployment_id}' to save that deployment's ` + "Library again, or choose another MCP deployment with 'dosu deployments list'.");
60465
+ failScope("SCOPE_MISMATCH", `The saved Library (${spaceId}) does not match the Library of the selected MCP ` + `deployment (${deployment.space_id}).`, `Run 'dosu deployments switch ${deployment.deployment_id}' to save that deployment's ` + "Library again, or choose another MCP deployment with 'dosu deployments list'.");
60416
60466
  }
60417
60467
  if (!library.found || !store) {
60418
- failScope(`The selected Library (${spaceId}) is unavailable: it was deleted, or the signed-in ` + "account cannot access it.");
60468
+ failScope("LIBRARY_UNAVAILABLE", `The selected Library (${spaceId}) is unavailable: it was deleted, or the signed-in ` + "account cannot access it.");
60419
60469
  }
60420
60470
  return {
60421
60471
  knowledgeStoreId: store.id,
@@ -60443,15 +60493,14 @@ function reviewCommand() {
60443
60493
  try {
60444
60494
  range = resolveTimeRange(opts.since, opts.until);
60445
60495
  } catch (err) {
60446
- console.error(import_picocolors27.default.red(`${err.message}.`));
60447
- process.exit(1);
60496
+ throw invalidArgument(`${err.message}.`);
60448
60497
  }
60449
60498
  const rangeLabel = describeTimeRange(range);
60450
60499
  const inRange = rangeLabel ? ` ${rangeLabel}` : "";
60451
60500
  const cfg = requireConfig10();
60452
60501
  const target = cfg.active_account?.target;
60453
60502
  if (!target?.space_id) {
60454
- failScope("No Library selected. The review list reads the Library of the selected MCP deployment.");
60503
+ failScope("NO_LIBRARY_SELECTED", "No Library selected. The review list reads the Library of the selected MCP deployment.");
60455
60504
  }
60456
60505
  const client = createTypedClient(cfg);
60457
60506
  const scope = await resolveReviewScope(client, target.space_id, target.deployment_id);
@@ -60532,30 +60581,22 @@ function reviewCommand() {
60532
60581
  const cfg = requireConfig10();
60533
60582
  const client = createTypedClient(cfg);
60534
60583
  if (opts.body !== undefined && opts.bodyFile !== undefined) {
60535
- console.error(import_picocolors27.default.red("Pass only one of --body or --body-file."));
60536
- process.exit(1);
60584
+ throw invalidArgument("Pass only one of --body or --body-file.");
60537
60585
  }
60538
60586
  let body = opts.body;
60539
60587
  if (opts.bodyFile) {
60540
60588
  try {
60541
60589
  body = readFileSync19(opts.bodyFile, "utf-8");
60542
60590
  } catch (err) {
60543
- console.error(import_picocolors27.default.red(`Failed to read --body-file: ${err.message}`));
60544
- process.exit(1);
60591
+ throw invalidArgument(`Failed to read --body-file: ${err.message}`);
60545
60592
  }
60546
60593
  }
60547
60594
  if (body === undefined && opts.title === undefined) {
60548
- console.error(import_picocolors27.default.red("Nothing to edit. Pass --title and/or --body/--body-file."));
60549
- process.exit(1);
60595
+ throw invalidArgument("Nothing to edit. Pass --title and/or --body/--body-file.");
60550
60596
  }
60551
60597
  if (isDraftId(id)) {
60552
- if (opts.title !== undefined) {
60553
- console.error(import_picocolors27.default.red("Draft replies support --body only (no --title)."));
60554
- process.exit(1);
60555
- }
60556
- if (body === undefined) {
60557
- console.error(import_picocolors27.default.red("Draft replies require --body or --body-file."));
60558
- process.exit(1);
60598
+ if (opts.title !== undefined || body === undefined) {
60599
+ throw invalidArgument("Draft replies support --body only (no --title).");
60559
60600
  }
60560
60601
  await requireDraft(client, id);
60561
60602
  await client.messages.saveDraft.mutate({
@@ -60571,8 +60612,7 @@ function reviewCommand() {
60571
60612
  });
60572
60613
  } catch (err) {
60573
60614
  if (isNotFound(err)) {
60574
- console.error(import_picocolors27.default.red(`No pending review item found for '${id}'. Run 'dosu review list' to see editable items.`));
60575
- process.exit(1);
60615
+ throw new CommandError("REVIEW_ITEM_NOT_FOUND", `No pending review item found for '${id}'. Run 'dosu review list' to see editable items.`);
60576
60616
  }
60577
60617
  throw err;
60578
60618
  }
@@ -60653,8 +60693,7 @@ function reviewCommand() {
60653
60693
  const client = createTypedClient(cfg);
60654
60694
  if (isDraftId(id)) {
60655
60695
  await requireDraft(client, id);
60656
- console.error(import_picocolors27.default.red("Revert is not supported for draft replies. A rejected draft is regenerated on the next agent run."));
60657
- process.exit(1);
60696
+ throw invalidArgument("Revert is not supported for draft replies. A rejected draft is regenerated on the next agent run.");
60658
60697
  }
60659
60698
  await requireChange(client, id);
60660
60699
  await client.page.updatePublicationStatus.mutate({
@@ -60674,6 +60713,7 @@ var import_picocolors27, DRAFT_MESSAGE_ID_PREFIX = "draft_message:", SELECT_LIBR
60674
60713
  var init_review = __esm(() => {
60675
60714
  init_dist();
60676
60715
  init_commander();
60716
+ init_command_error();
60677
60717
  init_trpc();
60678
60718
  init_auth();
60679
60719
  init_confirmation();
@@ -60767,7 +60807,7 @@ Use [commands.md](references/commands.md) as the sole detailed command and flag
60767
60807
  ## Handle failures literally
60768
60808
 
60769
60809
  - \`Not logged in\` or an unrecoverable expired session: run \`dosu login\` (agent setup may instead return a ticket flow).
60770
- - Missing organization, Library, deployment, or API-key context: run the appropriate \`dosu setup\` flow.
60810
+ - Missing or unavailable organization, Library, deployment, or API-key context: tell the user which \`dosu setup\` flow fixes it. Plain \`dosu setup\` is interactive, so don't start it for them unless they ask.
60771
60811
  - Wrong or unexpected Library context: read \`dosu status --json\` and \`dosu deployments info --json\` before changing anything. Switch only to an MCP deployment the user named or confirmed; never pick the first one listed, and never switch just because a list came back empty.
60772
60812
  - Skill guidance out of date: \`dosu skill update\` rewrites the installed skill from the running CLI version. Some agents read skills only when a session starts, so start a new session if the change does not appear. Installed files show the skill is on disk, not that the agent loaded it.
60773
60813
  - \`confirmRequired\`: no write occurred.
@@ -62452,6 +62492,65 @@ var init_topics = __esm(() => {
62452
62492
  import_picocolors31 = __toESM(require_picocolors(), 1);
62453
62493
  });
62454
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
+
62455
62554
  // src/mcp/providers/base.ts
62456
62555
  function createJSONProvider(opts) {
62457
62556
  const buildServer = opts.buildServer ?? defaultBuildServer;
@@ -62464,6 +62563,7 @@ function createJSONProvider(opts) {
62464
62563
  isInstalled: () => isInstalled(opts.paths),
62465
62564
  globalConfigPath: () => expandHome(opts.globalPath),
62466
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)),
62467
62567
  install(cfg, global2) {
62468
62568
  const endpoint = {
62469
62569
  url: mcpEndpoint(cfg),
@@ -62477,7 +62577,7 @@ function createJSONProvider(opts) {
62477
62577
  } else {
62478
62578
  throw new Error(`${opts.providerName} does not support local installation`);
62479
62579
  }
62480
- installJSONServer(configPath, opts.topKey, buildServer(endpoint, cfg));
62580
+ installJSONServer(configPath, opts.topKey, buildServer(endpoint));
62481
62581
  },
62482
62582
  remove(global2) {
62483
62583
  let configPath;
@@ -62500,6 +62600,7 @@ var defaultBuildServer = ({ url, headers }) => ({
62500
62600
  var init_base = __esm(() => {
62501
62601
  init_config_helpers();
62502
62602
  init_detect();
62603
+ init_shape();
62503
62604
  });
62504
62605
 
62505
62606
  // src/mcp/providers/antigravity.ts
@@ -62551,17 +62652,13 @@ var ClaudeDesktopProvider = () => ({
62551
62652
  isInstalled: () => isInstalled([join26(appSupportDir(), "Claude")]),
62552
62653
  globalConfigPath: () => configPath(),
62553
62654
  isConfigured: () => isJSONKeyConfigured(configPath(), "mcpServers"),
62655
+ isCurrent: (cfg) => entryHasShape(npxRemoteEntry(shapeEndpoint(cfg), ANY, ANY, npxPathEnv(join26(ANY, "npx"))), readJSONServer(configPath(), "mcpServers")),
62554
62656
  install(cfg, global2) {
62555
62657
  if (!global2)
62556
62658
  throw new Error("Claude Desktop does not support local installation");
62557
62659
  const url = mcpEndpoint(cfg);
62558
62660
  const npx = findNpx();
62559
- const remote = mcpRemoteServer(url, cfg.active_account?.target?.api_key);
62560
- installJSONServer(configPath(), "mcpServers", {
62561
- command: npx,
62562
- args: remote.args,
62563
- env: { PATH: npxPathEnv(npx), ...remote.env }
62564
- });
62661
+ installJSONServer(configPath(), "mcpServers", npxRemoteEntry(url, cfg.active_account?.target?.api_key, npx, npxPathEnv(npx)));
62565
62662
  },
62566
62663
  remove(global2) {
62567
62664
  if (!global2)
@@ -62572,6 +62669,7 @@ var ClaudeDesktopProvider = () => ({
62572
62669
  var init_claude_desktop = __esm(() => {
62573
62670
  init_config_helpers();
62574
62671
  init_detect();
62672
+ init_shape();
62575
62673
  });
62576
62674
 
62577
62675
  // src/mcp/providers/cline.ts
@@ -62643,25 +62741,70 @@ function writeTOML(path, content) {
62643
62741
  function tomlString(value) {
62644
62742
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
62645
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
+ }
62646
62803
  function installDosuToTOML(path, cfg) {
62647
- let content = readTOML(path);
62648
- content = removeDosuFromTOML(content);
62804
+ const content = removeDosuFromTOML(readTOML(path));
62649
62805
  const npx = findNpx();
62650
- const remote = mcpRemoteServer(mcpEndpoint(cfg), cfg.active_account?.target?.api_key);
62651
- const env = { PATH: npxPathEnv(npx), ...remote.env };
62652
- const envEntries = Object.entries(env).map(([key, value]) => `${key} = ${tomlString(value)}`).join(`
62653
- `);
62654
- const args = remote.args.map(tomlString).join(", ");
62655
- const section = `
62656
- [mcp_servers.dosu]
62657
- command = ${tomlString(npx)}
62658
- args = [${args}]
62659
- ` + `
62660
- [mcp_servers.dosu.env]
62661
- ${envEntries}
62662
- `;
62663
- content += section;
62664
- writeTOML(path, content);
62806
+ const entry = dosuEntry(mcpEndpoint(cfg), cfg.active_account?.target?.api_key, npx, npxPathEnv(npx));
62807
+ writeTOML(path, content + renderDosuEntry(entry));
62665
62808
  }
62666
62809
  function sectionName(line) {
62667
62810
  const header = line.trim();
@@ -62739,6 +62882,7 @@ var REMOTE_HTTP_KEY, CodexProvider = () => ({
62739
62882
  const content = readTOML(join29(codexHome3(), "config.toml"));
62740
62883
  return content.includes("[mcp_servers.dosu]");
62741
62884
  },
62885
+ isCurrent: (cfg) => entryHasShape(dosuEntry(shapeEndpoint(cfg), ANY, ANY, npxPathEnv(join29(ANY, "npx"))), readDosuEntry(readTOML(join29(codexHome3(), "config.toml")))),
62742
62886
  install(cfg, global2) {
62743
62887
  if (cfg.mode !== MODE_OSS && !cfg.active_account?.target?.deployment_id)
62744
62888
  throw new Error("deployment ID is required");
@@ -62763,11 +62907,15 @@ var init_codex = __esm(() => {
62763
62907
  init_config();
62764
62908
  init_config_helpers();
62765
62909
  init_detect();
62910
+ init_shape();
62766
62911
  REMOTE_HTTP_KEY = /^(url|type|bearer_token_env_var)\s*=/;
62767
62912
  });
62768
62913
 
62769
62914
  // src/mcp/providers/copilot.ts
62770
62915
  import { join as join30 } from "node:path";
62916
+ function globalServer(url, apiKey) {
62917
+ return { type: "http", url, tools: ["*"], headers: mcpHeaders(apiKey) };
62918
+ }
62771
62919
  function globalPath() {
62772
62920
  if (process.env.XDG_CONFIG_HOME) {
62773
62921
  return join30(process.env.XDG_CONFIG_HOME, "mcp-config.json");
@@ -62783,16 +62931,11 @@ var CopilotProvider = () => ({
62783
62931
  isInstalled: () => isInstalled([expandHome("~/.copilot")]),
62784
62932
  globalConfigPath: () => globalPath(),
62785
62933
  isConfigured: () => isJSONKeyConfigured(globalPath(), "mcpServers"),
62934
+ isCurrent: (cfg) => entryHasShape(globalServer(shapeEndpoint(cfg), ANY), readJSONServer(globalPath(), "mcpServers")),
62786
62935
  install(cfg, global2) {
62787
62936
  const url = mcpEndpoint(cfg);
62788
62937
  if (global2) {
62789
- const server = {
62790
- type: "http",
62791
- url,
62792
- tools: ["*"],
62793
- headers: mcpHeaders(cfg.active_account.target.api_key)
62794
- };
62795
- installJSONServer(globalPath(), "mcpServers", server);
62938
+ installJSONServer(globalPath(), "mcpServers", globalServer(url, cfg.active_account?.target?.api_key));
62796
62939
  } else {
62797
62940
  const configPath = join30(process.cwd(), ".vscode", "mcp.json");
62798
62941
  const server = {
@@ -62815,6 +62958,7 @@ var CopilotProvider = () => ({
62815
62958
  var init_copilot = __esm(() => {
62816
62959
  init_config_helpers();
62817
62960
  init_detect();
62961
+ init_shape();
62818
62962
  });
62819
62963
 
62820
62964
  // src/mcp/providers/cursor.ts
@@ -62907,6 +63051,9 @@ var init_manual = __esm(() => {
62907
63051
  // src/mcp/providers/mcporter.ts
62908
63052
  import { existsSync as existsSync23 } from "node:fs";
62909
63053
  import { join as join34 } from "node:path";
63054
+ function server(url, apiKey) {
63055
+ return { type: "http", url, headers: mcpHeaders(apiKey) };
63056
+ }
62910
63057
  function resolveGlobalConfigPath() {
62911
63058
  const jsonPath = expandHome("~/.mcporter/mcporter.json");
62912
63059
  if (existsSync23(jsonPath))
@@ -62925,14 +63072,10 @@ var MCPorterProvider = () => ({
62925
63072
  isInstalled: () => isInstalled(["~/.mcporter"]),
62926
63073
  globalConfigPath: () => resolveGlobalConfigPath(),
62927
63074
  isConfigured: () => isJSONKeyConfigured(resolveGlobalConfigPath(), "mcpServers"),
63075
+ isCurrent: (cfg) => entryHasShape(server(shapeEndpoint(cfg), ANY), readJSONServer(resolveGlobalConfigPath(), "mcpServers")),
62928
63076
  install(cfg, global2) {
62929
63077
  const configPath = global2 ? resolveGlobalConfigPath() : join34(process.cwd(), "config", "mcporter.json");
62930
- const server = {
62931
- type: "http",
62932
- url: mcpEndpoint(cfg),
62933
- headers: mcpHeaders(cfg.active_account.target.api_key)
62934
- };
62935
- installJSONServer(configPath, "mcpServers", server);
63078
+ installJSONServer(configPath, "mcpServers", server(mcpEndpoint(cfg), cfg.active_account?.target?.api_key));
62936
63079
  },
62937
63080
  remove(global2) {
62938
63081
  const configPath = global2 ? resolveGlobalConfigPath() : join34(process.cwd(), "config", "mcporter.json");
@@ -62942,6 +63085,7 @@ var MCPorterProvider = () => ({
62942
63085
  var init_mcporter = __esm(() => {
62943
63086
  init_config_helpers();
62944
63087
  init_detect();
63088
+ init_shape();
62945
63089
  });
62946
63090
 
62947
63091
  // src/mcp/providers/opencode.ts
@@ -63055,6 +63199,7 @@ var ZedProvider = (legacyPath = legacyDarwinSettingsPath()) => {
63055
63199
  return provider;
63056
63200
  return {
63057
63201
  ...provider,
63202
+ isCurrent: (cfg) => provider.isCurrent(cfg) && readJSONServer(legacyPath, "context_servers") === undefined,
63058
63203
  install(cfg, global2) {
63059
63204
  provider.install(cfg, global2);
63060
63205
  if (global2)
@@ -63133,9 +63278,21 @@ function configuredProviders() {
63133
63278
  }
63134
63279
  });
63135
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
+ }
63136
63290
  function refreshConfiguredProviders(cfg) {
63291
+ return refreshProviders(cfg, configuredProviders());
63292
+ }
63293
+ function refreshProviders(cfg, providers) {
63137
63294
  const result = { updated: [], failed: [] };
63138
- for (const provider of configuredProviders()) {
63295
+ for (const provider of providers) {
63139
63296
  try {
63140
63297
  provider.install(cfg, true);
63141
63298
  logger.info("mcp", `Refreshed ${provider.name()} MCP entry`);
@@ -63347,199 +63504,18 @@ var init_auto_update = __esm(() => {
63347
63504
  VERSION_PATTERN = /^[0-9A-Za-z.+-]{1,64}$/;
63348
63505
  });
63349
63506
 
63350
- // src/version/update-check.ts
63507
+ // src/version/mcp-refresh-check.ts
63351
63508
  import { existsSync as existsSync25, mkdirSync as mkdirSync15, readFileSync as readFileSync24, writeFileSync as writeFileSync16 } from "node:fs";
63352
63509
  import { join as join40 } from "node:path";
63353
- function isValidVersion2(value) {
63354
- return typeof value === "string" && SEMVER_PATTERN.test(value);
63355
- }
63356
- function stripPrerelease(version) {
63357
- return version.replace(/[-+].*$/, "");
63358
- }
63359
- function isNewerVersion(latest, current) {
63360
- const a = stripPrerelease(latest).split(".").map(Number);
63361
- const b = stripPrerelease(current).split(".").map(Number);
63362
- const len = Math.max(a.length, b.length);
63363
- for (let i = 0;i < len; i++) {
63364
- const av = a[i] ?? 0;
63365
- const bv = b[i] ?? 0;
63366
- if (av > bv)
63367
- return true;
63368
- if (av < bv)
63369
- return false;
63370
- }
63371
- return false;
63372
- }
63373
63510
  function getCachePath2() {
63374
63511
  return join40(getConfigDir(), CACHE_FILENAME3);
63375
63512
  }
63376
- function readCache() {
63513
+ function readMcpRefreshCache() {
63377
63514
  try {
63378
63515
  const path = getCachePath2();
63379
63516
  if (!existsSync25(path))
63380
63517
  return null;
63381
63518
  const data = JSON.parse(readFileSync24(path, "utf-8"));
63382
- if (typeof data.lastCheck === "number" && isValidVersion2(data.latestVersion)) {
63383
- return data;
63384
- }
63385
- return null;
63386
- } catch {
63387
- return null;
63388
- }
63389
- }
63390
- function writeCache(cache) {
63391
- try {
63392
- const dir = getConfigDir();
63393
- if (!existsSync25(dir)) {
63394
- mkdirSync15(dir, { recursive: true, mode: 448 });
63395
- }
63396
- writeFileSync16(getCachePath2(), JSON.stringify(cache), { mode: 384 });
63397
- } catch {}
63398
- }
63399
- async function fetchLatestVersion() {
63400
- const controller = new AbortController;
63401
- const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS2);
63402
- try {
63403
- const resp = await fetch(REGISTRY_URL, { signal: controller.signal });
63404
- if (!resp.ok)
63405
- return null;
63406
- const data = await resp.json();
63407
- const latest = data.latest;
63408
- return isValidVersion2(latest) ? latest : null;
63409
- } catch {
63410
- return null;
63411
- } finally {
63412
- clearTimeout(timeout);
63413
- }
63414
- }
63415
- function buildUpdateHint(channel, npx = false) {
63416
- if (channel === "npm" && npx) {
63417
- return 'Use "npx -y @dosu/cli@latest" for the next Dosu command';
63418
- }
63419
- return 'Run "dosu upgrade"';
63420
- }
63421
- function styleHint(hint) {
63422
- const match = hint.match(/^(.*)("[^"]+")(.*)$/);
63423
- if (!match)
63424
- return import_picocolors32.default.dim(hint);
63425
- const [, before, command, after] = match;
63426
- return [before && import_picocolors32.default.dim(before), import_picocolors32.default.cyan(command), after && import_picocolors32.default.dim(after)].filter(Boolean).join("");
63427
- }
63428
- function framedNotice(content, width) {
63429
- const innerWidth = Math.max(...content.map(visibleWidth2)) + BOX_PADDING * 2;
63430
- const framed = (line) => {
63431
- const pad = innerWidth - visibleWidth2(line);
63432
- const left = Math.floor(pad / 2);
63433
- return `${import_picocolors32.default.yellow("│")}${" ".repeat(left)}${line}${" ".repeat(pad - left)}${import_picocolors32.default.yellow("│")}`;
63434
- };
63435
- const box = [
63436
- import_picocolors32.default.yellow(`╭${"─".repeat(innerWidth)}╮`),
63437
- framed(""),
63438
- ...content.map(framed),
63439
- framed(""),
63440
- import_picocolors32.default.yellow(`╰${"─".repeat(innerWidth)}╯`)
63441
- ];
63442
- return `
63443
- ${centerBlock(box, width).join(`
63444
- `)}
63445
- `;
63446
- }
63447
- function versionArrow(current, latest) {
63448
- return `${import_picocolors32.default.dim(current)} ${import_picocolors32.default.dim("→")} ${import_picocolors32.default.bold(brand(latest))}`;
63449
- }
63450
- function buildAutoUpdateNotice(current, latest, interactive, width = process.stderr.columns ?? 80) {
63451
- if (interactive) {
63452
- return framedNotice([
63453
- `Updating Dosu: ${versionArrow(current, latest)}`,
63454
- import_picocolors32.default.dim("Installing in the background; your next command uses it")
63455
- ], width);
63456
- }
63457
- return `
63458
- [dosu:update] Installing Dosu ${latest} in the background (running ${current}). ` + `The next Dosu command uses it; no action needed.
63459
- `;
63460
- }
63461
- function buildUpdateNotice(current, latest, channel, interactive, npx = false, width = process.stderr.columns ?? 80) {
63462
- const hint = buildUpdateHint(channel, npx);
63463
- if (interactive) {
63464
- return framedNotice([`Update available: ${versionArrow(current, latest)}`, styleHint(hint)], width);
63465
- }
63466
- const agentAction = hint[0].toLowerCase() + hint.slice(1);
63467
- const verifyCommand = npx ? "npx -y @dosu/cli@latest --version" : "dosu --version";
63468
- return `
63469
- [dosu:update] Update available: ${current} → ${latest}
63470
- ` + `Tell the user Dosu CLI is outdated. After they approve, ${agentAction}, ` + `then verify with "${verifyCommand}".
63471
- `;
63472
- }
63473
- function getAvailableUpdate() {
63474
- const cache = readCache();
63475
- return cache && isNewerVersion(cache.latestVersion, VERSION) ? cache.latestVersion : null;
63476
- }
63477
- function handleNewerVersion(latest, notify) {
63478
- const autoUpdate = startAutoUpdate(latest);
63479
- if (!notify)
63480
- return;
63481
- const interactive = process.stderr.isTTY === true;
63482
- console.error(autoUpdate === "unavailable" ? buildUpdateNotice(VERSION, latest, INSTALL_CHANNEL, interactive, isNpxInvocation()) : buildAutoUpdateNotice(VERSION, latest, interactive));
63483
- }
63484
- async function checkForUpdates(options = {}) {
63485
- const notify = options.notify ?? true;
63486
- try {
63487
- const cache = readCache();
63488
- const isStale = !cache || Date.now() - cache.lastCheck > CHECK_INTERVAL_MS2;
63489
- if (!isStale) {
63490
- if (isNewerVersion(cache.latestVersion, VERSION)) {
63491
- handleNewerVersion(cache.latestVersion, notify);
63492
- }
63493
- return;
63494
- }
63495
- const latest = await fetchLatestVersion();
63496
- const latestKnownVersion = latest ?? cache?.latestVersion ?? VERSION;
63497
- writeCache({
63498
- lastCheck: Date.now(),
63499
- latestVersion: latestKnownVersion
63500
- });
63501
- if (latest) {
63502
- logger.debug("update-check", `Cached latest version: ${latest}`);
63503
- }
63504
- if (isNewerVersion(latestKnownVersion, VERSION)) {
63505
- handleNewerVersion(latestKnownVersion, notify);
63506
- }
63507
- } catch (err) {
63508
- logger.error("update-check", `Update check failed: ${err}`);
63509
- }
63510
- }
63511
- 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;
63512
- var init_update_check = __esm(() => {
63513
- init_config();
63514
- init_logger();
63515
- init_styles();
63516
- init_layout();
63517
- init_auto_update();
63518
- import_picocolors32 = __toESM(require_picocolors(), 1);
63519
- CHECK_INTERVAL_MS2 = 6 * 60 * 60 * 1000;
63520
- 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-]+)*)?$/;
63521
- });
63522
-
63523
- // src/version/mcp-refresh-check.ts
63524
- import { existsSync as existsSync26, mkdirSync as mkdirSync16, readFileSync as readFileSync25, writeFileSync as writeFileSync17 } from "node:fs";
63525
- import { join as join41 } from "node:path";
63526
- function needsMcpRefresh(previous, current) {
63527
- if (previous === null)
63528
- return true;
63529
- if (previous === current)
63530
- return false;
63531
- const [older, newer] = isNewerVersion(current, previous) ? [previous, current] : [current, previous];
63532
- return MCP_FORMAT_CHANGES.some((change) => isNewerVersion(change, older) && !isNewerVersion(change, newer));
63533
- }
63534
- function getCachePath3() {
63535
- return join41(getConfigDir(), CACHE_FILENAME4);
63536
- }
63537
- function readMcpRefreshCache() {
63538
- try {
63539
- const path = getCachePath3();
63540
- if (!existsSync26(path))
63541
- return null;
63542
- const data = JSON.parse(readFileSync25(path, "utf-8"));
63543
63519
  if (typeof data?.version === "string")
63544
63520
  return { version: data.version };
63545
63521
  return null;
@@ -63550,10 +63526,10 @@ function readMcpRefreshCache() {
63550
63526
  function writeMcpRefreshCache(cache) {
63551
63527
  try {
63552
63528
  const dir = getConfigDir();
63553
- if (!existsSync26(dir)) {
63554
- mkdirSync16(dir, { recursive: true, mode: 448 });
63529
+ if (!existsSync25(dir)) {
63530
+ mkdirSync15(dir, { recursive: true, mode: 448 });
63555
63531
  }
63556
- writeFileSync17(getCachePath3(), JSON.stringify(cache), { mode: 384 });
63532
+ writeFileSync16(getCachePath2(), JSON.stringify(cache), { mode: 384 });
63557
63533
  } catch {}
63558
63534
  }
63559
63535
  function canRefreshMcp(cfg) {
@@ -63564,26 +63540,21 @@ function canRefreshMcp(cfg) {
63564
63540
  }
63565
63541
  function displayNotice(names) {
63566
63542
  console.error(`
63567
- ${import_picocolors33.default.green(`✓ Dosu ${VERSION}: refreshed MCP config for ${names.join(", ")}`)}
63568
- ` + `${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.`)}
63569
63545
  `);
63570
63546
  }
63571
63547
  function checkForMcpRefresh(options = {}) {
63572
63548
  const notify = options.notify ?? true;
63573
63549
  try {
63574
- const previous = readMcpRefreshCache()?.version ?? null;
63575
- if (previous === VERSION)
63576
- return;
63577
- if (!needsMcpRefresh(previous, VERSION)) {
63578
- writeMcpRefreshCache({ version: VERSION });
63550
+ if (readMcpRefreshCache()?.version === VERSION)
63579
63551
  return;
63580
- }
63581
63552
  const cfg = loadConfigNonBlocking();
63582
63553
  if (!cfg || !canRefreshMcp(cfg)) {
63583
63554
  logger.debug("mcp-refresh", "Skipping MCP refresh: no API key or deployment configured");
63584
63555
  return;
63585
63556
  }
63586
- const result = refreshConfiguredProviders(cfg);
63557
+ const result = refreshProviders(cfg, staleProviders(cfg));
63587
63558
  writeMcpRefreshCache({ version: VERSION });
63588
63559
  logger.info("mcp-refresh", `Version ${VERSION}: refreshed ${result.updated.length}, failed ${result.failed.length}`);
63589
63560
  if (notify && result.updated.length > 0) {
@@ -63593,24 +63564,19 @@ function checkForMcpRefresh(options = {}) {
63593
63564
  logger.error("mcp-refresh", `MCP refresh check failed: ${err}`);
63594
63565
  }
63595
63566
  }
63596
- var import_picocolors33, CACHE_FILENAME4 = "mcp-refresh.json", MCP_FORMAT_CHANGES;
63567
+ var import_picocolors32, CACHE_FILENAME3 = "mcp-refresh.json";
63597
63568
  var init_mcp_refresh_check = __esm(() => {
63598
63569
  init_config();
63599
63570
  init_logger();
63600
63571
  init_refresh();
63601
- init_update_check();
63602
- import_picocolors33 = __toESM(require_picocolors(), 1);
63603
- MCP_FORMAT_CHANGES = [
63604
- "0.53.0",
63605
- "0.62.0"
63606
- ];
63572
+ import_picocolors32 = __toESM(require_picocolors(), 1);
63607
63573
  });
63608
63574
 
63609
63575
  // src/version/skill-update-check.ts
63610
63576
  function displayNotice2() {
63611
63577
  console.error(`
63612
- ${import_picocolors34.default.green(`✓ Dosu ${VERSION}: refreshed the bundled agent skills`)}
63613
- ` + `${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.")}
63614
63580
  `);
63615
63581
  }
63616
63582
  function checkForSkillUpdates(options = {}) {
@@ -63631,11 +63597,11 @@ function checkForSkillUpdates(options = {}) {
63631
63597
  logger.error("skill-update-check", `Skill update check failed: ${err}`);
63632
63598
  }
63633
63599
  }
63634
- var import_picocolors34;
63600
+ var import_picocolors33;
63635
63601
  var init_skill_update_check = __esm(() => {
63636
63602
  init_skill();
63637
63603
  init_logger();
63638
- import_picocolors34 = __toESM(require_picocolors(), 1);
63604
+ import_picocolors33 = __toESM(require_picocolors(), 1);
63639
63605
  });
63640
63606
 
63641
63607
  // src/auth/errors.ts
@@ -63701,15 +63667,15 @@ function createLogFollower(path, emit) {
63701
63667
  var init_follow = () => {};
63702
63668
 
63703
63669
  // src/sessions/session-title.ts
63704
- 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";
63705
63671
  import { homedir as homedir11 } from "node:os";
63706
- import { join as join42 } from "node:path";
63672
+ import { join as join41 } from "node:path";
63707
63673
  function cachePath2(configDir) {
63708
- return join42(configDir, CACHE_FILENAME5);
63674
+ return join41(configDir, CACHE_FILENAME4);
63709
63675
  }
63710
63676
  function loadCacheFile2(configDir) {
63711
63677
  try {
63712
- const raw = JSON.parse(readFileSync26(cachePath2(configDir), "utf-8"));
63678
+ const raw = JSON.parse(readFileSync25(cachePath2(configDir), "utf-8"));
63713
63679
  if (raw.schema_version !== CACHE_SCHEMA_VERSION2 || typeof raw.entries !== "object")
63714
63680
  return {};
63715
63681
  return raw.entries;
@@ -63719,12 +63685,12 @@ function loadCacheFile2(configDir) {
63719
63685
  }
63720
63686
  function saveCacheFile2(configDir, entries) {
63721
63687
  try {
63722
- if (!existsSync27(configDir))
63723
- mkdirSync17(configDir, { recursive: true, mode: 448 });
63688
+ if (!existsSync26(configDir))
63689
+ mkdirSync16(configDir, { recursive: true, mode: 448 });
63724
63690
  const path = cachePath2(configDir);
63725
63691
  const tmp = `${path}.${process.pid}.tmp`;
63726
63692
  const file = { schema_version: CACHE_SCHEMA_VERSION2, entries };
63727
- writeFileSync18(tmp, JSON.stringify(file), { mode: 384 });
63693
+ writeFileSync17(tmp, JSON.stringify(file), { mode: 384 });
63728
63694
  renameSync7(tmp, path);
63729
63695
  } catch {}
63730
63696
  }
@@ -63757,15 +63723,15 @@ function claudeSummaryFromHead(text) {
63757
63723
  }
63758
63724
  return null;
63759
63725
  }
63760
- function reconstructSession(harness, id, slug, home = homedir11(), exists = existsSync27) {
63726
+ function reconstructSession(harness, id, slug, home = homedir11(), exists = existsSync26) {
63761
63727
  if (!slug)
63762
63728
  return null;
63763
63729
  const candidates = [];
63764
63730
  if (harness === "claude") {
63765
- candidates.push(join42(home, ".claude", "projects", slug, `${id}.jsonl`));
63731
+ candidates.push(join41(home, ".claude", "projects", slug, `${id}.jsonl`));
63766
63732
  } else if (harness === "cursor") {
63767
- const project = join42(home, ".cursor", "projects", slug);
63768
- 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`));
63769
63735
  } else {
63770
63736
  return null;
63771
63737
  }
@@ -63777,7 +63743,7 @@ function reconstructSession(harness, id, slug, home = homedir11(), exists = exis
63777
63743
  }
63778
63744
  function readHead2(path) {
63779
63745
  try {
63780
- return readFileSync26(path, "utf-8").slice(0, 128 * 1024);
63746
+ return readFileSync25(path, "utf-8").slice(0, 128 * 1024);
63781
63747
  } catch {
63782
63748
  return null;
63783
63749
  }
@@ -63828,7 +63794,7 @@ function createSessionTitleResolver(configDir = getConfigDir(), deps = {}) {
63828
63794
  }
63829
63795
  };
63830
63796
  }
63831
- 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;
63832
63798
  var init_session_title = __esm(() => {
63833
63799
  init_config();
63834
63800
  init_read();
@@ -63857,7 +63823,7 @@ var init_alt_screen = __esm(() => {
63857
63823
  });
63858
63824
 
63859
63825
  // src/tui/activity-view.ts
63860
- import { existsSync as existsSync28, readFileSync as readFileSync27 } from "node:fs";
63826
+ import { existsSync as existsSync27, readFileSync as readFileSync26 } from "node:fs";
63861
63827
  import { basename as basename6 } from "node:path";
63862
63828
  function reduceActivityViewKey(key) {
63863
63829
  if (key === "q" || key === ESC7 || key === CTRL_C4)
@@ -63998,15 +63964,15 @@ function statusLine(status) {
63998
63964
  if (status.running) {
63999
63965
  const since = status.startedAt ? ` · since ${localTime(status.startedAt)}` : "";
64000
63966
  const pid = status.pid !== undefined ? ` (pid ${status.pid})` : "";
64001
- 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}`)}`;
64002
63968
  }
64003
63969
  if (status.state.paused) {
64004
- 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")}`;
64005
63971
  }
64006
63972
  if (status.staleLock) {
64007
- 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")}`;
64008
63974
  }
64009
- 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")}`;
64010
63976
  }
64011
63977
  function progressLine(done, ready, width, notes = 0) {
64012
63978
  const total = done + ready;
@@ -64015,7 +63981,7 @@ function progressLine(done, ready, width, notes = 0) {
64015
63981
  const cells = Math.max(10, Math.min(30, width - 44));
64016
63982
  const ratio = Math.max(0, Math.min(1, done / total));
64017
63983
  const filled = Math.min(cells, Math.round(ratio * cells));
64018
- 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));
64019
63985
  const pct = Math.floor(ratio * 100);
64020
63986
  const suffix = notes > 0 ? ` · ${notes} suggested page${notes === 1 ? "" : "s"}` : "";
64021
63987
  return `${bar} ${done}/${total} studied · ${pct}%${suffix}`;
@@ -64054,17 +64020,17 @@ function confirmBox(queuedCount, backlog, width, mode = "start") {
64054
64020
  const verb = mode === "clear" ? "clear" : mode === "stop" ? "stop" : mode === "resume" ? "resume" : "start";
64055
64021
  const maxInner = Math.max(20, Math.min(width, contentWidth()) - 4);
64056
64022
  const rows = [
64057
- import_picocolors35.default.bold(brand(title)),
64058
- ...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)),
64059
64025
  "",
64060
- `${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`
64061
64027
  ];
64062
64028
  const inner = Math.min(maxInner, Math.max(...rows.map(visibleWidth2)));
64063
64029
  const pad = (row) => row + " ".repeat(Math.max(0, inner - visibleWidth2(row)));
64064
64030
  return [
64065
- import_picocolors35.default.dim(`╭${"─".repeat(inner + 2)}╮`),
64066
- ...rows.map((row) => `${import_picocolors35.default.dim("│")} ${pad(row)} ${import_picocolors35.default.dim("│")}`),
64067
- 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)}╯`)
64068
64034
  ];
64069
64035
  }
64070
64036
  function buildActivityFrame(status, activity, width, backlog = null, pane = DEFAULT_PANE, queued = [], studiedBeforeRun = 0, open = [], runProgress = null, projectNames = {}, sessionNames = {}, frameLines) {
@@ -64080,7 +64046,7 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
64080
64046
  progress = progressLine(studiedDelta + stepDone, Math.max(0, backlog.ready - stepDone), width, runProgress?.notes ?? 0);
64081
64047
  }
64082
64048
  const refusal = !status.running && status.state.last_refusal;
64083
- 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)) : [];
64084
64050
  const fullRows = Boolean(pane.fullRows);
64085
64051
  const withName = (item, key) => projectNames[key] ? { ...item, project: projectNames[key] } : item;
64086
64052
  const latestPass = new Map;
@@ -64106,8 +64072,8 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
64106
64072
  breadcrumb(["home", "activity"], width),
64107
64073
  "",
64108
64074
  statusLine(status),
64109
- import_picocolors35.default.dim(studied),
64110
- ...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)) : [],
64111
64077
  ...progress ? [progress] : [],
64112
64078
  ...refusalLines,
64113
64079
  "",
@@ -64123,7 +64089,7 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
64123
64089
  status.running ? "s stop" : status.state.paused ? "s resume" : "s sync now",
64124
64090
  ...!status.running && status.state.watermark ? ["c clear"] : [],
64125
64091
  "esc back"
64126
- ].join(" · "), width, "").map((line) => import_picocolors35.default.dim(line))
64092
+ ].join(" · "), width, "").map((line) => import_picocolors34.default.dim(line))
64127
64093
  ]
64128
64094
  ];
64129
64095
  const defaultHeight = fullRows ? ACTIVITY_VIEW_FULL_LIST_ROWS : ACTIVITY_VIEW_LIST_LINES;
@@ -64136,7 +64102,7 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
64136
64102
  ({ visible, above, below } = windowList(source, pane.scroll, height));
64137
64103
  display = visible.flatMap((row) => wrapRow(row, width));
64138
64104
  }
64139
- 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)];
64140
64106
  const scrollParts = [];
64141
64107
  if (above > 0)
64142
64108
  scrollParts.push(`↑ ${above} earlier`);
@@ -64145,7 +64111,7 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
64145
64111
  const lines = [
64146
64112
  ...header,
64147
64113
  ...listRows,
64148
- ...scrollParts.length > 0 ? [import_picocolors35.default.dim(clip(scrollParts.join(" · "), width))] : [],
64114
+ ...scrollParts.length > 0 ? [import_picocolors34.default.dim(clip(scrollParts.join(" · "), width))] : [],
64149
64115
  ...footer
64150
64116
  ];
64151
64117
  return { lines, listHeight: height };
@@ -64178,7 +64144,7 @@ function createRowNamer() {
64178
64144
  names.titles[key] = t;
64179
64145
  };
64180
64146
  for (const s of [...backlog.queued, ...backlog.open]) {
64181
- if (!existsSync28(s.path))
64147
+ if (!existsSync27(s.path))
64182
64148
  continue;
64183
64149
  const key = `${s.harness}/${s.id}`;
64184
64150
  const dir = dirs.resolve(s);
@@ -64215,7 +64181,7 @@ function createRowNamer() {
64215
64181
  }
64216
64182
  function defaultReadLog2() {
64217
64183
  try {
64218
- return readFileSync27(logger.getLogPath(), "utf-8");
64184
+ return readFileSync26(logger.getLogPath(), "utf-8");
64219
64185
  } catch {
64220
64186
  return "";
64221
64187
  }
@@ -64400,7 +64366,7 @@ ${CLEAR_BELOW}`);
64400
64366
  input.on("data", onData);
64401
64367
  });
64402
64368
  }
64403
- 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;
64404
64370
  var init_activity_view = __esm(() => {
64405
64371
  init_follow();
64406
64372
  init_logger();
@@ -64415,7 +64381,7 @@ var init_activity_view = __esm(() => {
64415
64381
  init_alt_screen();
64416
64382
  init_layout();
64417
64383
  init_menu();
64418
- import_picocolors35 = __toESM(require_picocolors(), 1);
64384
+ import_picocolors34 = __toESM(require_picocolors(), 1);
64419
64385
  ESC7 = String.fromCharCode(27);
64420
64386
  HIDE_CURSOR3 = `${ESC7}[?25l`;
64421
64387
  SHOW_CURSOR3 = `${ESC7}[?25h`;
@@ -64432,14 +64398,14 @@ var init_activity_view = __esm(() => {
64432
64398
  });
64433
64399
 
64434
64400
  // src/rules/installer.ts
64435
- 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";
64436
64402
  import { homedir as homedir12 } from "node:os";
64437
- import { dirname as dirname12, join as join43 } from "node:path";
64403
+ import { dirname as dirname12, join as join42 } from "node:path";
64438
64404
  function claudeConfigDir4() {
64439
- return process.env.CLAUDE_CONFIG_DIR || join43(homedir12(), ".claude");
64405
+ return process.env.CLAUDE_CONFIG_DIR || join42(homedir12(), ".claude");
64440
64406
  }
64441
64407
  function codexHome4() {
64442
- return process.env.CODEX_HOME || join43(homedir12(), ".codex");
64408
+ return process.env.CODEX_HOME || join42(homedir12(), ".codex");
64443
64409
  }
64444
64410
  function isRuleAgent(agent) {
64445
64411
  return Object.hasOwn(RULE_TARGETS, agent);
@@ -64465,17 +64431,17 @@ async function fetchDosuRule(fetchImpl = globalThis.fetch) {
64465
64431
  return normalizeRule(FALLBACK_DOSU_RULE);
64466
64432
  }
64467
64433
  function ensureParent(path) {
64468
- mkdirSync18(dirname12(path), { recursive: true });
64434
+ mkdirSync17(dirname12(path), { recursive: true });
64469
64435
  }
64470
64436
  function writeIfChanged(path, content) {
64471
- if (!existsSync29(path)) {
64437
+ if (!existsSync28(path)) {
64472
64438
  ensureParent(path);
64473
- writeFileSync19(path, content, "utf-8");
64439
+ writeFileSync18(path, content, "utf-8");
64474
64440
  return "created";
64475
64441
  }
64476
- if (readFileSync28(path, "utf-8") === content)
64442
+ if (readFileSync27(path, "utf-8") === content)
64477
64443
  return "unchanged";
64478
- writeFileSync19(path, content, "utf-8");
64444
+ writeFileSync18(path, content, "utf-8");
64479
64445
  return "updated";
64480
64446
  }
64481
64447
  function findSection(content) {
@@ -64501,14 +64467,14 @@ function buildSection(content, lineEnding = `
64501
64467
  return `${DOSU_RULE_SECTION_START}${lineEnding}${body}${lineEnding}${DOSU_RULE_SECTION_END}`;
64502
64468
  }
64503
64469
  function upsertSection(path, content) {
64504
- if (!existsSync29(path)) {
64470
+ if (!existsSync28(path)) {
64505
64471
  const section = buildSection(content);
64506
64472
  ensureParent(path);
64507
- writeFileSync19(path, `${section}
64473
+ writeFileSync18(path, `${section}
64508
64474
  `, "utf-8");
64509
64475
  return "created";
64510
64476
  }
64511
- const existing = readFileSync28(path, "utf-8");
64477
+ const existing = readFileSync27(path, "utf-8");
64512
64478
  const lineEnding = lineEndingFor(existing);
64513
64479
  const section = buildSection(content, lineEnding);
64514
64480
  const location = findSection(existing);
@@ -64521,7 +64487,7 @@ function upsertSection(path, content) {
64521
64487
  }
64522
64488
  if (next === existing)
64523
64489
  return "unchanged";
64524
- writeFileSync19(path, next, "utf-8");
64490
+ writeFileSync18(path, next, "utf-8");
64525
64491
  return "updated";
64526
64492
  }
64527
64493
  function installRuleForAgent(agent, content) {
@@ -64538,13 +64504,13 @@ function removeRuleForAgent(agent) {
64538
64504
  return null;
64539
64505
  const target = RULE_TARGETS[agent];
64540
64506
  const path = target.path();
64541
- if (!existsSync29(path))
64507
+ if (!existsSync28(path))
64542
64508
  return { agent, path, action: "not_found" };
64543
64509
  if (target.kind === "file") {
64544
64510
  unlinkSync5(path);
64545
64511
  return { agent, path, action: "removed" };
64546
64512
  }
64547
- const existing = readFileSync28(path, "utf-8");
64513
+ const existing = readFileSync27(path, "utf-8");
64548
64514
  const lineEnding = lineEndingFor(existing);
64549
64515
  const location = findSection(existing);
64550
64516
  if (!location)
@@ -64553,7 +64519,7 @@ function removeRuleForAgent(agent) {
64553
64519
  if (!next) {
64554
64520
  unlinkSync5(path);
64555
64521
  } else {
64556
- writeFileSync19(path, `${next}${lineEnding}`, "utf-8");
64522
+ writeFileSync18(path, `${next}${lineEnding}`, "utf-8");
64557
64523
  }
64558
64524
  return { agent, path, action: "removed" };
64559
64525
  }
@@ -64581,36 +64547,36 @@ var init_installer = __esm(() => {
64581
64547
  RULE_TARGETS = {
64582
64548
  claude: {
64583
64549
  kind: "file",
64584
- path: () => join43(claudeConfigDir4(), "rules", "dosu.md")
64550
+ path: () => join42(claudeConfigDir4(), "rules", "dosu.md")
64585
64551
  },
64586
64552
  cursor: {
64587
64553
  kind: "file",
64588
- path: () => join43(homedir12(), ".cursor", "rules", "dosu.mdc"),
64554
+ path: () => join42(homedir12(), ".cursor", "rules", "dosu.mdc"),
64589
64555
  cursorFrontmatter: true
64590
64556
  },
64591
64557
  codex: {
64592
64558
  kind: "section",
64593
- path: () => join43(codexHome4(), "AGENTS.md")
64559
+ path: () => join42(codexHome4(), "AGENTS.md")
64594
64560
  },
64595
64561
  opencode: {
64596
64562
  kind: "section",
64597
- path: () => join43(homedir12(), ".config", "opencode", "AGENTS.md")
64563
+ path: () => join42(homedir12(), ".config", "opencode", "AGENTS.md")
64598
64564
  },
64599
64565
  gemini: {
64600
64566
  kind: "section",
64601
- path: () => join43(homedir12(), ".gemini", "GEMINI.md")
64567
+ path: () => join42(homedir12(), ".gemini", "GEMINI.md")
64602
64568
  },
64603
64569
  antigravity: {
64604
64570
  kind: "section",
64605
- path: () => join43(homedir12(), ".gemini", "GEMINI.md")
64571
+ path: () => join42(homedir12(), ".gemini", "GEMINI.md")
64606
64572
  }
64607
64573
  };
64608
64574
  });
64609
64575
 
64610
64576
  // src/setup/agents-md-step.ts
64611
64577
  import { execSync as execSync2 } from "node:child_process";
64612
- import { existsSync as existsSync30, readFileSync as readFileSync29, writeFileSync as writeFileSync20 } from "node:fs";
64613
- 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";
64614
64580
  function inGitWorkTree(cwd = process.cwd()) {
64615
64581
  try {
64616
64582
  const stdout = execSync2("git rev-parse --is-inside-work-tree", {
@@ -64624,10 +64590,10 @@ function inGitWorkTree(cwd = process.cwd()) {
64624
64590
  }
64625
64591
  function dosuAgentsSectionState(cwd = process.cwd()) {
64626
64592
  try {
64627
- const path = join44(cwd, "AGENTS.md");
64628
- if (!existsSync30(path))
64593
+ const path = join43(cwd, "AGENTS.md");
64594
+ if (!existsSync29(path))
64629
64595
  return "missing";
64630
- const content = readFileSync29(path, "utf-8");
64596
+ const content = readFileSync28(path, "utf-8");
64631
64597
  const startMatch = content.match(SECTION_START_RE);
64632
64598
  if (!startMatch || !content.includes(DOSU_SECTION_END))
64633
64599
  return "missing";
@@ -64662,14 +64628,14 @@ function findSection2(content) {
64662
64628
  return { start, end };
64663
64629
  }
64664
64630
  function upsertDosuAgentsSection(cwd = process.cwd(), content = FALLBACK_DOSU_RULE) {
64665
- const path = join44(cwd, "AGENTS.md");
64666
- if (!existsSync30(path)) {
64631
+ const path = join43(cwd, "AGENTS.md");
64632
+ if (!existsSync29(path)) {
64667
64633
  const section = buildDosuAgentsSection(content);
64668
- writeFileSync20(path, `${section}
64634
+ writeFileSync19(path, `${section}
64669
64635
  `);
64670
64636
  return { path, action: "created" };
64671
64637
  }
64672
- const existing = readFileSync29(path, "utf-8");
64638
+ const existing = readFileSync28(path, "utf-8");
64673
64639
  const lineEnding = lineEndingFor2(existing);
64674
64640
  const section = renderDosuAgentsSection(content, lineEnding);
64675
64641
  const location = findSection2(existing);
@@ -64681,7 +64647,7 @@ function upsertDosuAgentsSection(cwd = process.cwd(), content = FALLBACK_DOSU_RU
64681
64647
  }
64682
64648
  if (next === existing)
64683
64649
  return { path, action: "unchanged" };
64684
- writeFileSync20(path, next);
64650
+ writeFileSync19(path, next);
64685
64651
  return { path, action: "updated" };
64686
64652
  }
64687
64653
  async function stepUpdateAgentsMd(cwd = process.cwd(), content) {
@@ -66133,7 +66099,7 @@ async function stepSelectTools(detected) {
66133
66099
  initialValues: preselected,
66134
66100
  statusFor: (id, picked) => {
66135
66101
  if (configuredMap.get(id))
66136
- return picked ? dim("configured") : import_picocolors36.default.yellow("will remove");
66102
+ return picked ? dim("configured") : import_picocolors35.default.yellow("will remove");
66137
66103
  return picked ? brand("will configure") : undefined;
66138
66104
  },
66139
66105
  summary: (picked) => {
@@ -66308,7 +66274,7 @@ ${dim(wrapLog("Type it in a chat to keep that chat out of Dosu. To keep a whole
66308
66274
  log2.success("All agents already configured. No changes needed.");
66309
66275
  }
66310
66276
  }
66311
- 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;
66312
66278
  var init_flow2 = __esm(() => {
66313
66279
  init_errors4();
66314
66280
  init_client();
@@ -66331,7 +66297,7 @@ var init_flow2 = __esm(() => {
66331
66297
  init_analytics2();
66332
66298
  init_rules_step();
66333
66299
  init_styles();
66334
- import_picocolors36 = __toESM(require_picocolors(), 1);
66300
+ import_picocolors35 = __toESM(require_picocolors(), 1);
66335
66301
  OAUTH_ANALYTICS_REASONS = new Set([
66336
66302
  "access_denied",
66337
66303
  "bad_oauth_state",
@@ -66495,7 +66461,7 @@ Could not update Dosu automatically.`);
66495
66461
  ${plan.manualCommand}`);
66496
66462
  return result.status && result.status > 0 ? result.status : 1;
66497
66463
  }
66498
- console.log(import_picocolors37.default.green(`
66464
+ console.log(import_picocolors36.default.green(`
66499
66465
  ✓ Update command completed.`));
66500
66466
  console.log('Run "dosu --version" to verify.');
66501
66467
  return 0;
@@ -66537,12 +66503,13 @@ async function completeUpgrade(channel = INSTALL_CHANNEL, options = {}) {
66537
66503
  finishWithNewBinary(channel, options);
66538
66504
  return 0;
66539
66505
  }
66540
- async function finishUpgrade(from, options = {}) {
66541
- const previous = SEMVER.test(from) ? from : null;
66542
- if (!needsMcpRefresh(previous, VERSION)) {
66506
+ async function finishUpgrade(_from, options = {}) {
66507
+ const cfg = loadConfigNonBlocking();
66508
+ const stale = staleProviders(cfg);
66509
+ if (stale.length === 0) {
66543
66510
  checkForSkillUpdates();
66544
66511
  writeMcpRefreshCache({ version: VERSION });
66545
- 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.`));
66546
66513
  return 0;
66547
66514
  }
66548
66515
  if (options.interactive ?? isInteractiveTerminal()) {
@@ -66554,14 +66521,13 @@ This update changes how agents connect to Dosu. Running setup...
66554
66521
  return 0;
66555
66522
  }
66556
66523
  checkForSkillUpdates();
66557
- const cfg = loadConfigNonBlocking();
66558
66524
  if (!cfg || !canRefreshMcp(cfg)) {
66559
66525
  console.error(SETUP_FALLBACK);
66560
66526
  return 0;
66561
66527
  }
66562
66528
  console.log(`
66563
66529
  Refreshing agent MCP configs with the new version...`);
66564
- const result = refreshConfiguredProviders(cfg);
66530
+ const result = refreshProviders(cfg, stale);
66565
66531
  writeMcpRefreshCache({ version: VERSION });
66566
66532
  for (const provider of result.updated)
66567
66533
  console.log(` ✓ ${provider.name()}`);
@@ -66607,7 +66573,7 @@ function upgradeCommand() {
66607
66573
  process.exitCode = status;
66608
66574
  });
66609
66575
  }
66610
- 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.';
66611
66577
  var init_upgrade = __esm(() => {
66612
66578
  init_commander();
66613
66579
  init_config();
@@ -66615,7 +66581,7 @@ var init_upgrade = __esm(() => {
66615
66581
  init_auto_update();
66616
66582
  init_mcp_refresh_check();
66617
66583
  init_skill_update_check();
66618
- import_picocolors37 = __toESM(require_picocolors(), 1);
66584
+ import_picocolors36 = __toESM(require_picocolors(), 1);
66619
66585
  LATEST_PACKAGE = `${PACKAGE_NAME}@latest`;
66620
66586
  PACKAGE_MANAGERS = {
66621
66587
  npm: {
@@ -66634,31 +66600,30 @@ var init_upgrade = __esm(() => {
66634
66600
  installArgs: ["global", "add", LATEST_PACKAGE]
66635
66601
  }
66636
66602
  };
66637
- SEMVER = /^\d+\.\d+\.\d+/;
66638
66603
  });
66639
66604
 
66640
66605
  // src/version/incognito-backfill-check.ts
66641
- import { existsSync as existsSync31, mkdirSync as mkdirSync19, writeFileSync as writeFileSync21 } from "node:fs";
66642
- 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";
66643
66608
  function markerPath() {
66644
- return join45(getConfigDir(), MARKER_FILENAME);
66609
+ return join44(getConfigDir(), MARKER_FILENAME);
66645
66610
  }
66646
66611
  function writeMarker() {
66647
66612
  const dir = getConfigDir();
66648
- if (!existsSync31(dir))
66649
- mkdirSync19(dir, { recursive: true, mode: 448 });
66650
- 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 });
66651
66616
  }
66652
66617
  function displayNotice3(names) {
66653
66618
  console.error(`
66654
- ${import_picocolors38.default.green(`✓ Dosu: added the /${INCOGNITO_COMMAND_NAME} command to ${names.join(", ")}`)}
66655
- ` + `${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.`)}
66656
66621
  `);
66657
66622
  }
66658
66623
  function checkForIncognitoBackfill(options = {}) {
66659
66624
  const notify = options.notify ?? true;
66660
66625
  try {
66661
- if (existsSync31(markerPath()))
66626
+ if (existsSync30(markerPath()))
66662
66627
  return;
66663
66628
  const added = [];
66664
66629
  let failed = false;
@@ -66685,14 +66650,244 @@ function checkForIncognitoBackfill(options = {}) {
66685
66650
  logger.error("incognito-backfill", `Incognito backfill failed: ${err}`);
66686
66651
  }
66687
66652
  }
66688
- var import_picocolors38, MARKER_FILENAME = "incognito-backfill.json";
66653
+ var import_picocolors37, MARKER_FILENAME = "incognito-backfill.json";
66689
66654
  var init_incognito_backfill_check = __esm(() => {
66690
66655
  init_config();
66691
66656
  init_logger();
66692
66657
  init_agents();
66693
66658
  init_agents3();
66694
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();
66695
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-]+)*)?$/;
66834
+ });
66835
+
66836
+ // src/cli/fatal-error.ts
66837
+ function read2(value, key) {
66838
+ if (typeof value !== "object" && typeof value !== "function" || value === null) {
66839
+ return;
66840
+ }
66841
+ try {
66842
+ return Reflect.get(value, key);
66843
+ } catch {
66844
+ return;
66845
+ }
66846
+ }
66847
+ function tokenOf2(data, key) {
66848
+ const value = read2(data, key);
66849
+ if (typeof value !== "string")
66850
+ return;
66851
+ const trimmed = value.trim();
66852
+ return REQUEST_ID_PATTERN2.test(trimmed) ? trimmed : undefined;
66853
+ }
66854
+ function statusOf2(data) {
66855
+ const value = read2(data, "httpStatus");
66856
+ return typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599 ? value : undefined;
66857
+ }
66858
+ function fatalErrorDiagnostics2(err) {
66859
+ const data = read2(err, "data");
66860
+ if (!data)
66861
+ return;
66862
+ const code = tokenOf2(data, "code");
66863
+ const path = tokenOf2(data, "path");
66864
+ const status = statusOf2(data);
66865
+ const requestId = tokenOf2(data, "requestId");
66866
+ const parts = [
66867
+ code && `code=${code}`,
66868
+ path && `path=${path}`,
66869
+ status && `status=${status}`,
66870
+ requestId && `request_id=${requestId}`
66871
+ ].filter(Boolean);
66872
+ return parts.length > 0 ? parts.join(" ") : undefined;
66873
+ }
66874
+ function printFatalError2(err) {
66875
+ if (isCommandError(err)) {
66876
+ console.error(import_picocolors39.default.red(err.message));
66877
+ for (const line of err.details)
66878
+ console.error(import_picocolors39.default.dim(line));
66879
+ return;
66880
+ }
66881
+ console.error(read2(err, "message") ?? err);
66882
+ const diagnostics = fatalErrorDiagnostics2(err);
66883
+ if (diagnostics)
66884
+ console.error(diagnostics);
66885
+ }
66886
+ var import_picocolors39, REQUEST_ID_PATTERN2;
66887
+ var init_fatal_error = __esm(() => {
66888
+ init_command_error();
66889
+ import_picocolors39 = __toESM(require_picocolors(), 1);
66890
+ REQUEST_ID_PATTERN2 = /^[A-Za-z0-9][A-Za-z0-9:._-]{0,127}$/;
66696
66891
  });
66697
66892
 
66698
66893
  // src/tui/analytics-view.ts
@@ -66813,7 +67008,7 @@ function renderAnalyticsFrame(state, tab, scroll, pageStats = null, pagesPending
66813
67008
  const rows = analyticsTabRows(tab, state, pageStats);
66814
67009
  const { visible, above, below } = windowReport(rows, scroll, height);
66815
67010
  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.";
66816
- const listRows = visible.length > 0 ? visible.map((row) => import_picocolors39.default.dim(row)) : [import_picocolors39.default.dim(empty)];
67011
+ const listRows = visible.length > 0 ? visible.map((row) => import_picocolors40.default.dim(row)) : [import_picocolors40.default.dim(empty)];
66817
67012
  const scrollParts = [];
66818
67013
  if (above > 0)
66819
67014
  scrollParts.push(`↑ ${above} earlier`);
@@ -66829,9 +67024,9 @@ function renderAnalyticsFrame(state, tab, scroll, pageStats = null, pagesPending
66829
67024
  ], tab, width, { spread: false }),
66830
67025
  "",
66831
67026
  ...listRows,
66832
- ...scrollParts.length > 0 ? [import_picocolors39.default.dim(scrollParts.join(" · "))] : [],
67027
+ ...scrollParts.length > 0 ? [import_picocolors40.default.dim(scrollParts.join(" · "))] : [],
66833
67028
  "",
66834
- import_picocolors39.default.dim("tab switch · ↑↓ scroll · esc back")
67029
+ import_picocolors40.default.dim("tab switch · ↑↓ scroll · esc back")
66835
67030
  ];
66836
67031
  return lines.join(`
66837
67032
  `);
@@ -66928,7 +67123,7 @@ ${CLEAR_BELOW2}`);
66928
67123
  input.on("data", onData);
66929
67124
  });
66930
67125
  }
66931
- var import_picocolors39, 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);
67126
+ var import_picocolors40, 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);
66932
67127
  var init_analytics_view = __esm(() => {
66933
67128
  init_trpc();
66934
67129
  init_config();
@@ -66936,7 +67131,7 @@ var init_analytics_view = __esm(() => {
66936
67131
  init_alt_screen();
66937
67132
  init_layout();
66938
67133
  init_menu();
66939
- import_picocolors39 = __toESM(require_picocolors(), 1);
67134
+ import_picocolors40 = __toESM(require_picocolors(), 1);
66940
67135
  ESC8 = String.fromCharCode(27);
66941
67136
  HIDE_CURSOR4 = `${ESC8}[?25l`;
66942
67137
  SHOW_CURSOR4 = `${ESC8}[?25h`;
@@ -66954,7 +67149,7 @@ var init_analytics_view = __esm(() => {
66954
67149
  // src/tui/banner.ts
66955
67150
  function paintLogoRow(row) {
66956
67151
  return row.map(([tone, art]) => {
66957
- if (!import_picocolors40.default.isColorSupported)
67152
+ if (!import_picocolors41.default.isColorSupported)
66958
67153
  return art;
66959
67154
  const { fg, fallback } = LOGO_TONES[tone];
66960
67155
  return hasTruecolor() ? `${fg}${art}\x1B[39m` : fallback(art);
@@ -66985,14 +67180,14 @@ function wrapValue(text, room) {
66985
67180
  }
66986
67181
  function checklistRows(ctx, width) {
66987
67182
  const on = brand(CHECK2);
66988
- const off = import_picocolors40.default.dim(CIRCLE);
67183
+ const off = import_picocolors41.default.dim(CIRCLE);
66989
67184
  const rows = [["workspace", ctx.directory]];
66990
67185
  rows.push([
66991
67186
  "account",
66992
- ctx.sessionExpired ? `${off} ${import_picocolors40.default.yellow("session expired")} ${import_picocolors40.default.dim(`${DOT3} run Log in`)}` : ctx.signedIn ? `${on} signed in` : `${off} ${import_picocolors40.default.dim(`not signed in ${DOT3} run Setup`)}`
67187
+ ctx.sessionExpired ? `${off} ${import_picocolors41.default.yellow("session expired")} ${import_picocolors41.default.dim(`${DOT3} run Log in`)}` : ctx.signedIn ? `${on} signed in` : `${off} ${import_picocolors41.default.dim(`not signed in ${DOT3} run Setup`)}`
66993
67188
  ]);
66994
67189
  const missing = new Set(ctx.setupMissing ?? []);
66995
- const warnRow = `${off} ${import_picocolors40.default.yellow("not configured")} ${import_picocolors40.default.dim(`${DOT3} run Setup`)}`;
67190
+ const warnRow = `${off} ${import_picocolors41.default.yellow("not configured")} ${import_picocolors41.default.dim(`${DOT3} run Setup`)}`;
66996
67191
  if (missing.has("MCP"))
66997
67192
  rows.push(["mcp", warnRow]);
66998
67193
  else if (ctx.deploymentName)
@@ -67004,7 +67199,7 @@ function checklistRows(ctx, width) {
67004
67199
  if (ctx.repoAgentsMd) {
67005
67200
  rows.push([
67006
67201
  "repo",
67007
- ctx.repoAgentsMd === "current" ? `${on} AGENTS.md has the Dosu section` : `${off} ${import_picocolors40.default.yellow(ctx.repoAgentsMd === "outdated" ? "AGENTS.md Dosu section outdated" : "AGENTS.md missing the Dosu section")} ${import_picocolors40.default.dim(`${DOT3} run Setup`)}`
67202
+ ctx.repoAgentsMd === "current" ? `${on} AGENTS.md has the Dosu section` : `${off} ${import_picocolors41.default.yellow(ctx.repoAgentsMd === "outdated" ? "AGENTS.md Dosu section outdated" : "AGENTS.md missing the Dosu section")} ${import_picocolors41.default.dim(`${DOT3} run Setup`)}`
67008
67203
  ]);
67009
67204
  }
67010
67205
  if (missing.has("agents"))
@@ -67016,20 +67211,20 @@ function checklistRows(ctx, width) {
67016
67211
  if (ctx.studying) {
67017
67212
  rows.push([
67018
67213
  "sync",
67019
- `\uD83D\uDCDA ${brand("studying sessions...")} ${import_picocolors40.default.dim(`${DOT3} see activity`)}`
67214
+ `\uD83D\uDCDA ${brand("studying sessions...")} ${import_picocolors41.default.dim(`${DOT3} see activity`)}`
67020
67215
  ]);
67021
67216
  }
67022
67217
  if (ctx.update) {
67023
67218
  rows.push([
67024
67219
  "update",
67025
- `${import_picocolors40.default.yellow(`↑ ${ctx.update.version} available`)} ${import_picocolors40.default.dim(`${DOT3} ${ctx.update.hint}`)}`
67220
+ `${import_picocolors41.default.yellow(`↑ ${ctx.update.version} available`)} ${import_picocolors41.default.dim(`${DOT3} ${ctx.update.hint}`)}`
67026
67221
  ]);
67027
67222
  }
67028
67223
  const labelWidth = Math.max(...rows.map(([label]) => label.length));
67029
67224
  const valueColumn = LOGO_WIDTH + COLUMN_GAP.length + labelWidth + LABEL_GAP.length;
67030
67225
  const room = Math.max(1, width - valueColumn);
67031
67226
  const indent = " ".repeat(labelWidth + LABEL_GAP.length);
67032
- return rows.flatMap(([label, value]) => wrapValue(value, room).map((line, i) => i === 0 ? `${import_picocolors40.default.dim(label.padEnd(labelWidth))}${LABEL_GAP}${line}` : `${indent}${line}`));
67227
+ return rows.flatMap(([label, value]) => wrapValue(value, room).map((line, i) => i === 0 ? `${import_picocolors41.default.dim(label.padEnd(labelWidth))}${LABEL_GAP}${line}` : `${indent}${line}`));
67033
67228
  }
67034
67229
  function bannerWidth(columns = process.stdout.columns ?? 80) {
67035
67230
  return columns - layoutMargin(columns) - 1;
@@ -67039,7 +67234,7 @@ function renderBanner(ctx, width = bannerWidth()) {
67039
67234
  const text = [
67040
67235
  ...checklistRows(ctx, width),
67041
67236
  "",
67042
- `${brandBadge("dosu-cli")} ${import_picocolors40.default.dim(`${ctx.version} ${DOT3} ${ctx.webAppHost}`)}`
67237
+ `${brandBadge("dosu-cli")} ${import_picocolors41.default.dim(`${ctx.version} ${DOT3} ${ctx.webAppHost}`)}`
67043
67238
  ];
67044
67239
  const height = Math.max(logo.length, text.length);
67045
67240
  const combined = [];
@@ -67052,11 +67247,11 @@ function renderBanner(ctx, width = bannerWidth()) {
67052
67247
  return ["", ...combined, ""].join(`
67053
67248
  `);
67054
67249
  }
67055
- var import_picocolors40, CHECK2 = "✔", CIRCLE = "○", DOT3 = "·", LOGO_ROWS, LOGO_MARK, LOGO_WIDTH, LOGO_TONES, COLUMN_GAP = " ", LABEL_GAP = " ", LIST_SEP;
67250
+ var import_picocolors41, CHECK2 = "✔", CIRCLE = "○", DOT3 = "·", LOGO_ROWS, LOGO_MARK, LOGO_WIDTH, LOGO_TONES, COLUMN_GAP = " ", LABEL_GAP = " ", LIST_SEP;
67056
67251
  var init_banner = __esm(() => {
67057
67252
  init_styles();
67058
67253
  init_layout();
67059
- import_picocolors40 = __toESM(require_picocolors(), 1);
67254
+ import_picocolors41 = __toESM(require_picocolors(), 1);
67060
67255
  LOGO_ROWS = [
67061
67256
  [["sage", "▄▄▄▄▄▄"]],
67062
67257
  [["sage", "██ ▀▄"]],
@@ -67067,8 +67262,8 @@ var init_banner = __esm(() => {
67067
67262
  LOGO_MARK = LOGO_ROWS.map((row) => row.map(([, art]) => art).join(""));
67068
67263
  LOGO_WIDTH = Math.max(...LOGO_MARK.map((row) => row.length));
67069
67264
  LOGO_TONES = {
67070
- sage: { fg: "\x1B[38;2;180;187;145m", fallback: import_picocolors40.default.green },
67071
- moss: { fg: "\x1B[38;2;119;133;97m", fallback: (art) => import_picocolors40.default.dim(import_picocolors40.default.green(art)) }
67265
+ sage: { fg: "\x1B[38;2;180;187;145m", fallback: import_picocolors41.default.green },
67266
+ moss: { fg: "\x1B[38;2;119;133;97m", fallback: (art) => import_picocolors41.default.dim(import_picocolors41.default.green(art)) }
67072
67267
  };
67073
67268
  LIST_SEP = ` ${DOT3} `;
67074
67269
  });
@@ -67181,16 +67376,16 @@ function renderPagesFrame(model, width, selected) {
67181
67376
  const body = [];
67182
67377
  let scrollHint = "";
67183
67378
  if (model.phase === "loading") {
67184
- body.push(import_picocolors41.default.dim("Loading pages…"));
67379
+ body.push(import_picocolors42.default.dim("Loading pages…"));
67185
67380
  } else if (model.phase === "error") {
67186
- body.push(import_picocolors41.default.yellow(clip3(model.message, width)));
67381
+ body.push(import_picocolors42.default.yellow(clip3(model.message, width)));
67187
67382
  } else if (model.pages.length === 0) {
67188
- body.push(import_picocolors41.default.dim("No pages in this Library yet."));
67383
+ body.push(import_picocolors42.default.dim("No pages in this Library yet."));
67189
67384
  } else {
67190
67385
  const rows = pageRows2(model.pages, width - 2);
67191
67386
  const { start, above } = windowSelection(rows.length, selected);
67192
67387
  const visible = rows.slice(start, start + PAGES_VIEW_LINES);
67193
- body.push(...visible.map((row, i) => start + i === selected ? `${brand("▸")} ${import_picocolors41.default.bold(row)}` : ` ${import_picocolors41.default.dim(row)}`));
67388
+ body.push(...visible.map((row, i) => start + i === selected ? `${brand("▸")} ${import_picocolors42.default.bold(row)}` : ` ${import_picocolors42.default.dim(row)}`));
67194
67389
  const below = Math.max(0, Math.max(model.total, rows.length) - start - PAGES_VIEW_LINES);
67195
67390
  const parts = [];
67196
67391
  if (above > 0)
@@ -67199,14 +67394,14 @@ function renderPagesFrame(model, width, selected) {
67199
67394
  parts.push(`↓ ${below} more`);
67200
67395
  scrollHint = parts.join(" · ");
67201
67396
  }
67202
- const count = model.phase === "ready" ? ` ${import_picocolors41.default.dim(model.pages.length < model.total ? `(${model.pages.length} of ${model.total})` : `(${model.pages.length})`)}` : "";
67397
+ const count = model.phase === "ready" ? ` ${import_picocolors42.default.dim(model.pages.length < model.total ? `(${model.pages.length} of ${model.total})` : `(${model.pages.length})`)}` : "";
67203
67398
  const lines = [
67204
67399
  `${breadcrumb(["home", "pages"], width)}${count}`,
67205
67400
  "",
67206
67401
  ...body,
67207
- ...scrollHint ? [import_picocolors41.default.dim(scrollHint)] : [],
67402
+ ...scrollHint ? [import_picocolors42.default.dim(scrollHint)] : [],
67208
67403
  "",
67209
- import_picocolors41.default.dim("↑↓ move · enter open · esc back")
67404
+ import_picocolors42.default.dim("↑↓ move · enter open · esc back")
67210
67405
  ];
67211
67406
  return lines.join(`
67212
67407
  `);
@@ -67215,9 +67410,9 @@ function renderPageFrame(page, content, width, scroll) {
67215
67410
  const body = [];
67216
67411
  let scrollHint = "";
67217
67412
  if (content.phase === "loading") {
67218
- body.push(import_picocolors41.default.dim("Loading page…"));
67413
+ body.push(import_picocolors42.default.dim("Loading page…"));
67219
67414
  } else if (content.phase === "error") {
67220
- body.push(import_picocolors41.default.yellow(clip3(content.message, width)));
67415
+ body.push(import_picocolors42.default.yellow(clip3(content.message, width)));
67221
67416
  } else {
67222
67417
  const rows = wrapBody(content.body, width);
67223
67418
  const { visible, above, below } = windowReport(rows, scroll, PAGE_BODY_LINES);
@@ -67232,12 +67427,12 @@ function renderPageFrame(page, content, width, scroll) {
67232
67427
  const meta = `${page.type} · ${page.published ? "published" : "draft"} · updated ${page.updated_at.slice(0, 10)}`;
67233
67428
  const lines = [
67234
67429
  breadcrumb(["home", "pages", page.title], width),
67235
- import_picocolors41.default.dim(clip3(meta, width)),
67430
+ import_picocolors42.default.dim(clip3(meta, width)),
67236
67431
  "",
67237
67432
  ...body,
67238
- ...scrollHint ? [import_picocolors41.default.dim(scrollHint)] : [],
67433
+ ...scrollHint ? [import_picocolors42.default.dim(scrollHint)] : [],
67239
67434
  "",
67240
- import_picocolors41.default.dim("↑↓ scroll · esc back to pages")
67435
+ import_picocolors42.default.dim("↑↓ scroll · esc back to pages")
67241
67436
  ];
67242
67437
  return lines.join(`
67243
67438
  `);
@@ -67386,7 +67581,7 @@ ${CLEAR_BELOW3}`);
67386
67581
  input.on("data", onData);
67387
67582
  });
67388
67583
  }
67389
- var import_picocolors41, 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;
67584
+ var import_picocolors42, 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;
67390
67585
  var init_pages_view = __esm(() => {
67391
67586
  init_trpc();
67392
67587
  init_config();
@@ -67396,7 +67591,7 @@ var init_pages_view = __esm(() => {
67396
67591
  init_analytics_view();
67397
67592
  init_layout();
67398
67593
  init_menu();
67399
- import_picocolors41 = __toESM(require_picocolors(), 1);
67594
+ import_picocolors42 = __toESM(require_picocolors(), 1);
67400
67595
  ESC9 = String.fromCharCode(27);
67401
67596
  HIDE_CURSOR5 = `${ESC9}[?25l`;
67402
67597
  SHOW_CURSOR5 = `${ESC9}[?25h`;
@@ -67509,7 +67704,7 @@ function isSetUp(cfg) {
67509
67704
  }
67510
67705
  function setupHint(cfg) {
67511
67706
  const started = Boolean(cfg.active_account?.target);
67512
- return import_picocolors42.default.yellow(started ? `incomplete · missing ${missingSetupSteps(cfg).join(" + ")}` : "not set up yet");
67707
+ return import_picocolors43.default.yellow(started ? `incomplete · missing ${missingSetupSteps(cfg).join(" + ")}` : "not set up yet");
67513
67708
  }
67514
67709
  function isStudying() {
67515
67710
  try {
@@ -67754,7 +67949,7 @@ function handleLogout(cfg) {
67754
67949
  saveConfig(cfg);
67755
67950
  log2.success("Credentials cleared.");
67756
67951
  }
67757
- var import_picocolors42, ESC10, CLEAR_SCREEN;
67952
+ var import_picocolors43, ESC10, CLEAR_SCREEN;
67758
67953
  var init_tui = __esm(() => {
67759
67954
  init_client();
67760
67955
  init_config();
@@ -67778,7 +67973,7 @@ var init_tui = __esm(() => {
67778
67973
  init_menu();
67779
67974
  init_pages_view();
67780
67975
  init_prompts();
67781
- import_picocolors42 = __toESM(require_picocolors(), 1);
67976
+ import_picocolors43 = __toESM(require_picocolors(), 1);
67782
67977
  ESC10 = String.fromCharCode(27);
67783
67978
  CLEAR_SCREEN = `${ESC10}[2J${ESC10}[H`;
67784
67979
  });
@@ -68881,11 +69076,20 @@ async function execute() {
68881
69076
  try {
68882
69077
  await program.parseAsync(process.argv);
68883
69078
  } catch (err) {
69079
+ printFatalError2(err);
69080
+ logFatalError(err);
68884
69081
  if (telemetry)
68885
69082
  await finishTelemetry(() => telemetry.fail(err));
68886
- throw err;
69083
+ process.exit(1);
68887
69084
  }
68888
69085
  }
69086
+ function logFatalError(err) {
69087
+ try {
69088
+ const message = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
69089
+ const diagnostics = fatalErrorDiagnostics2(err);
69090
+ logger.error("cli", diagnostics ? `${message} (${diagnostics})` : message);
69091
+ } catch {}
69092
+ }
68889
69093
  function processCommandTelemetry() {
68890
69094
  try {
68891
69095
  const settings = loadTelemetrySettings();
@@ -68934,8 +69138,63 @@ var init_cli = __esm(() => {
68934
69138
  init_skill_update_check();
68935
69139
  init_update_check();
68936
69140
  init_errors();
69141
+ init_fatal_error();
68937
69142
  });
68938
69143
 
69144
+ // src/cli/fatal-error.ts
69145
+ init_command_error();
69146
+ var import_picocolors = __toESM(require_picocolors(), 1);
69147
+ var REQUEST_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9:._-]{0,127}$/;
69148
+ function read(value, key) {
69149
+ if (typeof value !== "object" && typeof value !== "function" || value === null) {
69150
+ return;
69151
+ }
69152
+ try {
69153
+ return Reflect.get(value, key);
69154
+ } catch {
69155
+ return;
69156
+ }
69157
+ }
69158
+ function tokenOf(data, key) {
69159
+ const value = read(data, key);
69160
+ if (typeof value !== "string")
69161
+ return;
69162
+ const trimmed = value.trim();
69163
+ return REQUEST_ID_PATTERN.test(trimmed) ? trimmed : undefined;
69164
+ }
69165
+ function statusOf(data) {
69166
+ const value = read(data, "httpStatus");
69167
+ return typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599 ? value : undefined;
69168
+ }
69169
+ function fatalErrorDiagnostics(err) {
69170
+ const data = read(err, "data");
69171
+ if (!data)
69172
+ return;
69173
+ const code = tokenOf(data, "code");
69174
+ const path = tokenOf(data, "path");
69175
+ const status = statusOf(data);
69176
+ const requestId = tokenOf(data, "requestId");
69177
+ const parts = [
69178
+ code && `code=${code}`,
69179
+ path && `path=${path}`,
69180
+ status && `status=${status}`,
69181
+ requestId && `request_id=${requestId}`
69182
+ ].filter(Boolean);
69183
+ return parts.length > 0 ? parts.join(" ") : undefined;
69184
+ }
69185
+ function printFatalError(err) {
69186
+ if (isCommandError(err)) {
69187
+ console.error(import_picocolors.default.red(err.message));
69188
+ for (const line of err.details)
69189
+ console.error(import_picocolors.default.dim(line));
69190
+ return;
69191
+ }
69192
+ console.error(read(err, "message") ?? err);
69193
+ const diagnostics = fatalErrorDiagnostics(err);
69194
+ if (diagnostics)
69195
+ console.error(diagnostics);
69196
+ }
69197
+
68939
69198
  // src/statusline/argv.ts
68940
69199
  var RENDER_ARGV = ["knowledge", "statusline", "render"];
68941
69200
  function isStatuslineRenderArgv(argv) {
@@ -68954,17 +69213,8 @@ async function main() {
68954
69213
  await execute();
68955
69214
  }
68956
69215
  main().catch((err) => {
68957
- console.error(err.message ?? err);
68958
- const data = err?.data;
68959
- if (data && (data.code || data.path || data.httpStatus)) {
68960
- const parts = [
68961
- data.code && `code=${data.code}`,
68962
- data.path && `path=${data.path}`,
68963
- data.httpStatus && `status=${data.httpStatus}`
68964
- ].filter(Boolean);
68965
- console.error(parts.join(" "));
68966
- }
69216
+ printFatalError(err);
68967
69217
  process.exit(1);
68968
69218
  });
68969
69219
 
68970
- //# debugId=5d3e9365-4283-2b42-6475-6e2164756e21
69220
+ //# debugId=fa693edb-14f4-abcb-6475-6e2164756e21